[TIL] 20250107 날짜와 시간

Drumj·2025년 1월 7일
0

2025 TIL

목록 보기
8/11

날짜와 시간

나는 이때까지 토이프로젝트를 만들던 실무를 진행하던 거의 LocalDateTime, LocalDate, LocalTimeLocalxxx를 많이 사용했었는데 꽤 다양한 날짜와 시간 관련된 클래스가 있다는 것을 처음 알았다. 큰 카테고리별로 알아보자.

Local

이름 그대로 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 클래스를 합쳐놓은 것이라고 한다.
그래서 분리를 할 수 있고 localDatelocalTime을 전달받아서 하나로 합치는 것도 가능!!


Zone

여기는 글로벌~
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 이라는 녀석도 있는데 이녀석은 LocalDateTimeZoneOffset 이 합쳐진 녀석이다. 이 녀석의 역할은 ZoneId 가 없어서 일광 절약 시간제가 적용되지 않고 UTC로부터의 시간대 차이인 오프셋 정보만 포함되는 것!

실제로 위 코드를 실행하면 내가 지정한 시간에 오프셋+09:00 만 붙어서 출력되는 걸 볼 수 있다.

음.. 뭐 일단 이런것도 있다고 알아두고 넘어가자..!! (글로벌 서비스를 하지 않으면 잘 사용하지 않는다고 한다...)


기계 중심의 시간 - Instant

이것도 그냥 이런게 있다~ 정도만 알아두고 간단하게 지나가자

//생성
//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편

0개의 댓글