Java 날짜 - Date-Time API

LocalDateTime/OffsetDateTime/ZonedDateTime

개요

JavaSE 8부터는 날짜/시간을 다루는 새로운 API로 Date and Time API가 도입되었다. Date-Time API는 java.time 패키지에 포함되어 있다.
java.time 패키지로 제공되는 주요 클래스는 아래와 같다.

  • LocalDateTime/LocalDate/LocalTime: 시차 정보가 없는 로컬 일시
  • OffsetDateTime/OffsetTime: 시차 정보가 있는 날짜와 시간
  • ZonedDateTime: 타임존과 지역을 기반으로 한 정보(서머 타임 등)가 있는 날짜와 시간

일반적으로는 시차 정보를 필요로 하지 않는 경우에는 Local클래스를 사용하고, 그렇지 않으면 Zoned클래스를 사용하는 식으로 구분된다. 그리고 Offset~클래스를 이용하는 경우는 많지 않을 것으로 생각된다.

LocalDateTime의 now/of/parse 메서드

now/of/parse 메서드는 문자열에서 날짜/시간 값을 생성한다.

public static LocalDateTime now()
public static LocalDateTime of(int year, int month, int day, int hour, int minute [,int second [,int nano]])
public static LocalDate parse(CharSequence text)
    year: 년
    month: 달
    day: 일
    hour: 시
    minute: 분
    second: 초
    nano: 나노초
    text: 날짜 시간 문자열

java.time 패키지에서는 목적에 따라 Local/Offset/Zoned 계열 클래스가 준비되어 있는데, 이 메소드들은 생성하는 방법은 동일하다.
먼저 현재 시간을 표시하려면 now 메서드를 사용한다.
of 메서드는 연월일/시분초를 지정하여 객체를 생성한다. parse 메소드를 사용하여 날짜/시간 문자열로부터 객체를 생성할 수도 있다.

package com.devkuma.basic.datetime;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

public class TimeCreate {
    public static void main(String[] args) {
        LocalDateTime local = LocalDateTime.now();
        System.out.println(local);
        // 결과: 2022-09-05T12:03:34.123041

        OffsetDateTime offset = OffsetDateTime.of(
                2022, 9, 5, 12, 3, 29, 0, ZoneOffset.of("+09:00"));
        System.out.println(offset);
        // 결과: 2022-09-05T12:03:29+09:00

        ZonedDateTime zoned = ZonedDateTime.parse("2022-09-05T12:03:29+09:00[Asia/Seoul]");
        System.out.println(zoned);
        // 결과: 2022-09-05T12:03:29+09:00[Asia/Seoul]
    }
}

실행 결과:

2022-09-05T12:03:34.123041
2022-09-05T12:03:29+09:00
2022-09-05T12:03:29+09:00[Asia/Seoul]

각 객체에서 다음 메서드를 사용하여, 각각의 날짜/시간 요소에 액세스할 수도 있다.

각각의 날짜/시간 요소에 액세스하는 메소드

메소드 개요
getYear 년
getMonth 월(Month 타입)
getMonthValue 월(1~12)
getDayOfMonth 일
getDayOfWeek 요일
getDayOfYear 연간 통산일
getHour 시간
getMinute 분
getSecond 초
getNano 나노초

between 메서드

between 메서드는 날짜/시간 값의 차이를 구한다.

public static Period between(LocalDate start, LocalDate end) // Period
public static Duration between(Temporal start, Temporal end) // Duration
    start: 시작일시
    end: 종료일시

Period/Duration#between 메서드를 사용하여, 날짜/시간 객체 간의 시간차를 구할 수 있다. Period는 날짜 간격을 나타내고, Duration은 시간 간격을 나타내는 객체이다.

  • Temporal 클래스는 Local/Offset/Zoned 계열 클래스의 공통 인터페이스이다.

다음은 미리 준비한 2개의 날짜/시간의 차이를 Period/Duration#between 메서드로 구하는 예제이다.

TimeBetween.java

package com.devkuma.basic.datetime;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Period;

public class TimeBetween {
    public static void main(String[] args) {
        LocalDateTime local1 = LocalDateTime.of(
                2021, 1, 1, 3, 30, 0, 0);
        LocalDateTime local2 = LocalDateTime.of(
                2022, 9, 5, 12, 14, 30, 0);
        System.out.println(local1); // 결과: 2021-01-01T03:30
        System.out.println(local2); // 결과: 2022-09-05T12:14:30

        Period period = Period.between(local1.toLocalDate(), local2.toLocalDate());
        System.out.println("날짜 차이: " + period);   // 결과: 날짜 차이: P1Y8M4D

        System.out.println("날짜 차이: " +
                period.getYears() + "년 " + period.getMonths() + "월 " +
                period.getDays() + "일"); // 결과: 날짜 차이: 1년 8월 4일

        Duration duration = Duration.between(local1, local2);
        System.out.println("시간 차이: " + duration);
        // 결과: 시간 차이: PT14696H44M30S

        System.out.println("시간 차이: " + duration.toHours() + "시간");
        // 결과: 시간 차이: 14696시간

        System.out.println("시간 차이: " + duration.toMinutes() + "분");
        // 결과: 시간 차이: 881804분

        System.out.println("시간 차이: " + duration.getSeconds() + "초");
        // 결과: 시간 차이: 52908270초
    }
}

실행 결과:

2021-01-01T03:30
2022-09-05T12:14:30
날짜 차이: P1Y8M4D
날짜 차이: 1년 8월 4일
시간 차이: PT14696H44M30S
시간 차이: 14696시간
시간 차이: 881804분
시간 차이: 52908270초

Period/Duration 객체로부터 일수나 시간 수를 구하려면, 다음과 같은 메서드를 이용한다.

Period/Duration 클래스의 주요 메서드

클래스 메서드 개요
Period getYears 년수
getDays 일수
getMonths 월수
Duration toDays 일수
toHours 시간 수
toMinutes 분수
toMillis 밀리초 수
toNanos 나노초 수

날짜 및 시간 데이터 계산

public LocalDateTime plus(long value, TemporalUnit unit)
public LocalDateTime minus(long value, TemporalUnit unit)
    value: 증감분
    unit: 단위

plus/minus 메서드를 사용하여, 날짜/시간 요소를 더하고 뺄 수 있다. 인수 unit는 TemporalUnit 인터페이스의 구현 클래스인 ChronoUnit의 상수로 추가 혹은 빼려는 단위를 지정한다.

TemporalUnit 인터페이스의 주요 상수

상수 개요
YEARS 년
MONTHS 달
DAYS 일
WEEKS 주
HOURS 시간
MINUTES 분
SECONDS 초
MILLIS 밀리
NANOS 나노초

다음은 미리 준비한 날짜에 대해, 3개월 후 10일 전의 날짜를 구하는 예제이다.

package com.devkuma.basic.datetime;

import java.time.LocalDateTime;

public class TimePlus {
    public static void main(String[] args) {
        LocalDateTime local = LocalDateTime.parse("2022-09-05T12:30");
        System.out.println(local);  // 결과: 2022-09-05T12:30
        System.out.println(local.plusMonths(3));    // 결과: 2022-12-05T12:30
        System.out.println(local.minusDays(10));    // 결과: 2022-08-26T12:30
    }
}

실행 결과:

2022-09-05T12:30
2022-12-05T12:30
2022-08-26T12:30

또한, plus/minus 메서드에는 각각의 날짜/시간 요소에 특화된 메소드가 다음과 같이 있다.

날짜/시간을 가산/감산하는 방법

메서드 개요
plusYears/minusYears 년
plusMonths/minusMonths 월
plusDays/minusDays 일
plusWeeks/minusWeeks 주수
plusHours/minusHours 시간
plusMinutes/minusMinutes 분
plusSeconds/minusSeconds 초
plusNanos/minusNanos 나노초

Date/Calendar 클래스에서 DateTime-API 클래스로 상호 변환

public static Date from(Instant instant) // Date
public Instant toInstant() // Date/Calendar
public static LocalDateTime ofInstant(Instant instant, ZoneId zone) // XxxxxDateTime
    instant: 변환할 인스턴트

새롭게 Date-Time API가 도입되었다고 해도, 갑자기 모든 코드를 Date-Time API로 통일하는 것은 현실적으로 쉽지 않다. 당분간은 기존의 java.util 패키지와 병행하게 될 것이다. 그래서 기존의 Date/Calendar 클래스에 Date-Time API와 상호 변환하기 위한 메소드를 제공하고 있다.
Date#from 메서드는 주어진 Instant 객체에서 Date 객체를 생성한다. Instant는 날짜 시간을 1970년 1월 1일 이후의 경과 시간으로 유지하는 클래스이다. Date-Time API의 날짜/시간 클래스는 각각 toInstant 메소드를 제공하고 있어서 Instant 객체를 생성할 수 있게 되어 있으므로, 이를 from 메소드에 건네주면 Date 객체를 생성할 수 있다.

  • Calendar 클래스에는 Date#from 메서드에 해당하는 메서드는 없다. 생성된 Date 객체로부터 Calendar 객체를 생성하게 된다.

Calendar/Date 클래스에도 toInstant 메소드는 제공하고 있다. 생성된 Instant 객체를 LocalDateTime/OffsetDateTime/ZonedDateTime 클래스의 ofInstant 메서드에 전달하여 각각 해당 객체를 생성할 수 있다.

다음은 LocalDateTime 객체를 Date 객체로, Calendar 객체를 LocalDateTime 객체로 변환하는 예제이다.

package com.devkuma.basic.datetime;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.Calendar;
import java.util.Date;

public class TimeTrans {
    public static void main(String[] args) {
        LocalDateTime local = LocalDateTime.now();
        System.out.println(local);  // 결과: 2022-09-05T14:32:05.526238

        Instant instant = local.toInstant(ZoneOffset.of("+09:00"));
        Date date = Date.from(instant);
        System.out.println(date);   // 결과: Mon Sep 05 14:32:05 KST 2022

        Calendar cal = Calendar.getInstance();
        Instant instant2 = cal.toInstant();
        LocalDateTime local2 = LocalDateTime.ofInstant(instant2, ZoneId.of("Asia/Seoul"));
        System.out.println(local2); // 결과: 2022-09-05T14:32:05.542
    }
}

실행 결과:

2022-09-05T14:32:05.526238
Mon Sep 05 14:32:05 KST 2022
2022-09-05T14:32:05.542

String에서 LocalDate/LocalDateTime으로 변환하는 방법

String을 java.time.LocalDate로 변환할 때는 LocalDate.parse와 DateTimeFormatter를 이용하여 날짜 문자열을 변환한다.
String을 java.time.LocalDateTime로 변환할 때는 문자열을 LocalDate로 만들고, LocalTime과 같이 인자로 받아서 생성한다.

package com.devkuma.basic.datetime;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

public class StringConvent {
    public static void main(String[] args) {
        String strDate = "2022/12/12";
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");

        LocalDate localDate = LocalDate.parse(strDate, dateTimeFormatter);
        System.out.println(localDate);

        LocalDateTime ldt = LocalDateTime.of(localDate, LocalTime.of(0, 0));
        System.out.println(ldt);
    }
}

실행 결과:

2022-12-12
2022-12-12T00:00

날짜 문자열이 2022/12/12와 같은 형식이면, Format 지정은 DateTimeFormatter.ISO_DATE로 대체할 수 있다. DateTimeFormatter.ISO_DATE는 yyyy-MM-dd를 상수로 선언한다.