나는 이때까지 토이프로젝트를 만들던 실무를 진행하던 거의 LocalDateTime
, LocalDate
, LocalTime
등 Localxxx
를 많이 사용했었는데 꽤 다양한 날짜와 시간 관련된 클래스가 있다는 것을 처음 알았다. 큰 카테고리별로 알아보자.
이름 그대로 Local
은 특정 지역의 날짜와 시간만을 고려할 때 사용한다. 한국에서 사용한다면 한국의 날짜,시간이 기준이 되는 모양. 글로벌한 서비스를 하지 않으면 거의 이 녀석을 사용한다고 한다. (언젠가는 글로벌한 시간도 사용해보고 싶어..)
코드로 빠르게 알아보자
//LocalDate = 날짜
LocalDate nowDate = LocalDate.now(); //2025-01-07, 현재 날짜
LocalDate ofDate = LocalDate.of(1995, 7, 17);
//계산
ofDate = ofDate.plusDays(10); // 불변객체라서 반환 받아서 사용해야 한다.
생성 방법은 간단하게 .now()
와 .of()
를 사용할 수 있다.
다양한 계산을 plusXXX()
와 같은 형태로 제공한다. LocalDate는 불변객체이기 때문에 계산을 한 이후에 반환 받아서 사용해야한다. 위의 예제에서는 ofDate
에 덮어씌웠다.
//LocalTime = 시간
LocalTime nowTime = LocalTime.now(); //현재 시간
LocalTime ofTime = LocalTime.of(11, 58, 12);
//계산
ofTime = ofTime.plusMinutes(15);
LocalTime
도 마찬가지.
//LocalDateTime = LocalDate + LocalTime
LocalDateTime nowDt = LocalDateTime.now(); //현재 날짜 + 시간
LocalDateTime ofDt = LocalDateTime.of(2025, 1, 7, 12, 1, 15);
//날짜와 시간 분리
LocalDate localDate = ofDt.toLocalDate();
LocalTime localTime = ofDt.toLocalTime();
//날짜와 시간 합체
LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
//계산
LocalDateTime ofDtPlus = ofDt.plusDays(15);
LocalDateTime
은 앞의 두 LocalDate
,LocalTime
클래스를 합쳐놓은 것이라고 한다.
그래서 분리를 할 수 있고 localDate
와 localTime
을 전달받아서 하나로 합치는 것도 가능!!
여기는 글로벌~
ZonedDateTime
을 사용하는데 이 녀석은 LocalDateTime
,ZoneId
,ZoneOffset
을 가지고 있는 녀석이다.
//ZoneId
ZoneId zoneId = ZoneId.systemDefault(); // 시스템이 사용하는 기본 ZoneId
ZoneId seoulZoneId = ZoneId.of("Asia/Seoul"); //타임존을 직접 제공
ZoneId
라는 녀석은 뭘까?
타임존이라고 부르는데 이건 Asia/Seoul
, UTC
, America/New_York
과 같은 시간대 지역(?) 을 나타내는 말이다.
이 타임존(ZoneId) 내부에는 일광 절약 시간(a.k.a 썸머타임) 관련 정보, UTC와의 오프셋 정보를 포함하고 있다고 한다.
오프셋 정보? -> 우리나라의 경우 UTC+9:00
와 같이 표시 되는데 이 정보가 오프셋 정보다. UTC 랑 9시간 차이가 난다는 뜻! (+라서 늦다)
//ZonedDateTime
ZonedDateTime nowZdt = ZonedDateTime.now(); //2025-01-07T13:15:26.259753300+09:00[Asia/Seoul]
LocalDateTime ldt = LocalDateTime.of(2014, 3, 1, 13, 30, 30);
ZonedDateTime zdt1 = ZonedDateTime.of(ldt, ZoneId.of("Asia/Seoul"));
ZonedDateTime zdt2 = ZonedDateTime.of(2025, 1, 7, 12, 17, 30, 0, ZoneId.of("Asia/Seoul"));
ZonedDateTime
은 이처럼 생성할 수 있고 날짜,시간,오프셋,타임존 이 표시된다.
그리고 이렇게 생성한 시간에는 일광 절약 시간제가 적용되서 표시된다고 한다. (썸머타임을 해본적이 없어서 어떻게 계산하는지 모르겠네... ㅠㅠ)
ZonedDateTime utcZdt = zdt2.withZoneSameInstant(ZoneId.of("UTC")); // 타임존 변경
그리고 withZoneSameInstant()
을 사용해서 타임존을 변경할 수 있다. 우리나라 시간으로 객체를 생성한 이후에 해당 메서드를 사용해서 UTC로 변경하면 UTC시간이 출력된다. 즉 우리나라 시간 -9시간을 자동으로 계산해서 표시해줌!
//OffsetDateTime
OffsetDateTime nowOdt = OffsetDateTime.now();
LocalDateTime ldt = LocalDateTime.of(2025, 7, 17, 13, 12, 11); //2025-07-17T13:12:11
OffsetDateTime odt = OffsetDateTime.of(ldt, ZoneOffset.of("+09:00")); //2025-07-17T13:12:11+09:00
또 OffsetDateTime
이라는 녀석도 있는데 이녀석은 LocalDateTime
과 ZoneOffset
이 합쳐진 녀석이다. 이 녀석의 역할은 ZoneId
가 없어서 일광 절약 시간제가 적용되지 않고 UTC로부터의 시간대 차이인 오프셋 정보만 포함되는 것!
실제로 위 코드를 실행하면 내가 지정한 시간에 오프셋+09:00
만 붙어서 출력되는 걸 볼 수 있다.
음.. 뭐 일단 이런것도 있다고 알아두고 넘어가자..!! (글로벌 서비스를 하지 않으면 잘 사용하지 않는다고 한다...)
이것도 그냥 이런게 있다~ 정도만 알아두고 간단하게 지나가자
//생성
//UTC 기준
Instant now = Instant.now(); //2025-01-07T04:47:26.659123200Z
//ZonedDateTime 활용
ZonedDateTime zdt = ZonedDateTime.now();
Instant from = Instant.from(zdt); //2025-01-07T04:47:26.659123200Z
//현재 시간이 epoch 시간으로 몇초 흘렀는지
long epochSecond = now.getEpochSecond(); //epochSecond = 1736225246
Instant epochStart = Instant.ofEpochSecond(0); //1970-01-01 00:00:00 기준
//epochStart = 1970-01-01T00:00:00Z
//계산
Instant later = epochStart.plusSeconds(3600); //1970-01-01T01:00:00Z
//조회
//later 가 epoch 시간으로부터 얼마나 흘렀는지
long laterEpochSecond = later.getEpochSecond(); //3600
Instant
를 어디에 사용하면 좋을까... 흐으음...
- 전 세계적인 시간 기준 필요 시: UTC를 기준으로 하기 때문
- 시간대 변환 없이 시간 계산 필요 시: 시간대의 변화 없이 순수하게 시간의 흐름만을 다루고 싶을 때
- 데이터 저장 및 교환: DB에 날짜와 시간 정보를 저장하거나, 다른 시스템과 날짜와 시간 정보를 교환할 때
라고 하는데. Instant
가 항상 UTC를 기준으로 생성되기 때문에 이런 동일한 기준점이 필요한 곳에서 사용하기 적합한것 같다. 하지만 대부분 LocalDateTime
이나 ZonedDateTime
을 더 많이 사용한다고 한다.
//생성
Period period = Period.ofDays(10); //P10D
//계산에 사용
LocalDate localDate = LocalDate.of(2025, 1, 7); //2025-01-07
LocalDate plusDays = localDate.plus(period); //2025-01-17 period에서 설정한 10일 만큼 plus 가 된다.
//기간 차이
LocalDate startDate = LocalDate.of(2024, 10, 20);
LocalDate endDate = LocalDate.now();
Period between = Period.between(startDate, endDate); //2025-01-07
System.out.println("기간: " + between.getMonths() + "개월 " + between.getDays() + "일");
날짜를 계산할 때는 Period
를 사용한다. 마지막 기간 차이 계산하는 것을 보면 getMonths()
와 getDays
를 사용해서 얼마나 차이나는지 확인 할 수 있다. 결과는 기간: 2개월 18일
이렇게 나온다.
//생성
Duration duration = Duration.ofMinutes(30); //PT30M
//계산에 사용
LocalTime localTime = LocalTime.of(1, 0); //01:00
LocalTime plusTime = localTime.plus(duration); //01:30 duration에서 설정한 30분 만큼 plus
//시간 차이
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(10, 0);
Duration between = Duration.between(start, end);
System.out.println("차이: " + between.getSeconds() + "초"); //차이: 3600초
System.out.println("근무 시간: " + between.toHours() + "시간 " + between.toMinutesPart() + "분"); //근무 시간: 1시간 0분
System.out.println("근무 시간: " + between.toMinutes() + "분"); //근무 시간: 60분
시간을 계산할 때는 Duration
을 사용, 여기서 중요한 점은 마지막 toMinutes()
와 toMinutesPart()
의 차이다. toMinutes()
는 단순하게 몇분 인지 계산하는 것이고 toMinutesPart()
는 시간과 분으로 나눠서 계산해준다. 60분은 1시간이라 1시간을 계산하고 남은 분만 보여주는 것. 여기선 남은 분이 없기 때문에 0분으로 출력되는 것이다. 70분이라면 10분이 출력될 것이다.
음... 일단 LocalDateTime 과 Period, Duration 을 좀 많이 활용 할 것 같다. 나머지 Zone,Instant 부분은 있다는 것만 알아두고 필요할 때 다시 찾아서 공부하는게 좋을 듯 하다.
- 김영한의 실전 자바 - 중급 1편