[Kotlin] 날짜와 시간 클래스

Hood·2025년 3월 30일

Kotlin

목록 보기
15/18
post-thumbnail

✍ 코틀린과 친해지자

공부가 필요하다고 느낀 문법을 정리한 글입니다.


들어가기 전

현재 A&I 동아리 레포트 관리 프로젝트를 진행하면서, 레포트 마감 시간이 지나면 레포트가 보이지 않도록 처리하는 API를 구현하고 있었습니다.
그런데 데이터베이스의 시간이 UTC 기준으로 저장되어 있어, 이를 서울 시간에 맞게 올바르게 변환하는 작업이 필요했습니다.

이 과정에서 날짜와 시간을 어떤 클래스로 다뤄야 하는지 헷갈리는 부분이 있어,
이번 글에서는 Kotlin에서 자주 사용하는 날짜·시간 관련 클래스들을 정리해보려고 합니다.


Java API

Kotlin은 JVM 위에서 동작하기 때문에, Kotlin/JVM 환경에서는 Java의 날짜·시간 API인 java.time 패키지를 그대로 사용할 수 있습니다.
이 패키지는 날짜, 시간, 타임존, 포맷팅 같은 기능을 체계적으로 제공하며, 현재 Java 진영에서 날짜·시간을 다룰 때 표준처럼 사용됩니다. (Kotlin)


날짜와 시간을 함께 다루는 클래스

1. LocalDateTime

LocalDateTime은 날짜와 시간을 함께 표현하는 클래스입니다.
다만 시간대(Time Zone)나 UTC 오프셋 정보는 포함하지 않습니다.
즉, “2025년 3월 30일 15시 30분”처럼 로컬 기준의 날짜와 시간만 표현할 때 적합합니다. (Oracle Docs)

import java.time.LocalDateTime

val localDateTime = LocalDateTime.now()
println(localDateTime)
// 예: 2025-03-30T15:30:45

LocalDateTime은 날짜와 시간을 모두 담고 있지만,
“이 시간이 서울 시간인지, UTC인지” 같은 정보는 포함하지 않는다는 점을 기억해야 합니다. (Oracle Docs)


2. ZonedDateTime

ZonedDateTimeLocalDateTime시간대 정보까지 포함한 클래스입니다.
예를 들어 Asia/Seoul 같은 타임존을 함께 가지므로,
세계 여러 지역의 시간을 정확하게 다룰 때 유용합니다. (Oracle Docs)

import java.time.ZonedDateTime
import java.time.ZoneId

val zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul"))
println(zonedDateTime)
// 예: 2025-03-30T15:30:45+09:00[Asia/Seoul]

사용자에게 보여줄 시간처럼 “어느 지역 기준 시간인지”가 중요한 경우에는
ZonedDateTime이 더 적합합니다. (Oracle Docs)


3. Instant

InstantUTC 기준의 한 순간을 나타내는 클래스입니다.
타임존이 붙은 지역 시간이라기보다, 전 세계에서 공통으로 해석할 수 있는 절대 시점을 표현할 때 사용합니다. (Oracle Docs)

import java.time.Instant

val instant = Instant.now()
println(instant)
// 예: 2025-03-30T06:30:45.123Z

데이터베이스 저장 시간이나 서버 내부 기준 시간처럼
“지역 시간”보다 “정확한 시점”이 중요한 경우에는 Instant가 잘 어울립니다.
그리고 필요할 때만 ZonedDateTime으로 변환해 보여주면 됩니다. 이는 Instant가 절대 시점을 표현하고, atZone(ZoneId)로 타임존을 붙여 지역 시간으로 바꿀 수 있다는 점에서 자연스럽게 이어지는 사용 방식입니다. (Oracle Docs)


날짜 및 시간 클래스 활용

1. Clock

Clock은 현재 시각을 얻기 위한 기준 객체입니다.
Oracle 문서에서도 Clock은 현재 instant와 날짜·시간에 접근하기 위한 클래스라고 설명하며, systemUTC()systemDefaultZone() 같은 팩토리 메서드를 제공합니다. 테스트에서 시간을 고정하거나 주입할 때도 자주 사용됩니다. (Oracle Docs)

import java.time.Clock
import java.time.Instant

val clock = Clock.systemUTC()
val instant = Instant.now(clock)

println(instant)
// 예: 2025-03-30T06:30:45Z

시간 관련 로직을 테스트해야 한다면 Clock을 직접 주입하는 방식이 유용합니다.
그러면 now()를 코드 곳곳에서 직접 호출하는 것보다 테스트 가능한 구조를 만들기 쉬워집니다. (Oracle Docs)


2. LocalDate

LocalDate시간 정보 없이 날짜만 표현하는 클래스입니다.
예를 들어 생일, 마감일, 출석일처럼 날짜만 중요할 때 사용하기 좋습니다.
이 클래스는 시간이나 타임존을 저장하지 않습니다. (Oracle Docs)

import java.time.LocalDate

val today = LocalDate.now()
println(today)
// 예: 2025-03-30

val tomorrow = today.plusDays(1)
val yesterday = today.minusDays(1)
val nextMonth = today.plusMonths(1)

3. LocalTime

LocalTime은 반대로 날짜 없이 시간만 표현하는 클래스입니다.
예를 들어 “오후 3시 회의”, “매일 23시 마감”처럼 시간만 중요할 때 사용합니다.
이 클래스도 날짜나 타임존은 포함하지 않습니다. (Oracle Docs)

import java.time.LocalTime

val now = LocalTime.now()
println(now)
// 예: 15:30:45.123

val later = now.plusHours(2)
val earlier = now.minusMinutes(30)

4. DateTimeFormatter

DateTimeFormatter는 날짜·시간을 문자열로 출력하거나, 문자열을 날짜·시간으로 파싱할 때 사용하는 클래스입니다.
공식 문서에서도 이 클래스는 날짜·시간을 출력하고 파싱하는 주요 진입점이라고 설명하며, ofPattern()으로 원하는 패턴의 포매터를 만들 수 있다고 안내합니다. 또한 포매터는 immutable하고 thread-safe합니다. (Oracle Docs)

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

val now = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")

val formatted = now.format(formatter)
println(formatted)
// 예: 2025/03/30 15:30:45

자주 쓰는 패턴

패턴의미
yyyy4자리 연도
MM2자리 월
dd2자리 일
HH24시간제 시간
mm
ss
SSS밀리초
aAM / PM
EEE짧은 요일
EEEE긴 요일
z시간대 이름
Z+0900 같은 오프셋
XISO-8601 오프셋 (+09 등)

포맷 패턴은 ofPattern()에 넣어 사용하며,
출력 형식뿐 아니라 문자열 파싱에도 같은 개념이 적용됩니다. (Oracle Docs)


5. 시간 비교

날짜와 시간은 isBefore()isAfter() 같은 메서드로 비교할 수 있습니다.
즉, 어떤 날짜가 다른 날짜보다 이전인지, 이후인지를 코드로 명확하게 표현할 수 있습니다. (Oracle Docs)

import java.time.LocalDate

val date1 = LocalDate.of(2025, 3, 30)
val date2 = LocalDate.of(2099, 3, 30)

println(date1.isBefore(date2)) // true
println(date1.isAfter(date2))  // false

UTC 시간을 서울 시간으로 바꾸려면?

이번 글의 시작처럼, 데이터베이스에 시간이 UTC 기준으로 저장되어 있다면
보통은 그 값을 Instant로 다루고, 사용자에게 보여주기 직전에 Asia/Seoul 타임존을 적용하는 방식이 가장 이해하기 쉽습니다. Instant는 절대 시점을 나타내고, atZone(ZoneId)를 통해 해당 시점을 원하는 지역 시간으로 해석할 수 있습니다. (Oracle Docs)

import java.time.Instant
import java.time.ZoneId

val utcTime = Instant.parse("2025-03-30T06:30:45Z")
val seoulTime = utcTime.atZone(ZoneId.of("Asia/Seoul"))

println(seoulTime)
// 예: 2025-03-30T15:30:45+09:00[Asia/Seoul]

즉, 저장은 UTC 기준으로 하고,
응답이나 화면 출력 시점에만 서울 시간으로 변환하는 방식이 실무에서 자주 쓰이는 흐름입니다. 이는 절대 시점과 지역 시간을 분리해 다루는 java.time API 구조와도 잘 맞습니다. (Oracle Docs)


📌 결론

이번 글에서는 Kotlin/JVM에서 사용하는 날짜·시간 클래스들을 정리해보았습니다.

핵심은 비슷해 보이는 클래스라도 역할이 다르다는 점입니다.

  • LocalDateTime : 날짜와 시간은 있지만 타임존은 없음
  • ZonedDateTime : 날짜, 시간, 타임존까지 포함
  • Instant : UTC 기준의 절대 시점
  • LocalDate : 날짜만
  • LocalTime : 시간만
  • DateTimeFormatter : 날짜·시간 포맷팅과 파싱

이번처럼 데이터베이스 시간이 UTC로 저장되어 있다면,
무조건 문자열만 바꾸기보다 어떤 클래스가 “절대 시점”을 나타내는지,
어떤 클래스가 “지역 시간”을 나타내는지 구분해서 사용하는 것이 중요합니다.

요구사항에 맞는 클래스를 선택하면 날짜와 시간 처리도 훨씬 덜 헷갈리게 작성할 수 있습니다.

참고

  • Oracle Java 공식 문서의 java.time 패키지와 LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Clock, DateTimeFormatter 설명을 참고했습니다. (Oracle Docs)
profile
달을 향해 쏴라, 빗나가도 별이 될 테니 👊

0개의 댓글