개발/Java

java.time

함수형 인간 2025. 3. 14. 07:17

java.time 패키지 (Java 8 Date and Time API)

java.time 패키지는 Java 8에서 새롭게 도입된 날짜와 시간 API. 기존의 java.util.Date와 java.util.Calendar 클래스의 문제점(가변성, 부족한 기능, 일관성 없는 API, 타임존 처리의 어려움 등)을 해결하고, 더 직관적이고 강력하며 스레드 안전한(thread-safe) 날짜/시간 기능을 제공.

java.time 패키지의 주요 특징:

  • 불변성 (Immutability): java.time 패키지의 모든 클래스는 불변(immutable). 즉, 객체의 상태를 변경할 수 없다. 날짜/시간을 변경하는 모든 작업은 새로운 객체를 반환. 이 덕분에 멀티스레드 환경에서 안전하게 사용할 수 있고, 예기치 않은 버그를 방지할 수 있다.
  • 명확하고 직관적인 API: 메서드 이름과 동작이 명확하고 일관성이 있어서 사용하기 쉽다. 예를 들어, plusDays(), minusMonths(), withYear()과 같이 직관적인 메서드를 제공.
  • 풍부한 기능: 날짜/시간 연산, 포매팅, 파싱, 타임존 처리, 기간(duration) 및 주기(period) 계산 등 다양한 기능을 제공.
  • 타임존 지원 강화: ZonedDateTime, ZoneId, ZoneOffset 등의 클래스를 통해 타임존을 명확하게 처리할 수 있다.
  • ISO 8601 표준 준수: 날짜와 시간 표현에 대한 국제 표준인 ISO 8601을 따른다.
  • 확장 가능성: Temporal 인터페이스를 기반으로 하여 다양한 시간 시스템(예: 다른 달력 시스템)을 지원할 수 있도록 설계됨.

java.time 패키지의 주요 클래스:

  • LocalDate: 날짜 (년, 월, 일)를 나타내는 불변 클래스. 시간 정보는 포함하지 않는다.
Java
 
LocalDate today = LocalDate.now(); // 현재 날짜
LocalDate christmas = LocalDate.of(2024, 12, 25); // 2024년 12월 25일
LocalDate parsedDate = LocalDate.parse("2023-10-27"); // 문자열 파싱

System.out.println(christmas.getYear()); // 년
System.out.println(christmas.getMonth()); // 월 (Month enum)
System.out.println(christmas.getMonthValue()); // 월 (1-12)
System.out.println(christmas.getDayOfMonth()); // 일
System.out.println(christmas.getDayOfWeek());  // 요일(DayOfWeek enum)
System.out.println(christmas.getDayOfYear());//연중 몇번째 일

LocalDate weekAfter = christmas.plusWeeks(1);  //1주 뒤
LocalDate yearBefore = christmas.minusYears(1); //1년 전

 

  • LocalTime: 시간 (시, 분, 초, 나노초)을 나타내는 불변 클래스. 날짜 정보는 포함하지 않는다.
Java
 
LocalTime now = LocalTime.now();
LocalTime lunchTime = LocalTime.of(12, 30); // 12시 30분
LocalTime parsedTime = LocalTime.parse("15:45:30");

System.out.println(lunchTime.getHour());
System.out.println(lunchTime.getMinute());
System.out.println(lunchTime.getSecond());
System.out.println(lunchTime.getNano()); //나노초

LocalTime afterOneHour = now.plusHours(1);

 

  • LocalDateTime: 날짜와 시간을 모두 나타내는 불변 클래스. LocalDate와 LocalTime을 조합한 형태.
Java
 
LocalDateTime now = LocalDateTime.now();
LocalDateTime meeting = LocalDateTime.of(2024, 10, 29, 14, 0, 0); // 2024-10-29 14:00:00
LocalDateTime parsedDateTime = LocalDateTime.parse("2023-10-27T10:15:30"); // ISO 8601 형식

//날짜와 시간 정보 모두 사용 가능
System.out.println(meeting.getYear()); //년
System.out.println(meeting.getMonth()); //월
System.out.println(meeting.getDayOfMonth()); //일
System.out.println(meeting.getHour()); //시
System.out.println(meeting.getMinute()); //분

LocalDateTime nextWeek = meeting.plusWeeks(1);

 

  • ZonedDateTime: 특정 타임존(TimeZone)의 날짜와 시간을 나타내는 불변 클래스. LocalDateTime에 타임존 정보를 추가한 형태.
Java
 
ZonedDateTime nowInSeoul = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));
ZonedDateTime newYorkTime = ZonedDateTime.of(2024, 10, 29, 10, 0, 0, 0, ZoneId.of("America/New_York"));
ZonedDateTime parsedZonedDateTime = ZonedDateTime.parse("2023-10-27T10:15:30+09:00[Asia/Seoul]");

System.out.println(nowInSeoul); // 2024-10-29T11:17:25.777920+09:00[Asia/Seoul]
System.out.println(newYorkTime);

// 타임존 변환
ZonedDateTime seoulTime = newYorkTime.withZoneSameInstant(ZoneId.of("Asia/Seoul"));

 

  • Instant: 특정 시점(타임스탬프)을 나타내는 불변 클래스. 기계 시간(machine time)을 표현하는 데 사용. 1970년 1월 1일 00:00:00 UTC (에포크)부터 경과한 나노초를 기준으로 합니다.
Java
 
Instant now = Instant.now(); // 현재 UTC 시점
Instant epoch = Instant.ofEpochSecond(0); // 1970-01-01T00:00:00Z
Instant fromEpochMilli = Instant.ofEpochMilli(1666934800000L); // 밀리초 기준

System.out.println(now);
System.out.println(now.getEpochSecond());//초
System.out.println(now.getNano());      //나노초

 

  • Duration: 두 시점 간의 시간 간격(duration)을 나타내는 불변 클래스. 초와 나노초 단위로 표현.
Java
 
LocalDateTime start = LocalDateTime.of(2023, 10, 27, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2023, 10, 27, 12, 30, 0);

Duration duration = Duration.between(start, end);
System.out.println(duration.toHours());   // 시간
System.out.println(duration.toMinutes()); // 분
System.out.println(duration.getSeconds()); // 초

LocalTime time1 = LocalTime.of(10, 30, 0);
LocalTime time2 = LocalTime.of(18, 0, 0);
Duration between = Duration.between(time1, time2);

 

  • Period: 두 날짜 간의 기간(period)을 나타내는 불변 클래스입니다. 년, 월, 일 단위로 표현.
Java
 
LocalDate startDate = LocalDate.of(2023, 1, 1);
LocalDate endDate = LocalDate.of(2024, 10, 29);

Period period = Period.between(startDate, endDate);
System.out.println(period.getYears());   // 년
System.out.println(period.getMonths());  // 월
System.out.println(period.getDays());    // 일

 

  • ZoneId: 타임존을 나타내는 클래스. (예: "Asia/Seoul", "America/New_York")
Java
 
ZoneId seoul = ZoneId.of("Asia/Seoul");
ZoneId newYork = ZoneId.of("America/New_York");
ZoneId utc = ZoneId.of("UTC");
ZoneId systemDefault = ZoneId.systemDefault();//시스템 기본 타임존

Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); // 사용 가능한 타임존 ID 목록

 

  • ZoneOffset: UTC로부터의 시간 오프셋(offset)을 나타내는 클래스. (예: +09:00, -05:00)
Java
 
ZoneOffset seoulOffset = ZoneOffset.of("+09:00");
ZoneOffset newYorkOffset = ZoneOffset.of("-05:00");

 

  • DayOfWeek: 요일을 나타내는 enum
  • Month: 월을 나타내는 enum

java.time.format.DateTimeFormatter (날짜/시간 포매팅 및 파싱):

java.time 패키지의 클래스들은 toString() 메서드를 통해 ISO 8601 형식의 문자열로 표현된다. 하지만 원하는 형식으로 날짜/시간을 출력하거나, 특정 형식의 문자열을 날짜/시간 객체로 파싱하려면 java.time.format.DateTimeFormatter를 사용해야 한다.

Java
 
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;

public class DateTimeFormatterExample {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();

        // 미리 정의된 포맷터 사용
        DateTimeFormatter isoFormatter = DateTimeFormatter.ISO_DATE_TIME;
        String formattedIso = now.format(isoFormatter);
        System.out.println("ISO Format: " + formattedIso);

        // 사용자 정의 패턴
        DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedCustom = now.format(customFormatter);
        System.out.println("Custom Format: " + formattedCustom);

        DateTimeFormatter customFormatter2 = DateTimeFormatter.ofPattern("MMMM dd, yyyy hh:mm a", Locale.US);
		String formattedCustom2 = now.format(customFormatter2);
		System.out.println("Custom Format2: " + formattedCustom2);

        // Localized 포맷 (SHORT, MEDIUM, LONG, FULL)
        DateTimeFormatter localizedFormatterShort = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
        String formattedShort = now.format(localizedFormatterShort);
        System.out.println("Short Format: " + formattedShort);  //23. 10. 29. 오후 2:43

        DateTimeFormatter localizedFormatterMedium = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
        String formattedMedium = now.format(localizedFormatterMedium);
        System.out.println("Medium Format: "+ formattedMedium); //2023. 10. 29. 오후 2:44:33


        // 문자열 파싱
        String dateString = "2023-10-27 15:30:00";
        DateTimeFormatter parser = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime parsedDateTime = LocalDateTime.parse(dateString, parser);
        System.out.println("Parsed DateTime: " + parsedDateTime);
    }
}

 

DateTimeFormatter의 주요 패턴 문자:

  • yyyy: 년 (4자리)
  • yy: 년 (2자리)
  • MM: 월 (2자리, 01-12)
  • M: 월 (1자리 또는 2자리, 1-12)
  • LLLL: 월 이름(긴 형식)
  • MMM: 월 이름(짧은 형식)
  • dd: 일 (2자리, 01-31)
  • d: 일 (1자리 또는 2자리, 1-31)
  • HH: 시 (24시간 형식, 00-23)
  • H: 시 (24시간 형식, 0-23)
  • hh: 시 (12시간 형식, 01-12)
  • h: 시 (12시간 형식, 1-12)
  • mm: 분 (00-59)
  • ss: 초 (00-59)
  • SSS: 밀리초 (000-999)
  • n: 나노초
  • E: 요일 (짧은 형식, 예: "월")
  • EEEE: 요일 (긴 형식, 예: "월요일")
  • a: 오전/오후 (AM/PM)
  • z: 타임존 이름 (예: "Asia/Seoul")
  • Z: 타임존 오프셋 (예: "+0900")
  • XXX: ISO 8601 타임존 오프셋 (예: "+09:00")

TemporalAdjuster (날짜/시간 조정):

TemporalAdjuster 인터페이스는 날짜/시간 객체를 특정 규칙에 따라 조정하는 기능을 제공. TemporalAdjusters 클래스는 유용한 TemporalAdjuster 구현체들을 제공.

Java
 
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;

public class TemporalAdjusterExample {
    public static void main(String[] args) {
        LocalDate today = LocalDate.now();

        // 다음 주 월요일
        LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
        System.out.println("Next Monday: " + nextMonday);

        // 이번 달의 마지막 날
        LocalDate lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
        System.out.println("Last day of month: " + lastDayOfMonth);

        // 특정 요일의 첫번째 날
        LocalDate firstMondayOfYear = today.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY));
        System.out.println("First Monday of the year: " + firstMondayOfYear);
    }
}

ChronoUnit (날짜/시간 단위):

ChronoUnit enum은 날짜/시간 단위를 나타냄 (YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS 등). Duration과 Period를 사용할 때, 또는 Temporal 인터페이스의 plus()/minus() 메서드에서 단위를 지정할 때 유용.

Java
 
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

LocalDate today = LocalDate.now();
LocalDate nextYear = today.plus(1, ChronoUnit.YEARS); // 1년 후
LocalDate tenDaysAgo = today.minus(10, ChronoUnit.DAYS); // 10일 전

 

java.util 패키지와의 상호 운용성:

java.util.Date와 java.util.Calendar를 java.time 패키지의 클래스로 변환하거나, 그 반대로 변환해야 하는 경우가 있을 수 있다.

  • java.util.Date -> java.time.Instant: Date.toInstant()
  • java.time.Instant -> java.util.Date: Date.from(Instant)
  • java.util.Date -> java.time.LocalDateTime: LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault())
  • java.time.LocalDateTime -> java.util.Date: Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant())
  • java.util.Calendar -> java.time.ZonedDateTime: calendar.toInstant().atZone(calendar.getTimeZone().toZoneId())
  • java.time.ZonedDateTime -> java.util.Calendar: GregorianCalendar.from(zonedDateTime)
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class ConversionExample {
    public static void main(String[] args) {

        // Date -> Instant
        Date utilDate = new Date();
        Instant instantFromDate = utilDate.toInstant();
        System.out.println("Date -> Instant: " + instantFromDate);

        // Instant -> Date
        Instant instant = Instant.now();
        Date dateFromInstant = Date.from(instant);
        System.out.println("Instant -> Date: " + dateFromInstant);

        // Date -> LocalDateTime
        LocalDateTime localDateTimeFromDate = LocalDateTime.ofInstant(utilDate.toInstant(), ZoneId.systemDefault());
        System.out.println("Date -> LocalDateTime: " + localDateTimeFromDate);

        // LocalDateTime -> Date
        LocalDateTime localDateTime = LocalDateTime.now();
        Date dateFromLocalDateTime = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
        System.out.println("LocalDateTime -> Date: " + dateFromLocalDateTime);
		
        // Calendar -> ZonedDateTime
        Calendar calendar = Calendar.getInstance();
        ZonedDateTime zonedDateTimeFromCalendar = calendar.toInstant().atZone(calendar.getTimeZone().toZoneId());
        System.out.println("Calendar -> ZonedDateTime: " + zonedDateTimeFromCalendar);

        // ZonedDateTime -> Calendar
        ZonedDateTime zonedDateTime = ZonedDateTime.now();
        Calendar calendarFromZonedDateTime = GregorianCalendar.from(zonedDateTime);
        System.out.println("ZonedDateTime -> Calendar: " + calendarFromZonedDateTime.getTime()); //Calendar는 .getTime()으로 출력
    }
}

java.time 패키지 사용 시 권장 사항:

  • java.util.Date와 java.util.Calendar 사용 최소화: 새로운 코드를 작성할 때는 java.time 패키지를 우선적으로 사용하고, 기존 코드와의 호환성을 위해서만 java.util.Date와 java.util.Calendar를 사용.
  • 불변성 활용: java.time 객체는 불변이므로, 메서드 호출 시 새로운 객체가 반환됨.
  • 명확한 타임존 처리: 타임존을 명시적으로 처리해야 하는 경우에는 ZonedDateTime을 사용하고, ZoneId를 사용하여 타임존을 지정.
  • DateTimeFormatter 사용: 날짜/시간을 포매팅하거나 파싱할 때는 DateTimeFormatter를 사용.
    SimpleDateFormat은 thread-safe하지 않으므로 주의.
  • Duration과 Period 구분: 시간 간격(초, 나노초)은 Duration, 기간(년, 월, 일)은 Period를 사용.