개발자의 서재
[java8]새로운 날짜와 시간 API 본문
1. 새로운 날짜와 시간 API 생성 배경
1-1. java 1.0 에서는 java.util.Date 클래스 하나로 날짜와 시간 관련 기능을 모두 제공했다.
날짜를 의미하는 Date 라는 뜻과 달리 특정 시점을 밀리초단위로 표현한다.
게다가 1900년을 기준으로 하고, 0부터 시작하는 달 인덱스등 모호한 설계가 문제였었다.
Date date = new Date();
System.out.println(date.getTime());
// 1654145972877 -> 1900년 1월1월 0시0분0초 기준 현재까지의 밀리초
System.out.println(date); //Thu Jun 02 13:59:32 KST 2022
Date date2 = new Date(2022,6,2); // 년,월,일을 그대로 넣으면..
System.out.println(date2); //Sun Jul 02 00:00:00 KST 3922 => 3922년 7월 2일로 나옴.
Date date3 = new Date(122,5,2); // 이렇게 넣어줘야
System.out.println(date3); //Tue Jun 02 00:00:00 KST 2022 => 원하는 날짜로 나옴. 불편!
1-2 .자바 1.1 에서는 java.util.Calendar 클래스가 대안으로 제공됨.
안타깝게도 Calendar 클래스도 설계문제가 존재했음.
1900년도부터 시작하는 오프셋은 없앴지만, 여전히 0부터 시작하는 달 인덱스.
그리고 Date,Calendar 두 가지 클래스를 쓰게 되면서
개발자들에게 혼란이 가중됨. 거기다 DateFormat 같은 기능은 Date 클래스에만 작동했다.
DateFormat 에도 멀티스레드에 안전하지 않다는 문제가 있었다. 두 스레드가 동시에 하나의 포매터로
날짜를 파싱할때 예기치 못한 문제가 생길 수 있다. (?)
그리고 Date와 Calendar는 모두 가변 클래스 (객체의 날짜정보가 변경가능) 라는 점 때문에
유지보수도 어려워짐. 그래서 이러한 문제를 해결하기 위해 java8에서는 java.time 패키지가 추가되었다.
2. LocalDate, LocalTime, LocalDateTime
2-1. LocalDate 클래스는 시간을 제외한 날짜를 표현하는 불변객체이다.
LocalDate localDate = LocalDate.of(2019,12,25); //of 메소드를 사용해 특정 날짜 지정 가능
System.out.println(localDate); // 2019-12-25
System.out.println(localDate.getYear()); // 2019
System.out.println(localDate.getMonth()); // DECEMBER
System.out.println(localDate.getMonthValue()); // 12
System.out.println(localDate.getDayOfMonth()); // 25
System.out.println(localDate.getDayOfWeek()); // WEDNESDAY
//Date와 Calerdar의 단점인 1900년 기준으로 연도 입력방식 과 원하는 월보다 -1해서 입력해야 하는 불편함이 해소됨.
LocalDate localDateToday = LocalDate.now(); // now()를 사용해 현재날짜 구함.
System.out.println(localDateToday); // 2022-06-02
2-2. 시간은 LocalTime 클래스로 표현할 수 있다.
LocalTime localTime = LocalTime.of(13,45,20);
System.out.println(localTime); // 13:45:20
System.out.println(localTime.getHour()); // 13
System.out.println(localTime.getMinute()); // 45
System.out.println(localTime.getSecond()); // 20
LocalTime localTimeNow = LocalTime.now();
System.out.println(localTimeNow); // 22:31:30.974735300 // LocalTime 은 나노초(10억분의 1초)까지 표현 가능.
System.out.println(localTimeNow.getHour()); // 22
System.out.println(localTimeNow.getMinute()); // 31
System.out.println(localTimeNow.getSecond()); // 30
2-3. LocalDateTime 은 LocalDate와 LocalTime 을 합친 복합클래스이다. 날짜와 시간 모두 표현할 수 있다.
LocalDateTime nowDateTime = LocalDateTime.now();
System.out.println(nowDateTime); // 2022-06-02T14:26:45.473175
LocalDateTime localDateTime = LocalDateTime.of(2017, Month.APRIL,20,15,30,10);
System.out.println(localDateTime); // 2017-04-20T15:30:10
LocalDate localDate1 = localDate.now();
LocalTime localTime1 = LocalTime.now();
LocalDateTime localDateTime2 = LocalDateTime.of(localDate1, localTime1); //LocalDate 객체와 LocalTime 객체를 결합해서 LocalDateTime 을 만들수도있다.
System.out.println(localDateTime2); // 2022-06-02T14:26:45.473301
//LocalDateTime 간 비교
System.out.println(nowDateTime.compareTo(localDateTime)); // 5 (양수 : 현재날짜가 비교날짜보다 미래이다. )
System.out.println(nowDateTime.compareTo(localDateTime2)); // -1 (음수 : 현재날짜가 비교날짜보다 과거이다. )
3. Duration 과 Period
3-1. Duration과 Period 를 사용하여 날짜,시간 클래스들 간의 날짜,시간 차이값을 구할 수 있다.
Duration 은 시간(일,시,분,초,나노초)간격 을 구할 때 사용하고,
Period 는 날짜(연,월,일 )간격을 구할 때 사용한다.
LocalDateTime nowDateTime1 = LocalDateTime.of(2022, Month.APRIL,20,6,0,0);
LocalDateTime nowDateTime2 = LocalDateTime.of(2022, Month.APRIL,21,8,20,30);
//duration 을 통해 두시간 사이의 간격을 구할 수 있다. (일수 차이~나노초 차이)
Duration d1 = Duration.between(nowDateTime1, nowDateTime2);
System.out.println(d1); //PT26H20M30S
System.out.println(d1.toDays()); // 1
System.out.println(d1.toHours()); // 26
System.out.println(d1.toMinutes()); //1580
System.out.println(d1.toSeconds()); //94830 초
System.out.println(d1.getNano()); // 0 ?? > 시간차이가 1초를 초과하면 나노초는 표기되지 않음.
LocalDateTime nowDateTime3 = LocalDateTime.of(2022, Month.APRIL,20,06,00,00, 999999999);
Duration d2 = Duration.between(nowDateTime1, nowDateTime3);
System.out.println(d2.getNano()); // 999999999 (nano 초는 0 ~ 999,999,999 나노초까지 표현가능)
//period 는 날짜간격(연, 월, 일)을 구할때 사용한다.
Period period = Period.between(LocalDate.of(2021,03,20), LocalDate.of(2022,06,01));
System.out.println(period); //P1Y2M12D
System.out.println(period.getYears()); // 1
System.out.println(period.getMonths()); // 2
System.out.println(period.getDays()); // 12
4. 날짜 조정, 변경
4-1. with, plus, minus 등의 메소드를 사용하여 날짜나 시간을 변경 할 수있다.
모든 메소드는 기존객체의 날짜를 바꾸진 않는다. (java.time 시간객체는 불변객체)
//절대적인 방식으로 LocalDate 속성 바꾸기 with 사용
LocalDate dt1 = LocalDate.of(2022,06,01);
LocalDate dt2 = dt1.withYear(2023);
LocalDate dt3 = dt2.withMonth(8);
LocalDate dt4 = dt3.withDayOfMonth(25);
System.out.println(dt4); // 2023-08-25
//상대적인 방식으로 LocalDate 속성 바꾸기
LocalDate dt5 = LocalDate.of(2022,04,01);
LocalDate dt6 = dt5.plusWeeks(1);
LocalDate dt7 = dt6.minusYears(2);
LocalDate dt8 = dt7.plusMonths(3);
System.out.println(dt8); // 2020-07-08
//LocalDateTime 도 동일함.
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // 2022-06-02T15:12:15.678334
LocalDateTime localDateTime1 = localDateTime.plusHours(3);
System.out.println(localDateTime1); // 2022-06-02T18:12:15.678334
- QUIZ
LocalDateTime quizTime = LocalDateTime.of(2022,06,02,18,00,00);
quizTime.plusYears(1);
quizTime = quizTime.minusMonths(2);
quizTime = quizTime.plusDays(5);
System.out.println(quizTime.format(DateTimeFormatter.ISO_DATE_TIME));
//???
- 정답 확인
- 2022-04-07T18:00:00
4-2. TemporalAdjusters 사용하기
지금까지 사용한 날짜 조정 기능은 비교적 간단한 편이고, 다음주 일요일, 돌아오는 평일, 어떤달의 마지막날 등
좀더 복잡한 날짜기능이 필요 한 경우 with메소드에 더 다양한 동작을 제공하는 TemporalAjusters를 전달
하는 방법을 사용한다.
LocalDate date1 = LocalDate.of(2022,06,01);
LocalDate date2 = date1.with(TemporalAdjusters.firstDayOfMonth()); // 이번달 첫일
System.out.println(date2); // 2022-06-01
LocalDate date3 = date1.with(TemporalAdjusters.lastDayOfMonth()); // 이번달 말일
System.out.println(date3); // 2022-06-30 // 이후에 나올 formater 를 사용하여 이번달 첫일, 말일이 무슨 요일인지 확인기능하다.
LocalDate date4 = date1.with(TemporalAdjusters.firstInMonth(DayOfWeek.SUNDAY)); //이번달 첫번째 일요일
System.out.println(date4); // 2022-06-05
LocalDate date5 = date1.with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY)); //이번달 마지막 일요일
System.out.println(date5); // 2022-06-30
LocalDate date6 = date1.with(TemporalAdjusters.dayOfWeekInMonth(3,DayOfWeek.FRIDAY)); //이번달 3번째 금요일
System.out.println(date6); // 2022-06-17
5. 날짜 포매팅, 파싱
5-1. 날짜 시간 관련 작업에서 포매팅과 파싱은 서로 떨어질 수 없는 관계다.
java8 에서는 포매팅과 파싱전용 패키지인 java.time.format 이 추가되었다.
이 패키지에서 가장 중요한 클래스는 DateTimeFormatter다. DateTimeFormatter를 이용해서
날짜와 시간을 특정형식의 문자열로 만들 수 있다.
다음은 두개의 서로다른 포매터로 문자열을 만드는 예제이다.
//DateTimeFormatter 에서 기본적으로 제공되는 포맷형식
LocalDate date = LocalDate.of(2022,06,01);
System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE)); // 20220601
System.out.println(date.format(DateTimeFormatter.ISO_DATE)); // 2022-06-01
LocalDateTime time = LocalDateTime.of(2022,06,01,11,10,20);
System.out.println(time.format(DateTimeFormatter.ISO_LOCAL_TIME)); // 11:10:20
System.out.println(time.format(DateTimeFormatter.ISO_DATE_TIME)); // 2022-06-01T11:10:20
//날짜나 시간을 표현하는 문자열을 파싱해서 날짜 객체를 만들 수 있다.
System.out.println(LocalDate.parse("20220612", DateTimeFormatter.BASIC_ISO_DATE)); // 2022-06-01
System.out.println(LocalDate.parse("2022-06-12", DateTimeFormatter.ISO_DATE)); // 2022-06-01
System.out.println(LocalDateTime.parse("2022-06-01T11:10:20", DateTimeFormatter.ISO_DATE_TIME)); // 2022-06-01T11:10:20
//원하는 특정 패던으로 DateTimeFormatter 만들기
LocalDateTime time1 = LocalDateTime.of(2022,06,02,13,45,10);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd(E) a hh:mm:ss");
System.out.println(time1.format(formatter)); // 2022/06/02(목) 오후 01:45:10
DateTimeFormatter formatter24 = DateTimeFormatter.ofPattern("yyyy/MM/dd(E) HH:mm:ss");
System.out.println(time1.format(formatter24)); // 2022/06/02(목) 13:45:10
6. ZoneDateTime, ZoneId
6-1.지금까지 살펴본 모든 클래스에는 시간대와 관련된 정보가 없었다.
기존의 java.util.TimeZone을 대체할 수 있는 java.tome.ZoneId 클래스가 새롭게 등장했다.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd(E) a hh:mm:ss");
LocalDateTime time = LocalDateTime.now();
LocalDateTime timeRome = LocalDateTime.now(ZoneId.of("Europe/Rome"));
System.out.println(time.format(formatter)); // 2022/06/02(목) 오후 03:26:28
System.out.println(timeRome.format(formatter)); // 2022/06/02(목) 오전 08:26:28
//ZonedDateTime 은 LocalDateTime에 시간대, 시차 정보가 추가된 클래스
ZonedDateTime ztime = ZonedDateTime.now();
ZonedDateTime ztimeRome = ZonedDateTime.now(ZoneId.of("Europe/Rome"));
System.out.println(ztime); // 2022-06-02T13:52:25.675228+09:00[Asia/Seoul]
System.out.println(ztimeRome); // 2022-06-02T06:52:25.675258+02:00[Europe/Rome]
//+9:00, +2:00 은 시차기준인 그리니치시 대비 시차를 의미함.
System.out.println(ztime.getZone()); //Asia/Seoul
System.out.println(ztime.getOffset()); //+09:00
7. 마치며
- 자바 8 이전 버전에서 제공하는 기존의 java.util.Date 클래스와 관련 클래스에서는 여러 불일치, 가변성, 기본값, 잘못된 이름 결정등의 설계 결함이 존재했다.
- 새로운 날짜와 시간 API 에서 날짜와 시간 객체는 모두 불변히다.
- 날짜와 시간 객체를 절대적인 방법과 상대적인 방법으로 처리할 수 있으며, 기존 인스턴스를 변경 하지 않도록 처리 결과로 새로운 인스턴스가 생성된다.
- TemporalAdjuster 를 이용하면 단순히 값을 바꾸는것 이상의 복잡한 동작을 수행 할 수 있다.
- 날짜와 시간 객체를 특정 포맷으로 출력하고, 파싱하는 포매터를 정의할 수 있다. 포매터는 스레드 안정성을 보장한다.
- 특정 지역/장소에 상대적인 시간대를 구할 수 있으며, 이 시간대를 날짜와 시간 객체에 적용하여 지역화 할 수 있다.
'JAVA' 카테고리의 다른 글
[java8] 인터페이스의 변화 - 인터페이스 기본메소드, 스태틱메소드 (0) | 2022.06.13 |
---|---|
[java8] 함수형 인터페이스와 람다 - 메소드 레퍼런스 (0) | 2022.06.13 |
[java8] 함수형 인터페이스와 람다 (0) | 2022.06.13 |
[lombok] 롬복 - @Slf4j 로 log 출력 (0) | 2022.03.15 |
[JAVA]Comparator 를 이용한 배열, List 정렬 (0) | 2022.02.20 |