TL;DR자바의 시간로컬 시간java.util.Datejava.sql.TimeStampjava.time.LocalDatejava.time.LocalTimejava.time.LocalDateTimeTimezone-aware을 다루기 전에ZoneIdZoneOffsetTimezone-awarejava.time.Instantjava.time.OffsetTimejava.time.OffsetDateTimejava.time.ZoneDateTimeSQLDATEDATETIMETIMESTAMPReferences
TL;DR
- Java
- Date는 immutable하지 않고, 레거시
- LocalDate = 날짜, LocalTime = 시간, LocalDateTime = LocalDate + LocalTime
- ZoneId: 지역 정보(Asia/Seoul), ZoneOffset: 시간대 정보(+09:00)
- OffsetDateTime = LocalDateTime + ZoneOffset 로 시간대 정보가 포함됨
- ZoneDateTime = LocalDateTime + ZoneOffset + ZoneId 로 지역 정보가 포함되어 써머타임등 관리 가능
- Instant: 1970-01-01T00:00:00Z 를 기준으로 지난 시간을 측정. OffsetDateTime과 비슷하다
- SQL
- DATE: 날짜만 저장하며 시간 정보는 포함되지 않습니다.
- DATETIME: 날짜와 시간을 저장하며 시간대 정보를 포함하지 않습니다.
- TIMESTAMP: 날짜와 시간을 저장하며, UTC 기반으로 저장되고 타임존 변환을 지원합니다.
자바의 시간
로컬 시간
java.util.Date
- 레거시
java.sql.TimeStamp
- 레거시
java.time.LocalDate
java.time.LocalTime
java.time.LocalDateTime
- LocalDate + LocalTime
Timezone-aware을 다루기 전에
ZoneId
val timeZone = ZoneId.of("America/Guatemala")
ZoneOffset
val zoneOffset = ZoneOffset.of("-06:00")
Timezone-aware
java.time.Instant
- 유닉스 타임스탬프 시작 시간인 1970-01-01T00:00:00Z 를 기준으로 지난 시간을 저장한다
private Instant(long epochSecond, int nanos) { this.seconds = epochSecond; // 1970-01-01T00:00:00Z 이후 지난 시간(초) this.nanos = nanos; // 소수점 시간, 음수일 수 없다 }
java.time.OffsetTime
- LocalTime + ZoneOffset
java.time.OffsetDateTime
- LocalDateTime + ZoneOffset
- LocalDateTime에 시간대(-12h ~ UTC ~ +12h)를 포함한 것
- Instant와 사용 측면에서 유사
java.time.ZoneDateTime
- LocalDateTime + ZoneOffset + ZoneId
- OffsetDateTime과 달리 ZoneId가 들어간다
- 즉, +09:00 만이 아닌, Asia/Seoul 정보도 저장한다
- DST(Daylight Saving Time)와 같은 써머타임의 정보도 들어가 있는 것
- Instant와 달리 시간대(ZoneId) 정보를 포함함
ZoneDateTime 생성자, of 구조
private ZonedDateTime(LocalDateTime dateTime, ZoneOffset offset, ZoneId zone) { this.dateTime = dateTime; this.offset = offset; this.zone = zone; } public static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone) { return ofLocal(localDateTime, zone, null); } public static ZonedDateTime ofLocal(LocalDateTime localDateTime, ZoneId zone, ZoneOffset preferredOffset) { if (zone instanceof ZoneOffset) { return new ZonedDateTime(localDateTime, (ZoneOffset) zone, zone); } ZoneRules rules = zone.getRules(); List<ZoneOffset> validOffsets = rules.getValidOffsets(localDateTime); ZoneOffset offset; if (validOffsets.size() == 1) { offset = validOffsets.get(0); } else if (validOffsets.size() == 0) { ZoneOffsetTransition trans = rules.getTransition(localDateTime); localDateTime = localDateTime.plusSeconds(trans.getDuration().getSeconds()); offset = trans.getOffsetAfter(); } else { if (preferredOffset != null && validOffsets.contains(preferredOffset)) { offset = preferredOffset; } else { offset = Objects.requireNonNull(validOffsets.get(0), "offset"); // protect against bad ZoneRules } } return new ZonedDateTime(localDateTime, offset, zone); }
ZoneDateTime.now() 호출 구조
public static ZonedDateTime now() { return now(Clock.systemDefaultZone()); } public static ZonedDateTime now(ZoneId zone) { return now(Clock.system(zone)); } public static ZonedDateTime now(Clock clock) { Objects.requireNonNull(clock, "clock"); final Instant now = clock.instant(); // called once return ofInstant(now, clock.getZone()); }
SQL
DATE
- 형식:
YYYY-MM-DD
- 범위: 1000-01-01 ~ 9999-12-31
날짜만 저장할 때 사용합니다. 시간 정보는 포함되지 않습니다.
DATETIME
- 형식:
YYYY-MM-DD HH:MM:SS
- 범위: 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59
날짜와 시간을 함께 저장할 때 사용합니다. 시간대 정보는 포함되지 않습니다.
TIMESTAMP
- 형식:
YYYY-MM-DD HH:MM:SS
- 범위: 1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07 UTC (범위는 MySQL 기준으로 다를 수 있습니다)
저장 시 서버의 타임존 설정에 따라 UTC로 변환되어 저장되고, 읽을 때는 다시 로컬 타임존으로 변환됩니다.
자동으로
CURRENT_TIMESTAMP
로 초기화되거나 업데이트되는 특성을 가질 수 있습니다.