TemporalAdjusters

  • 자주 쓰일만한 날짜 계산들을 대신 해주는 메서드를 정의해 놓은 클래스

    • 예시 : 다음 주 월요일의 날짜를 계산할 때 TemporalAdjusters에 정의된 next()를 사용한 예
LocalDate today = LocalDate.now();
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
  • 메서드 종류
메서드 설명
firstDayOfNextYear() 다음 해의 첫 날
firstDayOfNextMonth() 다음 달의 첫 날
firstDayOfYear() 올 해의 첫 날
firstDayOfMonth() 이번 달의 첫 날
lastDayOfYear() 올 해의 마지막 날
lastDayOfMonth() 이번 달의 마지막 날
firstInMonth(DayOfWeek dayOfWeek) 이번 달의 첫 번째 ?요일
lastInMonth(DayOfWeek dayOfWeek) 이번 달의 마지막 ?요일
previous(DayOfWeek dayOfWeek) 지난 ?요일(당일 미포함)
previousOrSame(DayOfWeek dayOfWeek) 지난 ?요일(당일 포함)
next(DayOfWeek dayOfWeek) 다음 ?요일(당일 미포함)
nextOrSame(DayOfWeek dayOfWeek) 다음 ?요일(당일 포함)
dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) 이번 달의 n번째 ?요일


  • TemporalAdjuster 인터페이스

    • 추상메서드 하나만 정의되어있고, 이 메서드만 구현하면 됨

    • 실제로 구현하는 것은 adjustInto()지만, 같이 사용해야 하는 메서드는 with()
      어느 쪽을 사용해도 되긴 함
      adjustInto() : 내부적으로 사용할 의도로 작성
      with() : 되도록 사용할 것

    • adjustInto()의 매개변수
      날짜와 시간에 관련된 대부분의 클래스가 Temporal 인터페이스를 구현하였기 때문에 adjustInto의 매개변수가 될 수 있음

@FuntionalInterface
public interface TemporalAdjuster {
    Temporal adjustInto(Temporal temporal);
}
  • TemporalAdjuster 직접 구현하기

    • 보통은 TemporalAdjusters에 정의된 메서드로 충분

    • 필요할 경우, 자주 사용되는 날짜계산을 해주는 메서드를 직접 만들 수도 있음

    • LocalDate의 with는 다음과 같이 정의되어있고, TemporalAdjuster인터페이스를 구현한 클래스의 객체를 매개변수로 제공
      LocalDate With(TemporalAdjuster adjuster)

    • with()
      LocalTime, LocalDateTime, ZonedDateTime, Instant 등 대부분의 날짜와 시간에 관련된 클래스에 포함되어있음

    • TemporalAdjuster 인터페이스 구현

    • 날짜와 시간에 관련된 대부분의 클래스가 Temporal 인터페이스를 구현하였기 때문에 adjustInto의 매개변수가 될 수 있음

    • 예제) 특정 날짜로부터 2일 후의 날짜를 계산하는 DayAfterTomorrow

Class DayAfterTomorrow implements TemporalAdjuster {
     @Override
     public Temporal adjustInto(Temporal temporal) {
         return temporal.plus(2, ChronoUnit.DAYS); // 2일을 더하기
     }
}



Period와 Duration

  • Period는 날짜의 차이를, Duration은 시간의 차이를 계산

    • Period = 날짜 - 날짜

    • Duration = 시간 - 시간

  • Period.between(date1, date2);

    • 두 날짜 date1과 date2의 차이

    • date1이 date2보다 날짜 상으로 이전이면 양수, 이후이면 음수로 저장

    • 예시

LocalDate date1 = LocalDate.of(2019, 1, 1);
LocalDate date2 = LocalDate.of(2019, 9, 14);

Period pe = Period.between(date1, date2);

System.out.println(pe); // P8M13D
  • Duration.between(time1, time2);

    • 두 시간 time1과 time2의 차이

    • 나머진 Period와 같음

    • 예시

LocalTime time1 = LocalTime.of(00,00,00); // 00시 00분 00초
LocalTime time2 = LocalTime.of(12,34,56); // 12시 34분 56초

Duration du = Duration.between(time1, time2);

System.out.println(du); //PT12H34M56S
  • get()

    • 특정 필드의 값을 얻을 때 사용
long year = pe.get(ChronoUnit.YEARS);    // int getYears()
long month = pe.get(ChronoUnit.MONTHS);    // int getMonths()
long day = pe.get(ChronoUnit.DAYS);    // int getDays()

long sec = du.get(ChronoUnit.SECONDS);   // long getSeconds() => 초로 전체 환산
int nano = du;get(ChronoUnit.NANOS);    // int getNano()
  • getUnits()

    • duration은 period와 달리 getHours(), getMinuites() 같은 메서드가 없음

    • getUnits()라는 메서드 : get()에 사용할 수 있는 ChronoUnit의 종류를 확인할 수 있음

System.out.println(pe.getUnits()); // [Years, Months, Days]
System.out.println(du.getUnits()); // [Seconds, Nanos]
  • duration에서 시/분/초/나노초 구하기 - 1

    • 불편하지만 사용할 수 있는 방법
시간 : long hour = du.getSeconds() / 3600;
 : long min = (du.getSeconds() - hour*3600) / 60;
 : long sec = (du.getSeconds() - hour*3600 - min * 60) % 60;
나노 : nano = du.getNano();
  • duration에서 시/분/초/나노초 구하기 - 2 (좀 더 안전한 방법)

    • Duration을 LocalTime으로 변환한 후 LocalTime이 가지고 있는 get 메서드들을 사용
Local Time tmpTime = LocalTime.of(0,0).plusSeconds(du.getSeconds());

int hour = tmpTime.getHour();
int min = tmpTime.getMinute();
int sec = tmpTime.getSecond();
int nano = du.getNano();
  • between()과 until()

    • until()은 between()과 거의 같은 일을 함

    • 차이점

    <buntil() : 인스턴스 메서드
    between() : static 메서드

// Period pe = Period.between(today, myBirthDay);
Period pe = today.until(myBirthDay);
long dday = today.until(myBirthDay, ChronoUnit.DAYS);
  • Period는 년월일을 분리해서 저장

    • D-day를 구하려는 경우 : 두 개의 매개변수를 받는 util()을 사용하는 것이 나음

    • 날짜가 아닌 시간에도 until()을 사용할 수 있지만, Duration을 반환하는 util()은 없음

long sec = LocalTime.now().until(endTime, ChronoUnit.SECONDS);
  • of()

    • Period : of(), ofYears(), ofMonths(), ofWeeks(), ofDays()

    • Duration : of(), ofDays(), ofHours(), ofMinutes(), ofSeconds() 등

    • 사용법 : LocalDate와 LocalTime과 다름

Period pe = Period.of(1, 12, 31); // 1년 12개월 31일
Duration du = Duration.of(60, ChronoUnit.SECONDS); // 60초
// Duration du = Duration.ofSeconds(60); // 위의 문장과 동일
  • with()
pe = pe.withYear(2); // 1년에서 2년으로 변경. withMonths(), withDays();
du = du.withSeconds(120); // 60초에서 120초로 변경. withNanos()
  • multipliedBy(), dividedBy()

    • plus(), minus()외에 곱셈과 나눗셈을 위한 메서드도 있음

    • Period - 나눗셈을 위한 메서드가 없음
      날짜와 기간을 표현하기 위한 것 : 나눗셈을 위한 메서드가 별로 유용하지 않기 떄문에 넣지 않은 것

pe = pe.minusYear(1).multipliedBy(2); // 1년을 빼고, 2배를 곱한다
du = du.plusHours(1).dividedBy(60); // 1시간을 더하고 60으로 나눈다.
  • isNegative(), isZero()

    • isNegative() : 음수인지 확인, isZero() : 0인지 확인

    • 두 날짜 또는 시간을 비교할 때, 사용하면 어느 쪽이 앞인지 또는 같은지 알아낼 수 있음

boolean sameDate = Period.between(date1, date2).isZero();
boolean isBefore = Duration.between(time1, time2).isNegative();
  • negate(), abs()

    • negate() : 부호를 반대로 변경, abs() : 부호를 없애는 것

    • Period에는 abs()가 없어서 다음 코드를 써야함

du = du.abs();
if(du.isNegative())
    du = du.negated();
  • Period의 noramlized()

    • 이 메서드는 월(month)의 값이 12를 넘지 않게, 1년 13개월을 2년으로 바꿔줌
      일(day)의 길이는 일정하지 않으므로, 그대로 놔둠
pe = Period.of(1,13,32).normalized();
// 1년 13개월 32일 > 2년 1개월 32일
  • 다른 단위로 변환 - toTotalMonths(), toDays(), toHours(), toMinutes()

    • Period와 Duration을 다른 단위의 값으로 변환하는데 사용

    • get()은 특정 필드의 값을 그대로 가져오지만, 아래의 메서드들은 특정 단위로 변환한 결과를 반환한다는 차이

    • 반환타입은 모두 정수(long 타입)이라, 지정된 단위 이하의 값들은 버려진다는 뜻

클래스 메서드 설명
Period long toTotalMonth() 년월일을 월단위로 변환해서 반환(일 단위는 무시)
Duration long toDay() 일단위로 변환해서 반환
Duration long toHours() 시간단위로 변환해서 반환
Duration long toMinutes() 분단위로 변환해서 반환
Duration long toMillis() 천분의 일초 단위로 변환해서 반환
Duration long toNanos() 나노초 단위로 변환해서 반환


  • LocalDate의 toEpochDay()메서드

    • Epoch Day : ‘1970-01-01’부터 날짜를 세어서 반환함

    • Period를 사용하지 않고도 두 날짜의 일수를 편리하게 계산할 수 있음
      (단 두 날짜 모두 Epoch Day) 이후의 것이어야 함

LocalDate date1 = LocalDate.of(2015, 11, 28);
LocalDate date2 = LocalDate.of(2015, 11, 29);

long period = date2.toEpochDay() - date1.toEpochDay(); 
  • LocalTime의 toSecondofDay(), toNanoOfDay()

    • Duration을 사용하지 않고도 위와 같이 뺄셈으로 시간 차이를 계산할 수 있음
int toSecondOfDay()
long toNanoOfDay()



파싱과 포맷

  • 날짜와 시간을 원하는 형식으로 출력하고 해석(파싱, parsing)하는 방법

  • 형식화(formatting)와 관련된 클래스들은 java.time.format패키지에 들어있음

    • 이 중 DateTimeFormatter가 핵심
LocalDate date = LocalDate.of(2016, 1, 2);

String yyyymmdd = DateTimeFormatter.ISO_LOCAL_DATE.format(date); // "2016-01-02"
String yyyymmdd = date.format(DateTimeFormatter.ISO_LOCAL_DATE); // "2016-01-02"
  • DateTimeFormatter

    • 자주 쓰이는 다양한 형식들을 기본적으로 정의

    • 그 외의 형식이 필요하다면 직접 정의해서 사용할 수 있음

  • DateTimeFormatter에 상수로 정의된 형식들의 목록

DateTimeFormatter 설명 보기
ISO_DATE_TIME Date and time with ZoneId 2019-09-14T08:59:23+09:00[Asia/Seoul]
ISO_LOCAL_DATE ISO Local Date 2019-09-14
ISO_LOCAL_TIME Time without offset 08:59:23
ISO_LOCAL_DATE_TIME ISO Local Date and Time 2019-09-14T08:59:23
ISO_OFFSET_DATE ISO Date with Offset 2019-09-14+09:00
ISO_OFFSET_TIME Time with offset 08:59:23+09:00
ISO_OFFSET_DATE_TIME Date Time with Offset 2019-09-14T08:59:23+09:00
ISO_ZONED_DATE_TIME Zoned Date Time 2019-09-14T08:59:23+09:00[Asia/Seoul]
ISO_INSTANT Date and Time of an Instant 2019-09-14T08:59:23Z
BASIC_ISO_DATE Basic ISO Date 20190914
ISO_DATE ISO Date with or without offset 2019-09-14+09:00
2019-08-14
ISO_TIME Time with or without offset 10:15:30+01:00
10:15:30
ISO_ORDINAL_DATE Year and day of year 2020-349
ISO_WEEK_DATE Year and Week 2020-W51-2
RFC_1123_DATE_TIME RFC 1123 / RFC 822 Wed, 14 NOV 2019 08:59:23 GMT


  • DateTimeFormatter의 static메서드 ofLocalizedDate(), ofLocalizedTime(), ofLocalizedDateTime()

    • 로케일에 종속된 형식화 : 로케일(locale)에 종속된 포메터를 생성
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
String shortFormat = formatter.format(LocalDate.now());
  • 로케일에 종속된 형식화 - FormatStyle의 종류에 따른 출력 형태
FormatStyle 날짜 시간
FULL 2015년 11월 28일 토요일 N/A
LONG 2015년 11월 28일 (토) 오후 9시 15분 13초
MEDIUM 2015. 11. 28 오후 9:15:13
SHORT 15. 11. 28 오후 9:15


  • 출력형식 직접 정의하기

    • DateTimeFormatter의 ofPattern()으로 원하는 출력형식을 직접 작성할 수 있음
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/mm/dd");
  • 출력형식 직접 정의시, 사용되는 기호 목록
기호 의미 보기
G 연대(BC, AD) 서기 또는 AD
y 또는 u 년도 2015
M 또는 L 월(1~12 또는 1월 ~ 12월) 11
Q 또는 q 분기(quarter) 4
w 년의 몇 번째 주(1~53) 48
W 월의 몇 번째 주(1~5) 4
D 년의 몇 번째 일(1~366) 332
d 월의 몇 번째 일(1~31) 28
F 월의 몇 번째 요일(1~5) 4
E 또는 e 요일 토 또는 7
a 오전/오후(AM, PM) 오후
H 시간(0~23) 22
k 시간(1~24) 22
K 시간(0~11) 10
h 시간(1~12) 10
m 분(0~59) 12
s 초(0~59) 35
S 천 분의 일 초(0~999) 7
A 천 분의 일 초
(그 날의 0시 0분 0초부터의 시간)
80263808
n 나노초(0~999999999) 475000000
N 나노초
(그 날의 0시 0분 0초부터의 시간)
81069992000000
V (VV) 시간대 ID Asia/Seoul
z 시간대(time-zone) 이름 KST
O 지역화된 zone-offset GMT+9
Z zone-offset +0900
X또는 x zone-offSet(Z는 +00:00를 의미) +09
escape 문자
(특수문자를 표현하는데 사용)
없음


  • parse() : 문자열을 날짜와 시간으로 파싱하기

    • static 메서드

    • 문자열을 날짜 또는 시간으로 변환할 때 사용

    • 날짜와 시간을 표현하는데 사용되는 클래스에는 해당 메서드가 거의 다 포함되어 있음

    • 오버로딩 된 메서드가 여러개 있음
      자주 사용 되는 것은 2개
      static LocalDateTime parse(CharSequence text)
      static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter)

    • 상수로 정의된 형식을 사용할 때는 다음과 같이
      LocalDate date = LocalDate.parse(“2019-08-19”, DateTimeFormatter.ISO_LOCAL_DATE);

    • 자주 사용되는 기본적인 형식의 문자열은 ISO_LOCAL_DATE와 같은 형식화 상수를 사용하지 않아도 됨
      LocalDate newDate = LocalDate.parse(“2019-08-19”);
      LocalTime newTime = LocalTime.parse(“12:31:30”);
      LocalDateTime newDateTime = LocalDateTime.parse(“2019-08-19T12:31:30”);

    • ofPattern()을 이용하여 파싱할 수도 있음
      DateTimeFormatter pattern = DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss”);
      LocalDateTime endOfYear = LocalDateTime.parse(“2019-08-19 12:31:30”, pattern);