Java에서 시간 다루기

dev_314·2023년 1월 19일
0

Java - Trial and Error

목록 보기
4/4

Date

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

Calendar

java.util에 속한 클래스
Date와 비슷한 이유로 legacy로 취급 받는다.

java.util.time

참고: What's the difference between Instant and LocalDateTime?

java.util의 시간 관련 클래스들을 개선한 클래스들의 패키지

들어가기전에 UTC

  • Coordinated Universal Time(UTC) aka 협정 세계시
  • 1970년 1월 1일 자정을 0 밀리초로 설정하여 기준을 삼아 그 후로 시간의 흐름을 밀리초로 계산
  • 경도 0°을 기준으로 함
    • 한국은 경도 135° == UTC+9 -> 한국은 UTC보다 9시간 빠르다.

대부분의 비즈니스 로직, 데이터 저장, 데이터 교환은 UTC기준으로 이뤄진다.

Instant


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


OffsetDateTime 는 date와 time으로 moment를 표현한다.
time은 hours-minutes-seconds로 구성되어있고, UTC보다 빠름, 늦음을 표현한다.
offset의 양, 즉 hours-minutes-seconds의 양은 ZoneOffset 클레스로 표현된다.
즉, hours-minutes-seconds의 양이 0이면, OffsetDateTimeInstant와 같은 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


ZoneOffset은 UTC로 부터 몇시나 빠른지, 늦은지를 표현하는 Offset을 의미한다.
즉, ZoneOffset은 단순히 hours-minutes-seconds의 양을 의미한다.

ZoneOffset of = ZoneOffset.of("+08:00"); // 단순히 UTC기준 8시간 빠르다는 정보

ZoneId


time zone(시간대)은 ZoneId 클레스로 표현된다.
time zone은 생각보다 다양한 이유로 빈번히 변경된다. 그러므로 이러한 변화를 계속해서 업데이트 해야하는데, Java 8부터는 Oracle이 발표하는 Timezone Updater Tool을 통해 업데이트 할 수 있다.

time zone

time zone은 지역에서 실행되는 조정, 이상 현상을 처리하기 위한 규칙을 의미한다.

이상현상은 써머 타임(DST)같이 임의로 시간을 조절하는 상황 등이 해당된다.

time zone은 과거, 현재, 미래에 다양한 이유에 따라 변경되고, 변경될 수 있다.

ZonedDateTime

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초 대한민국 표준시

LocalDate, LocalTime, LocalDateTime

LocalDateTime, LocalDate, LocalTime은 모두 다른 것이다.

이들은 어떠한 지역이나 time zone에 묶이지 않는다.
이들은 timeline에도 묶이지 않는다.

local이란 단어때문에 헷갈릴 수 있지만,
이들은 타임라인의 특정 지점을 알아내기 위해 지역을 부여하기 전까지 아무런 실제 의미를 가지지 않는다.

비즈니스 앱들은 보통 사건(메일 수신, 구매 일자, 배송 일자 등)에 대한 실제 (로컬라이징된) 시간을 필요로 하므로 Instant, ZonedDateTime을 사용해야 한다.

그렇다면 언제 Local들을 사용할까?

  1. 여러 지역에 특정 날짜, 시간을 적용할 때
  2. 약속을 잡을 때
  3. 아직 time zone이 결정되지 않았을 때

즉, 단순히 5월 20일이 생일임을 표시할 때처럼, timeline에서 특정 지점, 순간을 포함하지 않을 때 Local을 사용한다.

Local vs ZonedDateTime

상황: 올해 크리스마스는 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]

데이터베이스(SQL)과 Java Time

MySQL Date -> sql.Date -> time.LocalDate

Date date = resultSet.getDate("date"); // java.sql.Date
LocalDate localDate = date.toLocalDate(); // java.time.LocalDate

MySQL DateTime -> sql.Timestamp -> time.LocalDateTime

Timestamp timestamp = rs.getTimestamp("timestamp"); // java.sql.Timestamp
LocalDateTime localDateTime = timestamp.toLocalDateTime(); // java.time.LocalDate

MySQL Time -> sql.Time -> time.LocalTime

Time time = rs.getTime("time"); // java.sql.time
LocalTime localTime = time.toLocalTime(); // java.time.LocalTime
            ```
profile
블로그 이전했습니다 https://dev314.tistory.com/

0개의 댓글