Date/Time API 연습하기

Bruce Han·2023년 2월 12일
0

Java8-정리

목록 보기
12/20
post-thumbnail

이 포스팅의 코드 및 정보들은 강의를 들으며 정리한 내용을 토대로 작성한 것입니다.

기계용 시간 API 활용해보기

Instant

Instant instant = Instant.now();
// 				Instant.of~();

Instant는 now()나 of~()로 만들 수 있다. of~()로 만들면 특정 epoch time기준으로 만드는 것이고, now()가 지금의 시간을 기계 시간으로 찍어준다.


기계 시간으로 출력했음에도 비교적 사용자 친화적으로 시간을 출력해주고 있다.

System.out.println(Instant.now()); // 기준시 UTC, GMT (둘 다 같음)

시간을 출력해주는 기준은 Universal Time Coordinated나 Greenwich Mean Time을 기준으로 한다.
그래서 시스템의 기본값이 가리키는 지역을 기준으로 시간을 출력하려면

이 된다. 지역은 아시아/서울로 출력되고, 시간도 그에 맞게 3시 58분으로 출력되는 것을 확인할 수 있다.

시스템의 기본값이 되는 기준은 다음 문서의 내용과 같다.

systemDefault()
Gets the system default time-zone.
This queries TimeZone.getDefault() to find the default time-zone and converts it to a ZoneId. If the system default time-zone is changed, then the result of this method will also change.

시스템의 기본 시간대를 가져온다.
이는 TimeZone.getDefault()를 기본 시간대를 찾기 위해 쿼리하고, ZoneId로 변환한다. 시스템 기본 시간대가 변경된다면 메서드의 결과도 변경된다.

그 시스템의 기준이 되는 TimeZone.getDefault()의 문서를 찾아보면

TimeZone.getDefault()
Gets the default TimeZone of the Java virtual machine. If the cached default TimeZone is available, its clone is returned. Otherwise, the method takes the following steps to determine the default time zone.
1. Use the user.timezone property value as the default time zone ID if it's available.
2. Detect the platform time zone ID. The source of the platform time zone and ID mapping may vary with implementation.
3. Use GMT as the last resort if the given or detected time zone ID is unknown.
The default TimeZone created from the ID is cached, and its clone is returned. The user.timezone property value is set to the ID upon return.

JVM(Java Virtual Machine)의 기본 TimeZone을 가져온다. 캐시된 기본 TimeZone을 사용할 수 있는 경우, 복제본이 반환된다. 그렇지 않으면 메서드는 기본 시간대를 결정하기 위해 다음 단계를 수행한다.
1. 사용 가능한 경우 user.timezone 속성 값을 기본 시간대 ID로 사용한다.
2. 플랫폼 시간대 ID를 감지한다. 플랫폼 시간대 및 ID 매핑의 소스는 구현에 따라 다를 수 있다.
3. 주어지거나 감지된 시간대 ID를 알 수 없는 경우, GMT를 마지막 수단으로 사용해라.
ID에서 생성된 기본 TimeZone이 캐시되고 복제본이 반환된다. user.timezone 속성 값은 반환 시 ID로 설정된다.

UTC와 GMT가 같은지 보려면?

instant만 출력하면 GMT 기준으로 시간이 표시되고, UTC 기준으로 출력하려고 하면 GMT와 똑같은 시간이 표시된다. [UTC]라고 따로 표시되는 거 말고는 차이가 없다.

기계용 API인 Instant는

시간을 재거나, 메서드 실행 시간을 비교할 때 주로 사용된다.

사람용 시간 API 활용해보기

LocalDateTime.now()

LocalDateTime은 Local이 붙어있기 때문에, 시스템의 Zone 정보를 참고해서 로컬 시간을 가져와보면 16시가 나온다.
그런데 이 말은 코드가 동작하고 있는 Zone에 따라 달라지므로, 한국에서 코딩하고 미국에서 배포를 하면 미국 기준으로 시간이 출력된다는 것이다.
LocalDateTime은 서버의 시스템 기본 Zone 정보를 참고해서, 그 시간대가 쓰이는 것이다.

LocalDateTime.of()

특정한 시간대에 해당하는 시간을 만들 수 있다.

LocalDateTime 팁
ChronoUnit은 LocalDateTime의 now()에서드에서 두 번째 파라미터로 유용하게 쓰인다.

또한, 이 지금시간(now)에 plus한 것을 LocalDateTime의 다른 객체를 만들어서 넣어야지 새 인스턴스가 만들어지게 된다. 객체를 생성하지 않으면 아무 일도 일어나지 않게 된다.

ZonedDateTime

특정 Zone의 현재 시간을 보고 싶을 때 사용한다.

근데 Instant로도 똑같은 시간을 출력할 수 있다

위에서 이미 출력해봤던 Instant로도 atZone()을 통해서 똑같은 시간과 포맷으로 출력할 수 있다.
ZonedDateTime과 Instant는 서로 변환이 가능하다. 즉, ZonedDateTime은 Instant로, Instant는 ZonedDateTime으로 변경할 수 있다.

기간을 표현하는 방법

Period

Period는 주로 날짜를 가지고 비교하며, 파라미터로 LocalDate 타입 2개를 받는다.

주로 사람용으로 쓰인다

현재 시간으로부터 다음 해 설날까지 얼마나 남았는지 출력해봤다.

남은 일수로 계산해주면 좋을 텐데 그거는 더 알아봐야 할 것 같고, 23년 2월 12일 기준으로 11개월 29일 정도 남았다고 나와있다.

Period로는 각 날짜 사이를 비교해서 얼마나 남았는지 알고 싶을 때 활용할 수 있을 것 같다.

until()을 이용하여 get()에다가 ChronoUnit을 쓰면 위 사진과 같이 between()을 썼을 때랑 같은 결과 값을 구할 수 있는 것을 확인할 수 있다.

Duration

Duration도 Period와 비슷하게 날짜를 가지고 비교하지만, 기계용으로 쓰인다는 것이 Period와의 차이점이 되겠다.

Duration의 between()은 파라미터로 Temporal 타입을 받는다.

Instant 타입으로 현재 시간과 20초 추가한 시간을 각각 구해서 둘의 차이를 출력하면 20이 출력된다.

Parsing 또는 Formatting

Formatting

LocalDateTime으로 현재 시간을 출력하면 사람이 그래도 식별 가능한 형식으로 시간이 출력된다.

여기서 흔히 "23/02/12" 이런 식으로 출력되도록, 더 편하게 날짜를 볼 수 있게 포맷팅을 할 수 있다.

DateTimeFormatter

DateTimeFormatter 클래스를 이용해서 ofPattern()으로 월/일/년도의 패턴을 만든 다음, LocalDateTime의 now()를 통해서 format()의 파라미터에 작성한 패턴을 넣으면 '02/12/2023'과 같이 날짜를 더 쉽게 식별할 수 있게 된다. 즉, LocalDateTime 타입을 문자열로 변환한 것이다.

https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html#predefined

일일이 커스터마이징을 하기보다는 위의 공식문서의 링크를 통해서 다양한 포맷을 참고하는 것도 괜찮은 방법이 될 수 있다.

Parsing

LocalDate의 parse() 파라미터에 파싱할 값과 formatter를 넣는다.
그 반환 값으로 얻은 LocalDate 타입의 객체를 출력하면 "2023-02-12"가 출력된다.

레거시 API 지원

Date

LocalDateTime, DateTimeFormatter, LocalDate같은 Java 8 이후의 API 뿐만 아니라, 예전 API와도 호환이 된다.

// Date -> Instant로 변환
Date date = new Date();
Instant instant = date.toInstant();

// Instant -> Date로 변환
Date newDate = Date.from(instant);

예를 들어, Date의 레거시 API에서 Instant의 새 API로 변환할 수 있고, 그의 역도 가능하다.

GregorianCalendar

GregorianCalendar 클래스도 마찬가지로 새 API로 변환할 수 있다.

// GregorianCalendar -> LocalDateTime
GregorianCalendar gregorianCalendar = new GregorianCalendar();
        LocalDateTime dateTime = gregorianCalendar.toZonedDateTime().toLocalDateTime();

GregorianCalendar에는 Zone 정보가 들어있기 때문에 ZonedDateTime으로 쉽게 바꿀 수 있다.

ZonedDateTime 말고도 toInstant를 통해서 Instant 타입으로 바꾸기만 하면 Java 8 이후의 API로 얼마든지 변경할 수 있다.

  • Instant -> ZonedDateTime
  • Instant -> LocalDateTime으로 바꿀 수 있다.

TimeZone

java.util에 있는 TimeZone도 Java 8 이후에 생긴 ZoneId로 변환할 수 있다.

ZoneId도 getTimeZone()에 파라미터로 ZoneId에 해당하는 객체를 넣으면 TimeZone 타입의 객체로 변환할 수 있다.

정리

  • 지금 이 순간을 기계용 시간으로 표현하는 방법
    • Instant.now() : 현재 UTC(GMT)를 리턴한다.
    • Universal Time Coordinated == Greenwich Mean Time
Instant now = Instant.now();
System.out.println(now);
System.out.println(now.atZone(ZoneId.of("UTC")));

ZonedDateTime zonedDateTime = now.atZone(ZoneId.systemDefault());
System.out.println(zonedDateTime);
  • 지금 이 순간을 사람용 일시를 표현하는 방법
    • LocalDateTime.now() : 현재 시스템 Zone에 해당하는 (local) 일시 반환한다.
    • LocalDateTime.of(int, Month, int, int, int, int) : local의 특정 일시를 반환한다.
    • ZonedDateTime.of(int, Month, int, int, int, int, ZoneId) : 특정 Zone의 특정 일시를 반환한다.
  • 기간을 표현하는 방법
    • Period.between()
    • Duration.between()
Period between = Period.between(today, newYearsDay);
System.out.println(between.get(ChronoUnit.DAYS));
  • parsing 또는 formatting
    • 미리 정의해둔 format 참고
    • LocalDateTime.parse(String, DateTimeFormatter);
    • DateTime
DateTimeFormatter formatter =
	DateTimeFormatter.ofPattern("MM/dd/yyyy");
LocalDate date = LocalDate.parse("02/12/2023", formatter);
System.out.println(date);
System.out.println(today.format(formatter));
  • 레거시 API 지원
    • GregorianCalendar와 Date 타입의 인스턴스를 Instant나 ZonedDateTime으로 변환 가능
    • java.util.TimeZone에서 java.time.ZoneId로 상호 변환 가능
ZoneId newZoneAPI = TimeZone.getTimeZone("PST").toZoneId();
TimeZone legacyZoneAPI = TimeZone.getTimeZone(newZoneAPI);

Instant newInstant = new Date().toInstant();
Date legacyInstant = Date.from(newInstant);

Reference

profile
만 가지 발차기를 한 번씩 연습하는 사람은 두렵지 않다. 내가 두려워 하는 사람은 한 가지 발차기를 만 번씩 연습하는 사람이다. - Bruce Lee

0개의 댓글