generated from cotes2020/chirpy-starter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
post: [11주차_노경민] chap12. 새로운 날짜와 시간 API
- Loading branch information
Showing
1 changed file
with
330 additions
and
0 deletions.
There are no files selected for viewing
330 changes: 330 additions & 0 deletions
330
...-java-in-action/chap12.-새로운-날짜와-시간-API/2023-12-31-노경민-chap12.-새로운-날짜와-시간-API.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,330 @@ | ||
--- | ||
title: "🦊 chap12. 새로운 날짜와 시간 API" | ||
author: gengminy | ||
date: 2023-12-31 18:40:00 +09:00 | ||
categories: [모던 자바 인 액션, "chap12. 새로운 날짜와 시간 API"] | ||
tags: [모던 자바 인 액션, JAVA, 11주차, 노경민] | ||
render_with_liquid: false | ||
math: true | ||
--- | ||
|
||
# chap12. 새로운 날짜와 시간 API | ||
|
||
# 12.1 LocalDate, LocalTime, Instant, Duration, Period 클래스 | ||
|
||
### 12.1.1 LocalDate와 LocalTime 사용 | ||
|
||
LocalDate 인스턴스는 시간을 제외한 날짜를 표현하는 불변 객체다. | ||
|
||
```java | ||
LocalDate date = LocalDate.of(2017, 9, 21); | ||
int year = date.getYear(); | ||
Month month = date.getMonth(); | ||
int day = date.getDayOfMonth(); | ||
DayOfWeek dow = date.getDayOfWeek(); | ||
int len = date.lengthOfMonth(); | ||
boolean leap = date.isLeapYear(); | ||
``` | ||
|
||
팩토리 메서드 now 는 시스템 시계의 정보를 이용해서 현재 날짜 정보를 얻는다. | ||
|
||
```java | ||
LocalDate today = LocalDate.now(); | ||
``` | ||
|
||
get 메서드에 TemporalField 인터페이스를 전달해서 정보를 얻는 방법도 있다. | ||
|
||
열거자 ChronoField 는 TemporalField 인터페이스를 정의한다. | ||
|
||
```java | ||
int year = date.get(ChronoField.YEAR); | ||
int month = date.get(ChronoField.MONTH_OF_YEAR); | ||
int day = date.get(ChronoField.DAY_OF_MONTH); | ||
``` | ||
|
||
내장 메서드를 이용해 가독성을 높일 수도 있다. | ||
|
||
```java | ||
int year = date.getYear(); | ||
int month = date.getMonthValue(); | ||
int day = date.getDayOfMonth(); | ||
``` | ||
|
||
시간을 LocalTime 클래스로 표현할 수도 있다. | ||
|
||
```java | ||
LocalTime time = LocalTime.of(13, 45, 20); | ||
int hour = time.getHour(); | ||
int minute = time.getMinute(); | ||
int second = time.getSecond(); | ||
``` | ||
|
||
날짜와 시간 문자열로 parse 정적 메서드를 호출해 인스턴스를 만들 수도 있다. | ||
|
||
```java | ||
LocalDate date = LocalDate.parse("2017-09-21"); | ||
LocalTime time = LocalTime.parse("13:45:20"); | ||
``` | ||
|
||
parse 메서드에는 DateTimeFormatter 를 전달할 수도 있다. | ||
|
||
### 12.1.2 날짜와 시간 조합 | ||
|
||
LocalDateTime 은 LocalDate 와 LocalTime 을 쌍으로 갖는 복합 클래스이다. | ||
|
||
```java | ||
LocalDateTime dt1 = LocalDateTime.of(2017, Month.SEPTEMBER, 21, 13, 45, 20); | ||
LocalDateTime dt2 = LocalDateTime.of(date, time); | ||
LocalDateTime dt3 = date.atTime(13, 45, 20); | ||
LocalDateTime dt4 = date.atTime(time); | ||
LocalDateTime dt5 = time.atDate(date); | ||
``` | ||
|
||
LocalDateTime 에서 각 인스턴스를 추출할 수도 있다. | ||
|
||
```java | ||
LocalDate date1 = dt1.toLocalDate(); | ||
LocalTime time1 = dt1.toLocalTime(); | ||
``` | ||
|
||
### 12.1.3 Instant 클래스 : 기계의 날짜와 시간 | ||
|
||
기계의 관점에서는 연속된 시간에서 특정 지점을 하나의 큰 수로 표현하는 것이 가장 자연스러운 시간 표현 방식이다. | ||
|
||
`java.time.Instant` 클래스에서는 기계적인 관점에서 시간을 표현한다. | ||
|
||
이는 유닉스 에포크 시간 `Unix epoch time, 1970-01-01 00:00:00 UTC`을 기준으로 특정 시점까지의 시간을 초로 표현한다. | ||
|
||
```java | ||
Instant.ofEpochSecond(3); | ||
Instant.ofEpochSecond(3, 0); | ||
Instant.ofEpochSecond(2, 1_000_000_000); | ||
Instant.ofEpochSecond(4, -1_000_000_000); | ||
``` | ||
|
||
Instant 클래스도 사람이 확인할 수 있도록 시간을 표시해주는 정적 팩토리 메서드 now 를 제공한다. | ||
|
||
Instant 는 초와 나노초 정보를 포함한다. | ||
|
||
이를 읽기 위해 Duration 과 Period 클래스를 활용할 수 있다. | ||
|
||
### 12.1.4 Duration 과 Period 정의 | ||
|
||
지금까지의 모든 클래스는 Temporal 인터페이스를 구현한다. | ||
|
||
Duration 클래스의 정적 팩토리 메서드 between 으로 두 시간 객체 사이의 지속시간을 만들 수 있다. | ||
|
||
```java | ||
Duration d1 = Duration.between(time1, time2); | ||
Duration d2 = Duration.between(dateTime1, dateTime2); | ||
Duration d3 = Duration.between(instant1, instant2); | ||
``` | ||
|
||
다만 LocalDateTime 은 사람이 사용하도록, Instant 는 기계가 사용하도록 만들어진 클래스로 | ||
|
||
두 인스턴스는 서로 혼합할 수 없다. | ||
|
||
년, 월 일로 시간을 표현할 때는 Period 클래스를 사용한다. | ||
|
||
```java | ||
Period tenDays = Period.between(LocalDate.of(2017, 9, 11), | ||
LocalDate.of(2017, 9, 21)); | ||
``` | ||
|
||
Duration 과 Period 클래스는 자신의 인스턴스를 만들 수 있도록 다양한 팩토리 메서드를 제공한다. | ||
|
||
```java | ||
Duration threeMinutes = Duration.ofMinutes(3); | ||
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES); | ||
|
||
Period tenDays = Period.ofDays(10); | ||
Period threeWeeks = Period.ofWeeks(3); | ||
Period twoYearsSixMonthsOnday = Period.of(2, 6, 1); | ||
``` | ||
|
||
지금까지의 모든 클래스는 불변 클래스이다. | ||
|
||
# 12.2 날짜 조정, 파싱, 포매팅 | ||
|
||
`withArrtibute` 메서드로 기존의 LocalDate 를 바꾼 버전을 직접 간단하게 만들 수 있다. | ||
|
||
```java | ||
LocalDate date1 = LocalDate.of(2017, 9, 21); | ||
LocalDate date2 = date1.withYear(2011); | ||
LocalDate date3 = date2.withDayOfMonth(25); | ||
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 2); | ||
``` | ||
|
||
Temproal 인터페이스는 LocalDate, LocalTime, LocalDateTime, Instant 처럼 특정 시간을 정의하고 | ||
|
||
get 과 with 메서드로 Temporal 객체의 필드값을 읽거나 고칠 수 있다. | ||
|
||
만약 지정된 필드를 지원하지 않으면 `UnsupportedTemporalTypeException` 이 발생한다. | ||
|
||
선언형으로 LocalDate 를 사용하는 방법도 있다. | ||
|
||
```java | ||
LocalDate date1 = LcoalDate.of(2017, 9, 21); | ||
LocalDate date2 = date1.plusWeeks(1); | ||
LocalDate date3 = date2.minusYears(6); | ||
LocalDate date4 = date3.plus(6, ChronoUnit.MONTHS); | ||
``` | ||
|
||
plus, minus 메서드도 Temporal 인터페이스에 정의되어 있다. | ||
|
||
이를 활용해 특정 시간만큼 앞뒤로 이동시킬 수 있다. | ||
|
||
숫자와 TemporalUnit 을 인수로 활용할 수 있다. | ||
|
||
### 12.2.1 TemporalAdjusters 사용하기 | ||
|
||
`TemporalAdjuster` 는 with 메서드에 좀 더 다양한 동작을 수행할 수 있도록 하는 기능을 제공한다. | ||
|
||
```java | ||
import static java.time.tempoal.TemporalAdjusters.*; | ||
|
||
LocalDate date1 = LocalDate.of(2014, 3, 18); | ||
LocalDate date2 = date1.with(nextOfSame(DayOfWeek.SUNDAY)); | ||
LocalDate date3 = date2.with(lastDayOfMonth()); | ||
``` | ||
|
||
필요한 기능이 정의되어 있지 않을 때는 비교적 쉽게 커스텀 TemporalAdjuster 구현을 만들 수 있다. | ||
|
||
```java | ||
@FunctionalInterface | ||
public interface TemporalAdjuster { | ||
Temporal adjustInto(Temporal temporal); | ||
} | ||
``` | ||
|
||
TemporalAdjuster 인터페이스 구현은 Temporal 객체를 어떻게 다른 Temporal 객체로 변환할 지 정의한다. | ||
|
||
### 12.2.2 날짜와 시간 객체 출력과 파싱 | ||
|
||
포매팅과 파싱 전용 패키지인 `java.time.format` 이 추가되었다. | ||
|
||
가장 중요한 클래스는 `DateTimeFormatter` 이다. | ||
|
||
이를 이용해서 날짜나 시간을 특정 형식의 문자열로 만들 수 있다. | ||
|
||
```java | ||
LocalDate date = LocalDate.of(2014, 3, 18); | ||
String s1 = date.format(DateTimeFormatter.BASIC_ISO_DATE); // 20140318 | ||
String s2 = date.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2014-03-18 | ||
``` | ||
|
||
반대로 문자열을 파싱해서 날짜 객체를 다시 만들 수 있다. | ||
|
||
```java | ||
LocalDate date1 = LocalDate.parse("20140318", DateTimeFormatter.BASIC_ISO_DATE); | ||
LocalDate date2 = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_LOCAL_DATE); | ||
``` | ||
|
||
기존 `java.util.DateFormat` 과 달리 모든 `DateTimeFormatter` 는 스레드 안전한 클래스이다. | ||
|
||
또한 특정 패턴으로 포매터를 만들 수 있는 정적 팩토리 메서드도 제공한다. | ||
|
||
```java | ||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy"); | ||
LocalDate date1 = LocalDate.of(2014, 3, 18); | ||
String formattedDate = date1.format(formatter); | ||
LocalDate date2 = LocalDate.parse(formattedDate, formatter); | ||
``` | ||
|
||
`ofPattern` 메서드도 Locale 로 포매터를 만들 수 있도록 오버로드된 메서드를 제공한다. | ||
|
||
```java | ||
DateTimeFormatter italianFormatter = | ||
DateTimeFormatter.ofPattern("d. MMMM yyyy", Locale.ITALIAN); | ||
... | ||
``` | ||
|
||
DateTimeFormatterBuilder 클래스로 복합적인 포매터를 정의해서 세부적으로 제어할 수도 있다. | ||
|
||
```java | ||
DateTimeFormatter italianFormatter = new DateTimeFormatterBuilder() | ||
.appendText(ChronoField.DAY_OF_MONTH) | ||
.appendLiteral(". ") | ||
.appendText(ChronoField.MONTH_OF_YEAR) | ||
.appendLiteral(" ") | ||
.appendText(ChronoField.YEAR) | ||
.parseCaseInsensitive() | ||
.toFormatter(Locale.ITALIAN); | ||
``` | ||
|
||
# 12.3 다양한 시간대와 캘린터 활용 방법 | ||
|
||
기존의 `java.util.TimeZone` 을 대체할 수 있는 `java.time.ZoneId` 클래스가 새롭게 등장했다. | ||
|
||
이 클래스를 이용하면 써머타임(DST) 같은 복잡한 사항이 자동으로 처리된다. | ||
|
||
ZoneId 클래스는 불변이다. | ||
|
||
### 12.3.1 시간대 사용하기 | ||
|
||
`ZoneRules` 클래스에는 약 40개 정도의 시간대가 있고, getRules() 를 이용해 해당 시간대의 규정을 획득할 수 있다. | ||
|
||
```java | ||
ZoneId romeZone = ZoneId.of("Europe/Rome"); | ||
``` | ||
|
||
지역 ID 는 `지역/도시` 형식으로 이루어지고 `IANA Time Zone Datebase` 에서 제공하는 지역 집합 정보를 사용한다. | ||
|
||
ZoneId 객체를 얻은 다음에는 LocalDate, LocalDateTime, Instant 를 이용해서 ZoneDateTime 인스턴스로 변환할 수 있다. | ||
|
||
```java | ||
LocalDate date = LocalDate.of(2014, Month.MARCH, 18); | ||
ZonedDateTime zdt1 = date.atStartOfDay(romeZone); | ||
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); | ||
ZonedDateTime zdt2 = dateTime.atZone(romeZone); | ||
Instant instant = Instant.now(); | ||
ZonedDateTime zdt3 = instant.atZone(romeZone); | ||
``` | ||
|
||
ZoneId 를 이용해서 LocalDateTime 을 Instant 로 바꿀 수 있다. | ||
|
||
```java | ||
Instant instant = Instant.now(); | ||
LocalDateTime timeFromInstant = LocalDateTime.ofInstant(instant, romeZone); | ||
``` | ||
|
||
### 12.3.2 UTC/Greenwich 기준의 고정 오프셋 | ||
|
||
때로는 협정 세계시(UTC)/그리니치 표준시(GMT) 를 기준으로 표현하기도 한다. | ||
|
||
ZoneId 의 서브클래스인 ZoneOffset 클래스로 런던의 그리니치 0도 자오선과 시간값의 차이를 표현할 수 있다. | ||
|
||
```java | ||
ZoneOffset newYorkOffset = ZoneOffset.of("-05:00"); | ||
``` | ||
|
||
이는 서머타임을 제대로 처리할 수 없어 권장하지 않는다. | ||
|
||
ISO-8601 캘린더 시스템에서 정의하는 UTC/GMT 와 오프셋으로 날짜와 시간을 표현하는 OffsetDateTime 을 만드는 방법도 있다. | ||
|
||
```java | ||
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45); | ||
OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(date, newYorkOffset); | ||
``` | ||
|
||
### 12.3.3 대안 캘린더 시스템 사용하기 | ||
|
||
자바 8에서는 추가로 4개의 캘린더 시스템을 제공한다. | ||
|
||
ThaiBuddhistDate, MinguoDate, JapaneseDate, HijrahDate 이다. | ||
|
||
이 클래스와 LocalDate 클래스는 ChronoLocalDAte 인터페이스를 구현하는데 이는 임의의 연대기에서 특정 날짜를 표현할 수 있는 기능을 제공하는 인터페이스이다. | ||
|
||
```java | ||
LocalDate date = LocalDate.of(2014, Month.MARCH, 18); | ||
JapaneseDate japaneseDate = JapaneseDate.from(date); | ||
``` | ||
|
||
특정 Locale 과 Locale 에 대한 날짜 인스턴스로 캘린더 시스템을 만들수도 있다. | ||
|
||
```java | ||
Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN); | ||
ChronoLocalDate now = japaneseChronology.dateNow(); | ||
``` | ||
|
||
날짜와 시간 API 의 설계자는 ChronoLocalDate 보다는 LocalDate 를 사용하라고 권고한다. |