LocalDate, LocalTime, Instant, Duration, Period
LocalDate와 LocalTime 사용
LocalDate 인스턴스는 시간을 제외한 날짜를 표현하는 불변 객체
LocalDate 만들고 값 읽기
LocalDate date =LocalDate.of(2017, 9, 21); // 2017-09-21
int year = date.getYear(); // 2017
Month month = date.getMonth() // SEPTEMBER;
int day = date.getDayOfMonth(); // 2
DayOfWekk dow = date.getDayOfWeek(); // THURSDAY
int len = date.lengthOfMonth(): // 31(3월의 일 수)
boolean leap = date.isLeapYear(); // false(윤년이 아님)
TemporalField를 이용해서 LocalDate값 읽기
int year = date.get(ChronoField.YEAR);
int month = date.get(ChronoField.MONTH_OF_YEAR);
int dat = date.get(ChronoField.Day_OF_MONTH);
LocalTime 만들고 값 읽기
LocalTime time = LocalTime.of(13, 45, 20); // 13:45:20
int hour =time.getHour(); // 13
int minute = time.getMinute(); // 45
int second = time.getSecond(); // 20
날짜와 시간 조합
LocalDateTime을 직접 만드는 방법과 날짜와 시간을 조합하는 방법
// 2017-09-21T13:45:20
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의 toLocalDate나 toLocalTime 메서드로 LocalDate나 LocalTime 인스턴스를 추출할 수 있다.
LocalDate date1 = dt1.toLocalDate(); // 2017-09-21
LocalTime time1 = dt1.toLocalTime(); // 13:45:20
Instant 클래스 : 기계의 날짜와시간
팩토리 메서드 OfEpochSecond에 초를 넘겨줘서 Instant 클래스 인스턴스를 만들 수 있다. Instant 클래스는 나노초(10억분의 1초)의 정밀도를 제공한다. 두 번째 인수를 이용해서 나노초 단위로 시간을 보정할 수 있다. 0 ~ 999,999,999 사이 값을 지정할 수 있다.
Instant.ofEpochSecond(3);
Instant.ofEpochSecond(3, 0);
Instant.ofEpochSecond(2, 1_000_000_000); // 2초 이후의 1억 나노초(1초)
Instant.ofEpochSecond(4, -1_000_000_000); // 4초 이전의 1억 나노초(1초)
Instant는 기계 전용의 유틸리티라서 사람이 읽을 수 잇는 시간 정보를 제공하지 않는다.
int day = Instant.now().get(ChronoField.DAY_OF_MONTH);
위 코드는 아래와 같이 예외를 일으킨다.
Java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: DayOfMonth
Duration과 Period 정의
Duration 클래스의 정적 팩토리 메서드 between으로 두 시간 객체 사이의 지속시간을 만들 수 있다. 두 개의 LocalTime, 두 개의 LocalDateTime, 또는 두 개의 Instant로 Duration을 만들 수 있다.
Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration d2 = Duration.between(instant1, instant2);
LocalDateTime은 사람이 사용하도록, Instant는 기계가 사용하도록 만들어진 클래스로 두 인스턴스는 서로 혼합할 수 없다. Duration 클래스는 초와 나노초로 시간 단위를 표현하므로 betwwen 메서드에 LocalDate를 전달할 수 없다. 년, 월, 일로 시간을 표현할 때는 Periode 클래스를 사용한다.
Period tenDays = Period.between(LocalDate.of(2017, 9, 11), LocalDate.of(2017, 9, 21));
Duration과 Period 만들기
Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);
Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
간격을 표현하는 날짜와 시간 클래스의 공통 메서드
메서드 | 정적 | 설명 |
between | 네 | 두 시간 사이의 간격을 생성함 |
from | 네 | 시간 단위로 간격을 생성함 |
of | 네 | 주어진 구성 요소에서 간격 인스턴스를 생성함 |
parse | 네 | 문자열을 파싱해서 간격 인스턴스를 생성함 |
addTo | 아니오 | 현재값의 복사본을 생성한 다음에 지정된 Temporal 객체에 추가함 |
get | 아니오 | 현재 가격 정보값을 읽음 |
isNegative | 아니오 | 간격이 음수인지 확인함 |
isZero | 아니오 | 가격이 0인지 확인함 |
minus | 아니오 | 현재값에서 주어진 시간을 뺀 복사본을 생성함 |
multipliedBy | 아니오 | 현재값에 주어진 값을 곱한 복사본을 생성함 |
negated | 아니오 | 주어진 값의 부호를 반전한 복사본을 생성함 |
plus | 아니오 | 현재값에 주어진 시간을 더한 복사본을 생성함 |
subtractFrom | 아니오 | 지정된 Temporal 객체에서 간격을 뺌 |
날짜 조정, 파싱, 포매팅
withAttribute 메서드로 기존의 LocalDate를 바꾼 버전을 직접 간단하게 만들 수 있다.
절대적인 방식으로 LocalDate의 속성 바꾸기
LocalDate date1 = LocalDate.of(2017, 9, 21) // 2017-09-21
LocalDate date2 = date1.withYear(2011); // 2011-09-21
LocalDate date3 = date2.withDayOfMonth(25); // 2011-09-25
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 2); // 2011-02-25
상대적인 방식으로 LocalDate 속성 바꾸기
LocalDate date1 = LocalDate.of(2017, 9, 21) // 2017-09-21
LocalDate date2 = date1.plusWeeks(1); // 2017-09-28
LocalDate date3 = date2.minsYears(6); // 2011-09-28
LocalDate date4 = date3.plus(6, ChonoUnit.MONTHS); // 2012-03-28
메서드 | 정적 | 설명 |
from | 예 | 주어진 Temporal 객체를 이용해서 클래스의 인스턴스를 생성함 |
now | 예 | 시스템 시계로 Temporal 객체를 생성함 |
of | 예 | 주어진 구성 요소에서 Temporal 객체의 인스턴스를 생성함 |
parse | 예 | 문자열을 파싱해서 Temploral 객체를 생성함 |
atOffset | 아니오 | 시간대 오프셋과 Temporal 객체를 합침 |
atZone | 아니오 | 시간대 오프셋과 Temporal 객체를 합침 |
format | 아니오 | 지정된 포매터를 이용해서 Temporal 객체를 문자열로 변환함(Instant는 지원하지 않음) |
get | 아니오 | Temporal 객체의 상태를 읽음 |
minus | 아니오 | 특정 시간을 뺀 Temporal 객체의 복사본을 생성함 |
plus | 아니오 | 특정 시간을 더한 Temporal 객체의 복사본을 생성함 |
with | 아니오 | 일부 상태를 바꾼 Temporal 객체의 복사본을 생성함 |
[퀴즈 12-1] LocalDate 조정
다음 코드를 실행했을 때 date의 변숫값은?
LocalDate date = LocalDate.of(2014, 3, 18);
date = date.with(ChronoField.MONTH_OF_YEAR, 9);
date = date.plusYears(2).minusDays(10);
date.withYear(2011);
정답
2016-09-08
TemporalAdjusters 사용하기
미리 정의된 TemporalAdjusters 사용하기
import static java.time.temporal.TemporalAdjusters.*;
LocalDate date1 = LocalDate.of(2014, 3, 18); // 2014-03-18
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY)); // 2014-03-23
LocalDate date3 = date2.with(lastDayOfMonth)); // 2014-03-31
TemporalAdjusters 클래스의 팩토리 메서드(http://goo.gl/e1krg1)
메서드 | 설명 |
dayOfWeekInMonth | 서수 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환함(음수를 사용하면 월의 끝에서 거꾸로 계산) |
firstDayOfMonth | 현재 달의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환함 |
firstDayOfNextMonth | 다음 달의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환함 |
firstDayOfNextYear | 내년의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환함 |
firstDayOfYear | 올해의 첫 번째 날짜를 반환하는 TemporalAdjuster를 반환함 |
firstInMonth | 현재 달의 첫 번째 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환함 |
lastDayOfMonth | TemporalAdjuster를 반환함 |
lastDayOfNextMonth | 현재 달의 마지막 날짜를 반환하는 TemporalAdjuster를 반환함 |
lastDayOfNextYear | 다음 달의 마지막 날짜를 반환하는 TemporalAdjuster를 반환함 |
lastDayOfYear | 올해의 마지막 날짜를 반환하는 TemporalAdjuster를 반환함 |
lastInMonth | 현재 달의 마지막 요일에 해당하는 날짜를 반환하는 TemporalAdjuster를 반환함 |
next previous | 현재 달에서 현재 날짜 이후로 지정한 요일이 처음으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환함 |
nextOrSame | 현재 날짜 이후로 지정한 요일이 처음/이전으로 나타나는 날짜를 반환하는 TemporalAdjuster를 반환함(현재 날짜도 포함) |
previousOrSame |
TemporalAdjuster 인터페이스
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInfo(Temporal temporal);
}
[퀴즈 12-2] 커스텀 TemporalAdjuster 구현하기
TemporalAdjuster 인터페이스를 구현하는 nextWorkingDay 클래스를 구현하시오. 이 클래스는 날짜를 하루씩 다음날로 바꾸는데 이때 토요일과 일요일은 건너뛴다. 즉, 다음 코드를 실행하면 다음날로 이동한다.
date = date.with(new NextWorkingDay());
만일 이동된 날짜가 평일이 아니라면, 즉 토요일이나 일요일이라면 월요일로 이동한다.
public class NextWorkingDay implements TemporalAdjuster {
@Override
public Temporal adjustInto(Temporal temporal) {
DayOfWeek dow =
DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK)); // 현재 날짜 읽기
int dayToAdd = 1; // 보통은 하루 추가
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3; // 그러나 오늘이 금요일이면 3일 추가
else if (dow == DayOfWeek.SATURDAY) dayToAdd = 2; // 토요일이면 2일 추가
return temporal.plus(dayToAdd, ChronoUnit.DAYS); // 적절한 날 수만큼 추가된 날짜를 반환
}
}
TemporalAdjuster 클래스는 날짜를 다음날로 이동한다. 하지만 현재 요일이 금요일이나 토요일이라면 각각 3일 또는 2일을 이동하게 된다. TemporalAdjuster는 함수형 인터페이스이므로 람다 표현식을 이용할 수 있다.
date = date.with(temporal -> {
DayOfWeek dow =
DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int dayToAdd = 1;
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
else if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
return temporal.plus(daToAdd, ChronoUnit.DAYS);
});
만일 TemporalAdjuster를 람다 표현식으로 정의하고 싶다면 다음 코드에서 보여주는 것처럼 UnaryOperator<LocalDate>를 인수로 받는 TemporalAdjusters 클래스의 정적 팩토리 메서드 ofDateAdjuster를 사용하는 것이 좋다.
TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(
temporal -> {
DayOfWeek dow =
DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
else if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;
return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});
Date = date.with(nextWorkingDay);
날짜와 시간 객체 출력과 파싱
날짜나 시간을 특정 형식의 문자열로 만들기
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
반대로 날짜나 시간을 표현하는 문자열을 파싱해서 날짜 객체를 다시 만들기
LocalDate date1 = LocalDate.parse(“20140318”,
DateTimeFormatter.BASIC_ISO_DATE);
LocalDate date2 = LocalDate.parse(“2014-03-18”, DateTimeFormatter.ISO_LOCAL_DATE);
패턴으로 DateTimeFormatter 만들기
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);
지역화된 DateTimeFormatter 만들기
DateTimeFormatter italianFormatter =
DateTimeFormatter.ofPattern(“d. MMMM yyyy”, Locale.ITALIAN);
LocalDate date1 = LocalDate.of(2014, 3, 18);
String formattedDate = date.format(italianFormatter); // 18. Marzo 2014
LocalDate date2 = LocalDate.parse(formattedDate, italianFormatter);
DateTimeFormatter 만들기
DateTimeFOrmatter italianFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(". ")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.ITALIAN);
다양한 시간대와 캘린더 활용 방법
Java.timeZoneId 클래스가 새롭게 등장했다. 새로운 클래스를 이용하면 서머타임(Daylight Saving Time)(DST) 같은 복잡한 사항이 자동으로 처리된다. ZoneId는 불변 클래스다.
시간대 사용하기
표준 시간이 같은 지역을 묶어서 시간대(time zone)규칙 집합을 정의한다.
ZoneId의 getRules()를 이용해서 해당 시간대의 규정을 획득할 수 있다.
ZoneId romeZone = ZoneId.of(“Europe/Rome”);
지역 ID는’{지역}/{도시}’ 형식으로 지루어진다.(https://www.iana.org/time-zones 참고)
새로운 메서드인 toZoneId로 기존 TimeZone 객체를 ZoneId 객체로 변환할 수 있다.
ZoneId zoneId = TimeZone.getDefault().toZoneId();
특정 시점에 시간대 적용
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
ZonedDateTime zdt1 = date.atStratOfDay(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로 바꾸는 방법도 있다.
Instant instant = Instant.now();
LocalDateTime timeFormInstant = LocalDateTime.ofInstant(instant, romeZone);
폐기된 API와 새 날짜와 시간 API 간의 동작에 도움이 되는 toInstant(), 정적 메서드 fromInstant() 두 개의 메서드가 있다.
UTC/Greenwich 기준의 고정 오프셋
때로는 UTC(Universal Time Coordinated)(협정 세계시)/GMT(Greenwich Mean Time)(그리니치 표준시)를 기준으로 시간대를 표현하기도 한다. 예를 들어 ‘뉴욕은 런던보다 5시간 느리다’라고 표현할 수 있다. ZoneId의 서브클래스인 ZoneOffest 클래스로 런던의 그리니치 0도 자오선과 시간값의 차이를 표현할 수 있다.
ZoneOffset newYorkOffset = ZoneOffset.of(“-05:00”);
UTC/GMT와 오프셋으로 날짜와 시간을 표현하는 OffsetDateTime을 만드는 방법도 있다.
LocalDateTime dateTime = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45);
OffsetDateTime dateTimeInNewYork = OffsetDateTime.of(date, newYorkOffset);
Eodks 캘린더 시스템 사용하기
자바 8에서는 ThaiBuddhistDate, MinguoDate, JapaneseDate, HijrahDate개의 대표 캘린더 시스템을 제공한다. LocalDate를 이용해서 4개의 클래스 중 하나의 인스턴스를 만들 수 있다.
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
JapaneseDate japaneseDate = JapaneseDate.from(date);
Locale과 Locale에 대한 날짜 인스턴스로 캘린더 시스템으 만드는 방법도 있다. 정적 팩토리 메서드 ofLocale을 이용해서 Chronology의 인스턴스를 획득할 수 있다.
Chronology japaneseChronology = Chronology.ofLocale(Locale.JAPAN);
ChronoLocalDate now = japaneseChronology.date();
LocalDate 사용을 권고한다. 프로그램의 입출력을 지역화하는 상황을 제외하고는 모든 데이터 저장, 조작, 비즈니스 규칙 해석 등의 작업에서 LocalDate를 사용하는 것이 좋다.
이슬람격
자바 8에서는 HijrahDate의 표준 변형 방법으로 UmmAl-Qura를 제공한다.
HijrahDate ramadanDate =
HijrahDate.now().with(ChronoField.DAY_OF_MONTH, 1)
.with(ChronoField.MONTH_OF_YEAR, 9); // 현재 Hijrah 날짜를 얻음 얻은 날짜를 Ramadan의 첫 번째 날, 즉 9번째 달로 바꿈
System.out.println("Ramadan starts on " +
IsoChronology.INSTANCE.dater(ramadanDate) + // INSTANCE는 IsoChronology 클래스의 정적 인스턴스임
" and ends on " +
IsoChronology.INSTANCE.date( // Ramadan 1438은 2017-05-26에 시작해서 2017-06-24에 종료됨.
ramadanDate.with(
TemporalAdjusters.lastDayOfMonth())));
'Programming > Java' 카테고리의 다른 글
EPISODE 14. 모던 자바 인 액션(CHAPTER 14 자바 모듈 시스템) (0) | 2023.07.25 |
---|---|
EPISODE 13. 모던 자바 인 액션(CHAPTER 13 디폴트 메서드) (1) | 2023.07.24 |
EPISODE 11. 모던 자바 인 액션(CHAPTER 11 null 대신 Optional 클래스) (0) | 2023.07.20 |
EPISODE 10. 모던 자바 인 액션 (CHAPTER 10. 람다를 이용한 도메인 전용 언어) (0) | 2023.07.18 |
EPISODE 9. 모던 자바 인 액션(CHAPTER 9 리팩터링, 테스팅, 디버킹) (0) | 2023.07.17 |