자바 (Java)

자바, SQL 날짜/시간 정리

Wibaek 2025. 4. 6. 20:57
728x90

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**로 초기화되거나 업데이트되는 특성을 가질 수 있습니다.

References

Java 타임존, 날짜 그리고 시간객체 뽀개기

 

Java 타임존, 날짜 그리고 시간객체 뽀개기

항상 헷갈리는 타임존과 관련된 용어를 정리고, 데이터베이스에 어떻게 저장되고, 어플리케이션에선 어떻게 보여지는지 알아봅니다.

medium.com

 

When should I use OffsetDateTime, ZonedDateTime and Instant in database applications?

 

When should I use OffsetDateTime, ZonedDateTime and Instant in database applications?

The documentation of the java.time package says: Many applications can be written only using LocalDate, LocalTime and Instant, with the time-zone added at the user interface (UI) layer. The offset...

stackoverflow.com

 

728x90