일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- pooled-lo
- MSSQL
- mysql equal null
- getEntityGraph
- EmbeddedId
- Protecle
- 1*1000
- https
- 오블완
- createEntityGraph
- @EntityListeners
- AuditingEntityListener
- fractional seconds
- load order
- RootGraph
- 버전 문자열 비교
- getDateCellValue
- yml
- MYSQL
- 운동해서 광명찾자
- apatch poi
- NamedEntityGraph
- mysql not equal null
- deserializer
- 티스토리챌린지
- mysql =
- spring boot
- +9:00
- @CreateDate
- sendFractionalSeconds
- Today
- Total
Hello
Daylight Saving Time(DST) in JAVA 본문
데이터 포멧이 미국시인 데이터를 받아 한국시로 변환해 처리해야하는 일이 있었다.
단순하게 미국시+8을 생각했는데 ST(Summer time) 혹은, DST(Daylight saving time)라고 하는 시간제가 적용된 시간 처리가 필요했다.
먼저 DST에 대해 알아볼 필요가 있다. 하절기에 표준시를 원래 시간보다 한 시간 앞당긴 시간을 쓰는 것을 말한다.
필요하다면 링크를 통해 DST사용 국가 확인 후 적용하면 된다.
2023년 DST는 2023년3월12일 - 2023년11월05일에 종료된다.
여름에는 오전 2시부터 시작되는데 시계는 1시간 당겨쓰게 되어 오전 3시부터 시작되고,
가을에는 시계가 오전 2시부터 시작되어 당겨쓴 1시간을 반납해 1시로 되돌아 갑니다.
즉, 2023-03-12 02:00:00 시 부터 2023-03-12 03:00:00 시 로 사용된다.
java 8 에서 추가된 java.time을 사용해 DST를 처리하는 방법을 정리하고자 한다.
java.util을 사용해 처리하는 방법은 baeldung 블로그에 정리된 내용을 읽어보는 것을 추천합니다.
LocalDateTime, ZonedDateTime
LocalDateTime은 시간대가 없는 날짜-시간을 표현한다.
ZonedDateTime은 시간대가 있는 날짜-시간을 표현한다. 애매한 현지 날짜-시간을 처리하는데 오프셋과 함께 날짜-시간을 저장합니다. DST를 처리하기 위해서는 ZonedDateTime를 사용해야한다.
아래의 예시를 확인하면 dateTime 문자열은 시간대 정보를 포함하고 있지만 LocalDateTime은 오프셋, 시간대 정보나 time-line 에서 순간을 표현하지 못하는 것을 확인 할 수 있습니다.
ZonedDateTime은 두 가지 정보 -08:00, [America/Los_Angeles]가 같이 보여진다. 각각 ZoneOffset, ZoneId 입니다.
String dateTime = "2023-03-12 01:59:59 AM America/Los_Angeles";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm:ss a VV", Locale.ENGLISH);
LocalDateTime localDateTime = LocalDateTime.parse(dateTime, dateTimeFormatter);
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateTime, dateTimeFormatter);
Assert.assertEquals("2023-03-12T01:59:59", localDateTime.toString());
Assert.assertEquals("2023-03-12T01:59:59-08:00[America/Los_Angeles]", zonedDateTime.toString());
LocalDateTime은 zoneOffset, ZoneId 갖지 못하기 때문에 DST처리가 필요하다면 ZonedDateTime으로 변환이 필요하다.
ZonedDateTime change1 = ZonedDateTime.of(localDateTime, ZoneId.of("America/Los_Angeles"));
ZonedDateTime change2 = localDateTime.atZone(ZoneId.of("America/Los_Angeles")); //내부적으로 ZonedDateTime.of 호출
Assert.assertEquals("2023-03-12T01:59:59-08:00[America/Los_Angeles]", change1.toString());
Assert.assertEquals("2023-03-12T01:59:59-08:00[America/Los_Angeles]", change2.toString());
2023년 DST 시작 시간은 위에 작성해 두었다.
DST가 시작되기 전 2023-03-12 01:59:59 시간에 10분을 더하면 DST가 처리된 03:09:59시간, ZoneOffset(-07:00) 을 확인할 수 있습니다.
1시간 10분이 차이나는 것 처럼 보이나 실제 차이는 10분 차이나는 것을 확인할 수 있다.
ChronoUnit 은 TemporalUnit을 구현한 Enum으로 다양한 시간 단위를 갖고 있으며 시간과 관련된 다양한 API를 지원한다.
나는 분의 개념을 나타내는 단위로 두 파라메터의 시간을 계산하는 메소드를 사용해 차이를 계산했다.
ZonedDateTime beforeDst = ZonedDateTime.of(localDateTime, ZoneId.of("America/Los_Angeles"));
Assert.assertEquals("2023-03-12T01:59:59-08:00[America/Los_Angeles]", beforeDst.toString()); //DST가 시작되기 전 +8로 계산 됨
ZonedDateTime afterDst = beforeDst.plusMinutes(10); //10분을 추가하게 되면 DST가 시작되는 시간을 넘김
Assert.assertEquals("2023-03-12T03:09:59-07:00[America/Los_Angeles]", afterDst.toString()); // DST가 적용되어 +7로 계산 됨
long deltaBetweenDatesInMinutes = ChronoUnit.MINUTES.between(beforeDst, afterDst);
Assert.assertEquals(10L, deltaBetweenDatesInMinutes);
A TimeZone 을 받아 B TimeZone으로 계산
나는 미국시를 받아 한국시로 계산해 DB 저장이 필요했다.
ZonedDateTime.withZoneSameInstant 메소드는 다른 타임존을 가진 복사본을 만든다. 시간대를 변경하고 동일한 순간을 유지하는 것입니다.
미국 로스앤젤레스 2023-03-12T03:09:59 시간은 한국시로 2023-03-12T19:09:59 인 것을 확인 할 수 있다.
ZonedDateTime beforeDst = ZonedDateTime.of(localDateTime, ZoneId.of("America/Los_Angeles"));
ZonedDateTime afterDst = beforeDst.plusMinutes(10);
Assert.assertEquals("2023-03-12T01:59:59-08:00[America/Los_Angeles]", beforeDst.toString());
Assert.assertEquals("2023-03-12T03:09:59-07:00[America/Los_Angeles]", afterDst.toString());
ZonedDateTime beforeDstToKor = beforeDst.withZoneSameInstant(ZoneId.of("Asia/Seoul"));
ZonedDateTime afterDstToKor = afterDst.withZoneSameInstant(ZoneId.of("Asia/Seoul"));
Assert.assertEquals("2023-03-12T18:59:59+09:00[Asia/Seoul]",beforeDstToKor.toString());
Assert.assertEquals("2023-03-12T19:09:59+09:00[Asia/Seoul]",afterDstToKor.toString());
현지 날짜, 시간을 유지 하고 시간대가 다른 경우가 필요하다면 ZonedDateTime.withZoneSameLocal 메소드를 사용하면 된다.
미국 로스앤젤레스 2023-03-12T03:09:59[America/Los_Angeles] -> 한국 2023-03-12T03:09:59[Asia/Seoul]로 만들 수 있다.
ZonedDateTime beforeDst = ZonedDateTime.of(localDateTime, ZoneId.of("America/Los_Angeles"));
Assert.assertEquals("2023-03-12T01:59:59-08:00[America/Los_Angeles]", beforeDst.toString());
ZonedDateTime beforeDstToKor = beforeDst.withZoneSameLocal(ZoneId.of("Asia/Seoul"));
Assert.assertEquals("2023-03-12T01:59:59+09:00[Asia/Seoul]",beforeDstToKor.toString());
Three-letter time zone IDs
JDK 1.1.x 버전에서 호환성을 위해 "PST","AST" 등 세글자 시간대 ID를 지원했으나, 동일한 약어로 여러시간대가 존재해 의도와 다르게 동작할 수 있어 권장하지 않는 방법입니다.
String dateTime = "2023-03-12 01:59:59 AM America/Los_Angeles";
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd h:mm:ss a VV", Locale.ENGLISH);
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateTime,dateTimeFormatter);
Assert.assertEquals("2023-03-12T01:59:59-08:00[America/Los_Angeles]", zonedDateTime.toString());
ZonedId docs를 참고하면 Map으로 갖고있는 Three-letter time zone 리스트를 확인할 수 있다.
추가로, DateTimeFormatter 사용 시 정의된 패턴을 사용하는 것이 아닌 사용자 지정 패턴을 만들 경우 기호의 의미 확인 후 패턴 작성 사용이 필요하다.
참고 :
https://www.baeldung.com/java-daylight-savings
https://docs.oracle.com/javase/8/docs/api/java/time/temporal/ChronoUnit.html
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html
https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html
https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
https://unicode-org.github.io/icu/userguide/format_parse/datetime
https://stackoverflow.com/questions/30710829/java-time-datetimeformatter-pattern-for-timezone-offse
'java' 카테고리의 다른 글
Java MultipartFile to String (0) | 2023.10.25 |
---|---|
[JPA] 일대일 조인 테이블 @EmbeddedId 사용 (0) | 2023.10.12 |
JPA 기본키 생성 전략 (0) | 2023.09.07 |
Apache poi excel 읽기 빈 값(null), 숫자를 문자로 읽기 (0) | 2023.04.06 |
Apache poi getDateCellValue() 엑셀과 다른 경우 (0) | 2023.02.07 |