일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- spring
- bean
- ECS
- producer
- cd
- QueryDSL
- git
- kafka
- Entity
- PAGING
- AWS
- mysql
- CodePipeline
- consumer
- JPA
- entity graph
- K8s
- Spring JPA
- centos7
- transactionaleventlistener
- Kotlin
- topic생성
- API
- spring kafka
- CI
- mirror maker2
- offsetdatetime
- Spring Data JPA
- Streams
- Kubernetes
- Today
- Total
Yebali
Java, Kotlin의 날짜와 시간 본문
Java나 Kotlin에서 날짜와 시간을 다루는 API들을 알기 전
만약 Time zone, UTC 같은 시간에 대한 몇 가지 개념들을 모르신다면 아래 글을 참고하세요.
Timezone, GMT/UTC 등 시간의 기준과 표기법
Timezone Timezone은 동일한 로컬 시간을 따르는 지역을 말한다. 일반적으로 영국의 그리니치 천문대를 기준으로 경도 값에 의해 달라지지만 국가나 지역의 경계를 따르는 경우도 있다. 보통 국가별
yebali.tistory.com
LocalDate / LocalTime / LocalDateTime
Time zone(ZoneOffset / ZoneRegion)에 대한 정보가 없는 API들이다.
그냥 사용하게 되면 해당 컴퓨터의 Timezone에 맞춘 시간을 반환한다.
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.util.Calendar
fun main() {
println("zoneId = ${Calendar.getInstance().timeZone.id}") // Asia/Seoul
println("localDateNow = ${LocalDate.now()}") // 2022-11-27
println("localTimeNow = ${LocalTime.now()}") // 23:07:48.314023
println("localDateTimeNow = ${LocalDateTime.now()}") // 2022-11-27T23:07:48.314036
}
특정 시간대를 사용하고 싶다면 VM 옵션에 `-Duser.timezone=UTC` 와 같이 시간대를 명시해주면 된다.
ZoneOffset
UTC와 현지 시간과의 차이를 의미한다.
우리나라는 KST를 사용하는데 KST는 UTC보다 9시간 빠르기 때문에 'UTC +09:00'으로 표기한다.
'UTC +09:00'는 아래와 같이 다양한 방법으로 나타낼 수 있다.
import java.time.ZoneId
import java.time.ZoneOffset
fun main() {
println("zoneOffset1 = ${ZoneOffset.of("+9")}") // +09:00
println("zoneOffset2 = ${ZoneOffset.of("+09")}") // +09:00
println("zoneOffset3 = ${ZoneOffset.of("+09:00")}") // +09:00
println("zoneOffset4 = ${ZoneOffset.of("+09:00:00")}") // +09:00
// ZoneOffset은 ZoneId의 자식 클래스이다.
println("zoneOffset5 = ${ZoneId.of("+9")}") // +09:00
println("zoneOffset6 = ${ZoneId.of("+09")}") // +09:00
println("zoneOffset7 = ${ZoneId.of("+09:00")}") // +09:00
println("zoneOffset8 = ${ZoneId.of("+09:00:00")}") // +09:00
}
ZoneRegion
Time zone 정보이다.
KST는 Time zone의 이름이고 이를 나타내는 ZoneRegion은 'Asia/Seoul'이다.
import java.time.ZoneId
fun main() {
// ZoneRegion은 ZoneId의 자식 클래스이나 Public 클래스가 아니라서 ZoneId 클래스를 통해서만 생성이 가능하다.
println("zoneRegion = ${ZoneId.of("Asia/Seoul")}") // Asia/Seoul
println("zoneRegion = ${ZoneId.of("UTC+9")}") // UTC+09:00
println("zoneRegion = ${ZoneId.of("UTC+09")}") // UTC+09:00
println("zoneRegion = ${ZoneId.of("UTC+09:00")}") // UTC+09:00
println("zoneRegion = ${ZoneId.of("UTC+09:00:00")}") // UTC+09:00
println("zoneRegion = ${ZoneId.of("GMT+9")}") // GMT+09:00
println("zoneRegion = ${ZoneId.of("GMT+09")}") // GMT+09:00
println("zoneRegion = ${ZoneId.of("GMT+09:00")}") // GMT+09:00
println("zoneRegion = ${ZoneId.of("GMT+09:00:00")}") // GMT+09:00
println("zoneRegion = ${ZoneId.of("UT+9")}") // UT+09:00
println("zoneRegion = ${ZoneId.of("UT+09")}") // UT+09:00
println("zoneRegion = ${ZoneId.of("UT+09:00")}") // UT+09:00
println("zoneRegion = ${ZoneId.of("UT+09:00:00")}") // UT+09:00
}
ZoneRules
ZoneOffset의 'UTC + 09:00'와 ZoneRegion의 'Asia/Seoul'을 보면 전혀 차이가 없어 보인다.
하지만 이 둘은 분명한 차이점을 가지고 있다.
차이는 바로 DST(Daylight Saving Time)이나 Summer time 같은 Time Transition Rule의 포함 여부이다.
ZoneOffset은 Time Transition Rule을 포함하지 않는 ZoneRules를 가진다.
반면에 ZoneRegion은 TimeTrasition Rule을 포함할 수 있다. (없을 수도 있다)
import java.time.ZoneId
import java.time.ZoneOffset
fun main() {
// ZoneOffset이기 때문에 Time Transition Rule이 없다.
ZoneOffset.of("+01:00").rules.transitionRules.forEach { println(it) }
// ZoneRegion이지만, Time Transition Rule이 없다.
ZoneId.of("Africa/Brazzaville").rules.transitionRules.forEach { println(it) }
// Time Transition Rule을 가지고 있다.
// TransitionRule[Gap +01:00 to +02:00, SUNDAY on or after MARCH 25 at 02:00 STANDARD, standard offset +01:00]
// TransitionRule[Overlap +02:00 to +01:00, SUNDAY on or after OCTOBER 25 at 02:00 STANDARD, standard offset +01:00]
ZoneId.of("CET").rules.transitionRules.forEach { println(it) }
}
OffsetDateTime
OffsetDateTime은 LocalDateTIme에 + ZoneOffset 정보가 포함된 API이다.
import java.time.LocalDateTime
import java.time.OffsetDateTime
import java.time.ZoneId
import java.time.ZoneOffset
fun main() {
val christMax = OffsetDateTime.of(LocalDateTime.of(2022, 12, 25, 9, 0, 0), ZoneOffset.of("+09:00"))
println(christMax) // 2022-12-25T09:00+09:00
// 'Z'는 UTC를 의미한다.
println(christMax.atZoneSameInstant(ZoneId.of("Z"))) // 2022-12-25T00:00Z
}
ZonedDateTime
ZonedDateTime은 OffsetDateTime + ZoneRegion 정보가 포함된 API이다.
OffsetDateTime과의 차이점은 DST, Summer time과 같은 Time Trasition Rule을
포함하는 ZoneRegion을 가질 수 있다는 것이다.
실제로 summer time이 적용되는 유럽에는 CET(Central European Time)과 CEST(Central European Summer Time)이
모두 사용되는데 Java에서는 이 두 Time zone을 'CET'로 통일하여 사용하고
summer time에 의해 바뀌는 시간은 ZoneRules를 통해 내부에서 알아서 계산해준다.
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
fun main() {
// 2022-12-25T00:00+09:00
println(ZonedDateTime.of(LocalDateTime.of(2022, 12, 25, 0, 0, 0), ZoneId.of("+9")))
// 2022-12-25T00:00+09:00[Asia/Seoul]
println(ZonedDateTime.of(LocalDateTime.of(2022, 12, 25, 0, 0, 0), ZoneId.of("Asia/Seoul")))
// 2022-12-25T00:00+09:00[Asia/Tokyo]
println(ZonedDateTime.of(LocalDateTime.of(2022, 12, 25, 0, 0, 0), ZoneId.of("Asia/Tokyo")))
// TransitionRule[Gap +01:00 to +02:00, SUNDAY on or after MARCH 25 at 02:00 STANDARD, standard offset +01:00]
// TransitionRule[Overlap +02:00 to +01:00, SUNDAY on or after OCTOBER 25 at 02:00 STANDARD, standard offset +01:00]
ZoneId.of("CET").rules.transitionRules.forEach { println(it) }
// 2022년 유럽은 3/27~10/30동안 summer time이 적용되었다.
// 2022-01-01T00:00+01:00[CET]
println(ZonedDateTime.of(2022, 1, 1, 0, 0, 0, 0, ZoneId.of("CET")))
// 2022-06-01T00:00+02:00[CET]
println(ZonedDateTime.of(2022, 6, 1, 0, 0, 0, 0, ZoneId.of("CET")))
}
위의 내용들을 한 장의 사진으로 요약하면 아래와 같이 나타낼 수 있다.
Instant
어느 순간을 나타내기 위한 API이며 Unix TimeStamp를 구하기 위해 사용한다.
UTC 기준으로 1970년 1월 1일 0시 0분 0초를 숫자 0으로 정하고 그로부터 경과된 시간을 양수 또는 음수로 표현한다.
Unix Timestamp는 Interger, Long 등등 숫자 자료형을 가지고 연산하기 때문에
연산 속도가 다른 java.time API들에 비해 빠르다.
UTC 기준이기 때문에 글로벌한 서비스에도 적용할 수 있다.
import java.time.Instant
fun main() {
// Instant 객체는 ISO8601 포맷으로 출력된다.
// 2022-11-27T15:44:03.672132Z
println(Instant.now())
// Time stamp 값을 Long 타입으로 얻을 수 있다.
// 1669564118274
println(Instant.now().toEpochMilli())
}
Duration / Period
Duration은 두 '시간' 사이의 간격을 나타내기 위한 API이며, Period는 두 '날짜'사이의 간격을 나타내기 위한 API이다.
import java.time.Duration
import java.time.LocalDate
import java.time.LocalTime
import java.time.Period
fun main() {
val startTime = LocalTime.of(12, 0, 0)
val endTime = LocalTime.of(12, 30, 0)
val duration = Duration.between(startTime, endTime)
println(duration) // PT30M
println("duration as seconds = ${duration.seconds}") // 1800
val startDate = LocalDate.of(2022, 12, 1)
val endDate = LocalDate.of(2025, 6, 15)
val period = Period.between(startDate, endDate)
println(period) // P2Y6M14D
println("year = ${period.years}") // 2
println("month = ${period.months}") // 6
println("day = ${period.days}") // 14
}
참고
- https://www.daleseo.com/java8-duration-period/
- https://perfectacle.github.io/2018/09/26/java8-date-time/
'Kotlin' 카테고리의 다른 글
Coroutine 톺아보기 (0) | 2024.04.27 |
---|---|
OffsetDateTime의 시간 비교 (0) | 2023.06.04 |
Spring JPA Entity에 Data Class를 사용해도 될까? (0) | 2021.10.31 |
Kotlin의 Collections (0) | 2021.10.31 |
Kotlin의 Data/Enum 클래스 (0) | 2021.09.22 |