java.util
에 속한 클래스
애매한 사용법, 멀티스레드 정상 지원 X, 실행 환경에 따라 다르게 작동 하는 이유등으로 사실상 deprecated
// 현재 시간 정보를 담고 있는 Date 객체
Date date = new Date();
// 현재 시간이 Unix Time 기준 몇 '밀리초'가 흘렀는가
long time = date.getTime();
// Unix Time(aka 'Epoch time') 기준 1000ms(1초) 뒤로 설정
date.setTime(1000); // Thu Jan 01 09:00:01 KST 1970
// 현재 시간 보다 오래됐는가(after), 이른가(before)
date.after(new Date()); // false
date.before(new Date()); // true
java.util
에 속한 클래스
Date
와 비슷한 이유로 legacy로 취급 받는다.
참고: What's the difference between Instant and LocalDateTime?
java.util
의 시간 관련 클래스들을 개선한 클래스들의 패키지
대부분의 비즈니스 로직, 데이터 저장, 데이터 교환은 UTC기준으로 이뤄진다.
Instant
는 UTC로 표현한 타임라인의 특정 순간(moment)에 대한 nano초 단위의 정보를 가진다.
// 현재 시간
Instant now = Instant.now();
// epoch time (unix time)기준 s 초가 흐른 시간
Instant instant = Instant.ofEpochSecond(1);
// epoch time (unix time)기준 s '밀리'초가 흐른 시간
Instant instant1 = Instant.ofEpochMilli(1000);
// unix 시간 기준 몇 초가 흘렀는가
long epochSecond = now.getEpochSecond();
OffsetDateTime
는 date와 time으로 moment를 표현한다.
time은 hours-minutes-seconds로 구성되어있고, UTC보다 빠름, 늦음을 표현한다.
offset
의 양, 즉 hours-minutes-seconds의 양은 ZoneOffset
클레스로 표현된다.
즉, hours-minutes-seconds의 양이 0이면, OffsetDateTime
은 Instant
와 같은 moment를 의미한다.
// zone id를 입력하지 않으면 알아서 실행 환경에 맞춰 time zone을 설정하는 것 같다.
OffsetDateTime now = OffsetDateTime.now(); // 2023-01-23T22:50:20.947496+09:00
OffsetDateTime SaoPaulo = OffsetDateTime.now(ZoneId.of("America/Sao_Paulo")); // 2023-01-23T11:49:41.184568-03:00
ZoneOffset offset = now.getOffset(); // +09:00
int totalSeconds = offset.getTotalSeconds(); // 32400 = 9시간을 '초'로 환산
ZoneOffset
은 UTC로 부터 몇시나 빠른지, 늦은지를 표현하는 Offset을 의미한다.
즉, ZoneOffset
은 단순히 hours-minutes-seconds의 양을 의미한다.
ZoneOffset of = ZoneOffset.of("+08:00"); // 단순히 UTC기준 8시간 빠르다는 정보
time zone(시간대)은 ZoneId
클레스로 표현된다.
time zone은 생각보다 다양한 이유로 빈번히 변경된다. 그러므로 이러한 변화를 계속해서 업데이트 해야하는데, Java 8부터는 Oracle이 발표하는 Timezone Updater Tool을 통해 업데이트 할 수 있다.
time zone은 지역에서 실행되는 조정, 이상 현상을 처리하기 위한 규칙을 의미한다.
이상현상은 써머 타임(DST)같이 임의로 시간을 조절하는 상황 등이 해당된다.
time zone은 과거, 현재, 미래에 다양한 이유에 따라 변경되고, 변경될 수 있다.
ZonedDateTime
은 한마디로 ZoneId + Instant
이다.
즉, 특정 지역에 사는 사람이 현재 자신이 보고 있는 시간이 곳 ZonedDateTime이다.
ZoneId seoulZoneId = ZoneId.of("Asia/Seoul");
ZonedDateTime seoulTime = ZonedDateTime.now(seoulZoneId); // 실행 순간의 아시아-서울의 시간
// 존재하지 않는 곳의 시간에 접근하면 예외가 발생한다.
ZoneId noZone = ZoneId.of("Asia/Seoul");
ZonedDateTime noTime = ZonedDateTime.now(noZone); // 예외 발생
거의 모든 시간 작업은 UTC기준으로 이뤄져야 한다. 그러나 유저에게는 자신이 위치한 지역의 시간으로 보여져야 한다.
이런 목적을 위해 ZonedDateTime
을 사용하는 것이다.
ZoneId seoulZoneId = ZoneId.of("Asia/Seoul");
ZonedDateTime seoulTime = ZonedDateTime.now(seoulZoneId);
String seoulNow = seoulTime.toString(); // 2023-01-23T23:14:54.738995+09:00[Asia/Seoul]
DateTimeFormmater
를 통해 기가막히게 로컬라이징 할 수 있다.
ZonedDateTime seoulTime = ZonedDateTime.now(seoulZoneId);
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(Locale.KOREA);
String outputFormatted = seoulTime.format(f); // 2023년 1월 23일 월요일 오후 11시 16분 9초 대한민국 표준시
LocalDateTime
, LocalDate
, LocalTime
은 모두 다른 것이다.
이들은 어떠한 지역이나 time zone에 묶이지 않는다.
이들은 timeline에도 묶이지 않는다.
local이란 단어때문에 헷갈릴 수 있지만,
이들은 타임라인의 특정 지점을 알아내기 위해 지역을 부여하기 전
까지 아무런 실제 의미를 가지지 않는다.
비즈니스 앱들은 보통 사건(메일 수신, 구매 일자, 배송 일자 등)에 대한 실제 (로컬라이징된) 시간을 필요로 하므로 Instant
, ZonedDateTime
을 사용해야 한다.
그렇다면 언제 Local
들을 사용할까?
즉, 단순히 5월 20일이 생일임을 표시할 때
처럼, timeline에서 특정 지점, 순간을 포함하지 않을 때 Local을 사용한다.
상황: 올해 크리스마스는 2023년 12월 25일임을 표현하고 싶다.
크리스마스는 어디에서나 12월 25일이다. (아마도...?)
즉, 단순히 크리스마스가 2023-12-25일이다는 사실은 Local
로 표현하는게 적절하다.
LocalDate ld = LocalDate.of( 2023 , Month.DECEMBER , 25 ) ;
LocalTime lt = LocalTime.MIN ; // 00:00:00
LocalDateTime ldt = LocalDateTime.of( ld , lt ) ; // 크리스마스는 어디에서나 2023-12-25 00:00:00시에 시작한다.
// 또는 y:m:d H:M로 표현
LocalDateTime christmas = LocalDateTime.of(2023, Month.DECEMBER, 25, 0, 0); // 2023-12-25T00:00
상황: 한국에 있는 치과에(한국 시간으로) 2023-01-23 오후 3:00에 예약을 하고 싶다.
한국에 있는 치과는 한국 시간으로 예약해야 한다. (나는 보통 그렇다)
즉, 특정 time zone에 국한되어 있으므로 ZonedDateTime
으로 표현하는게 적절하다.
ZonedDateTime of = ZonedDateTime.of(
LocalDate.of(2023, Month.JANUARY, 23),
LocalTime.of(15, 0, 0),
ZoneId.of("Asia/Seoul")
); // 2023-01-23T15:00+09:00[Asia/Seoul]
Date date = resultSet.getDate("date"); // java.sql.Date
LocalDate localDate = date.toLocalDate(); // java.time.LocalDate
Timestamp timestamp = rs.getTimestamp("timestamp"); // java.sql.Timestamp
LocalDateTime localDateTime = timestamp.toLocalDateTime(); // java.time.LocalDate
Time time = rs.getTime("time"); // java.sql.time
LocalTime localTime = time.toLocalTime(); // java.time.LocalTime
```