변경사항
자바 8에서는 신규 Date와 Time에 관한 API가 소개 되었다. 과거 java.util.Date 와 java.util.Calendar 유틸은 정말 형편없었다.
그럼 Java 8이전과 이후의 Date, Time관련 유틸리티가 어떻게 바뀌었는지 한번 살펴보자.
기존 Java Date 유틸리티의 문제.
과거 Java Date유틸리느는 비 일관성, 설계적인 결함 을 가지고 있었다.
- Object 내용 자체가 변경이 된다.
- 이름규칙 Date 클래스이면서 시간까지 모두 커버
- 1월은 데이트 객체 생성시 0에 해당한다.
- Date객체를 생성할때 년도 = 현재년도 - 1900 에 해당하는 오프셋을 .. (흠 매번 계산을 해야하다니)
- thread unsafe한 DateTime Format
새롭게 바뀐점.
- 날짜 구조 LocalDate + LocalTime + ZoneId LocateDateTime (LocalDate + LocalTime) ZonedDateTime (LocalDate + LocalTime + ZoneId)
- 사람을 위한 유틸리티와, 머신을 위한 유틸리티의 구분
- immutable 한 객체 (메소드를 실행하면 새로운 객체가 생성되는 방식) = ThreadSafe한 구조
- TemporalAdjuster를 이용하면 날짜에 대한 커스텀 기능을 이용할 수 있다.
- Thread Safe한 DateTimeFormat지원
- TimeZone을 지원하여 Daylight Saving Time을 지원한다.
사용방법
LocalDateTest.java
// LocalDate생성
LocalDate date = LocalDate.of(2016, 9, 12);
// 현재 날짜를 생성한다
date = LocalDate.now();
System.out.println("-------------[ LocalDate Methods ]--------------");
int year = date.getYear();
System.out.println(String.format("int year = date.getYearI(); : [%s]", year));
Month month = date.getMonth();
System.out.println(String.format("Month month = date.getMonth(); : [%s]", month));
int day = date.getDayOfMonth();
System.out.println(String.format("int day = date.getDayOfMonth(); : [%s]", day));
DayOfWeek dow = date.getDayOfWeek();
System.out.println(String.format("DayOfWeek dow = date.getDayOfWeek(); : [%s]", dow));
int len = date.lengthOfMonth();
System.out.println(String.format("int len = date.lengthOfMonth(); : [%s]", len));
boolean leap = date.isLeapYear();
System.out.println(String.format("boolean leap = date.isLeapYear();: [%s]", leap));
System.out.println("-------------[ Using Temporal Fields ]--------------");
int yearByField = date.get(ChronoField.YEAR);
System.out.println(String.format("int yearByField = date.get(ChronoField.YEAR); : [%s]", yearByField));
int monthByField = date.get(ChronoField.MONTH_OF_YEAR);
System.out.println(String.format("int monthByField = date.get(ChronoField.MONTH_OF_YEAR); : [%s]", monthByField));
int dayByField = date.get(ChronoField.DAY_OF_MONTH);
System.out.println(String.format("int dayByField = date.get(ChronoField.DAY_OF_MONTH); : [%s]", dayByField));-------------[ LocalDate Methods ]--------------
int year = date.getYearI(); : [2016]
Month month = date.getMonth(); : [SEPTEMBER]
int day = date.getDayOfMonth(); : [12]
DayOfWeek dow = date.getDayOfWeek(); : [MONDAY]
int len = date.lengthOfMonth(); : [30]
boolean leap = date.isLeapYear();: [true]
-------------[ Using Temporal Fields ]--------------
int yearByField = date.get(ChronoField.YEAR); : [2016]
int monthByField = date.get(ChronoField.MONTH_OF_YEAR); : [9]
int dayByField = date.get(ChronoField.DAY_OF_MONTH); : [12]LocalTime.java
LocalTime time = LocalTime.of(20, 30, 01);
int hour = time.getHour();
System.out.println(String.format("int hour = time.getHour(); : [%s]", hour));
int minute = time.getMinute();
System.out.println(String.format("int minute = time.getMinute(); : [%s]", minute));
int second = time.getSecond();
System.out.println(String.format("int second = time.getSecond(); : [%s]", second));int hour = time.getHour(); : [20]
int minute = time.getMinute(); : [30]
int second = time.getSecond(); : [1]LocalDateTimeTest.java
LocalDateTime dateTime1 = LocalDateTime.of(2014, Month.SEPTEMBER, 12, 20, 30, 01);
LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dateTime2 = LocalDateTime.of(date, time);
LocalDateTime dateTime3 = date.atTime(20, 35, 30);
LocalDateTime dateTime4 = date.atTime(time);
LocalDate localDate = dateTime1.toLocalDate();
System.out.println(String.format("LocalDate localDate = dateTime1.toLocalDate(); : [%s]", localDate));
LocalTime localTime = dateTime1.toLocalTime();
System.out.println(String.format("LocalTime localTime = dateTime1.toLocalTime(); : [%s]", localTime));LocalDate localDate = dateTime1.toLocalDate(); : [2014-09-12]
LocalTime localTime = dateTime1.toLocalTime(); : [20:30:01]머신을 위한 시간 처리방법
Instant.ofEpochSecond(3); Instant.ofEpochSecond(3, 0); Instant.ofEpochSecond(2, 1_000_000_000); Instant.ofEpochSecond(4, -1_000_000_000);
경과시간 알아내기
Duration durationByTime = Duration.between(time1, time2); Duration durationByDateTime = Duration.between(dateTime1, dateTime2); Duration durationByInstant = Duration.between(instant1, instant2);
경과 날짜 및 월, 년 알아내기
Period tenDays = Period.between(LocalDate.of(2016, 09, 02), LocalDate.of(2016.09.12));
Duration threeMinutes = Duration.ofMinutes(3);
System.out.println(String.format("Duration threeMinutes = Duration.ofMinutes(3); : [%s]", threeMinutes));
Duration fourMinutes = Duration.of(4, ChronoUnit.MINUTES);
System.out.println(String.format("Duration fourMinutes = Duration.of(4, ChronoUnit.MINUTES); : [%s]", fourMinutes));
Period tenDays = Period.ofDays(10);
System.out.println(String.format("Period tenDays = Period.ofDays(10); : [%s]", tenDays));
Period threeWeeks = Period.ofWeeks(3);
System.out.println(String.format("Period threeWeeks = Period.ofWeeks(3); : [%s]", threeWeeks));
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
System.out.println(String.format("Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1); : [%s]", twoYearsSixMonthsOneDay));Duration threeMinutes = Duration.ofMinutes(3); : [PT3M]
Duration fourMinutes = Duration.of(4, ChronoUnit.MINUTES); : [PT4M]
Period tenDays = Period.ofDays(10); : [P10D]
Period threeWeeks = Period.ofWeeks(3); : [P21D]
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1); : [P2Y6M1D]날짜 변경
LocalDate date01 = LocalDate.of(2006, 06, 01);
2006-06-01
LocalDate date02 = date01.withYear(2016);
2016-06-01
LocalDate date03 = date02.withDayOfMonth(12);
2016-06-12
LocalDate date04 = date03.with(ChronoField.MONTH_OF_YEAR, 09);
2016-09-12날짜 연산
LocalDate date01 = LocalDate.of(2006, 06, 01);
2006-06-01
LocalDate date02 = date01.plusYears(10);
2016-06-01
LocalDate date03 = date02.plusMonth(3);
2016-09-01
LocalDate date04 = date03.plus(11, ChronoUnit.DAYS);
2016-09-12TemporalAdjuster interface
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}Temporal 객체를 새로운 Temporal객체로 변경하는 작업을 하도록 하는 인터페이스이다. 이를 통해서 복잡한 날자 연산 처리를 구현할 수 있다.
주말 배송을 하지 않을경우 배송일 처리용 TemporalAdjuster 구현체
public class EstimateDeliverableDay implements TemporalAdjuster {
@Override
public Temporal adjustInto(Temporal temporal) {
DayOfWeek dayOfWeek = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
int duration = 1;
if (dayOfWeek == DayOfWeek.FRIDAY) duration = 3; // 월요일
else if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) duration = 2; // 화요일
return temporal.plus(duration, ChronoUnit.DAYS);
}
}사용
EstimateDeliverableDay estmateDay = new EstimateDeliverableDay();
date = date.with(estmateDay);날짜 포매팅하기.
LocalDate date = LocalDate.of(2016, 9, 12);
String isoDate = date.format(DateTimeFormatter.BASIC_ISO_DATE);
20160912
String isoLocalDate = date.format(DateTimeFormatter.ISO_LOCAL_ATE);
2016-09-12
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String formattedString = date.format(formatter);
LocalDate parsedDate = LocalDate.parse(formattedString, formatter);
2016 09 12
DateTimeFormatter koreanFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.KOREAN);
LodalDate date02 = LocalDate.of(2016,9,12);
String formattedDateString = date02.format(koreanFormatter);
LocaleDate parsedDate02 = LocalDate.parse(formattedDateString, koreanFormatter);날짜 포매팅 빌더 이용하기.
DateTimeFormatter koreanFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH)
.appendLiteral(".")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral(" ")
.appendText(ChronoField.YEAR)
.parseCaseInsensitive()
.toFormatter(Locale.KOREAN);
LocalDate date1 = LocalDate.of(2016, 9, 12);
System.out.println("Formatted Date : " + date1.format(koreanFormatter));
DateTimeFormatter koreanFormatter2 = new DateTimeFormatterBuilder()
.appendText(ChronoField.YEAR)
.appendLiteral("-")
.appendText(ChronoField.MONTH_OF_YEAR)
.appendLiteral("/")
.appendText(ChronoField.DAY_OF_MONTH)
.parseCaseInsensitive()
.toFormatter(Locale.KOREAN);
System.out.println("Formatted Date2 : " + date1.format(koreanFormatter2));
Formatted Date : 12.9월 2016
Formatted Date2 : 2016-9월/12타임존 이용하기.
타임존은 DST(Daylight Saving Time)을 적용한 지역 시간을 위해 사용한다.
ZoneId koreanZone = ZoneId.of("Asia/Seoul");
ZoneId defaultZone = TimeZone.getDefault().toZoneId();
System.out.println("KoreanZone : " + koreanZone);
System.out.println("defaultZone : " + defaultZone);
LocalDate date = LocalDate.of(2016, Month.SEPTEMBER, 12);
ZonedDateTime zoneTime = date.atStartOfDay(koreanZone);
System.out.println(String.format("LocalDate date = LocalDate.of(2016, Month.SEPTEMBER, 12); [%s]", zoneTime));
LocalDateTime datetime = LocalDateTime.of(2016, Month.SEPTEMBER, 12, 23, 24);
ZonedDateTime zoneTime2 = datetime.atZone(koreanZone);
System.out.println(String.format("LocalDateTime datetime = LocalDateTime.of(2016, Month.SEPTEMBER, 12, 23, 24); [%s]", zoneTime2));
Instant instant = Instant.now();
ZonedDateTime zoneTime3 = instant.atZone(koreanZone);
System.out.println(String.format("Instant instant = Instant.now(); [%s]", zoneTime3));KoreanZone : Asia/Seoul
defaultZone : Asia/Seoul
LocalDate date = LocalDate.of(2016, Month.SEPTEMBER, 12); [2016-09-12T00:00+09:00[Asia/Seoul]]
LocalDateTime datetime = LocalDateTime.of(2016, Month.SEPTEMBER, 12, 23, 24); [2016-09-12T23:24+09:00[Asia/Seoul]]
Instant instant = Instant.now(); [2016-09-12T23:17:43.652+09:00[Asia/Seoul]]