자바 8에 새로운 날짜와 시간 API가 생긴 이유
- 기존에 사용한 날짜/시간 클래스 : Date, Calendar/GregorianCalendar, SimpleDateFormat, ...
- Date인데 시간까지 다루고, 사실상 TimeStamp이다. => Date 클래스 이름이 명확하지 않다.
- 날짜 및 시간을 내가 지정하여 객체의 상태를 바꿀 수 있다. => Date 클래스는 mutable하다. => multi-Thread 환경에서 안전하게 사용하기 어렵다.
- 한 스레드가 객체를 사용하는 동안 다른 쓰레드가 이 자원에 접근하지 못하도록 막는다면 문제가 생기지 않는다.
- 멀티쓰레드 환경에서 한 쓰레드가 뮤터블한 객체를 사용하는동안, 다른 쓰레드가 접근하여 데이터를 다른 값으로 바꿀 가능성이 있다. => Thread safe 하지 않다.
- 버그 발생할 여지가 많다.
- month가 0부터 시작한다. 7월을 6으로 표현해야 하며 실수할 가능성이 높다.
- Constant Variable (Calendar.JULY)를 사용하여 표현해야 실수가 적다.
- year, month, dayOfMonth등의 값을 int로 받는데, 이는 -100도 받을 수 있다는 뜻이다. => Type safe하지 않다.
- Type safe하려면 enum으로 허용된 month값만 파라미터로 받을 수 있도록 설정해야한다.
- 날짜 시간 처리가 복잡한 애플리케이션에서는 보통 Joda Time 라이브러리를 쓰곤했는데, 이 기능들을 JSL-310이라는 스펙으로 표준화하여 자바 스탠다드 라이브러리에 들어오게 되었다.
- month가 0부터 시작한다. 7월을 6으로 표현해야 하며 실수할 가능성이 높다.
자바 8의 Date-Time API 디자인 철학
- Clear : API가 명확해야 한다.
- Fluent : null을 리턴하거나 받는 것이 없기 때문에 계속 .을 찍으며 이어갈 수 있고, 더 유연해진다.
- Immutable : 값이 변경되지 않도록 한다.
- Extensible : 그레고리안 달력 외에도 다른 캘린더까지도 지원하며 새로운 달력 시스템을 만들 수 도 있다.
주요 API 특징
- 기계용 시간 (machine time)
- EPOCK (1970년 1월 1일 0시 0분 0초)부터 현재까지의 타임스탬프를 표현한다. ex)1592779348765
- 인류용 시간 (human time)
- 우리가 흔히 사용하는 연,월,일,시,분,초 등을 표현한다.
- 타임스탬프는 Instant를 사용한다.
- 특정 날짜(LocalDate), 시간(LocalTime), 일시(LocalDateTime)를 사용할 수 있다.
- 기간을 표현할 때는 Duration (시간 기반)과 Period (날짜 기반)를 사용할 수 있다.
- DateTimeFormatter를 사용해서 일시를 특정한 문자열로 포매팅할 수 있다.
특정 일시 표현
Instant instant = new Instant.now(); //UTC/GMT 기준
System.out.println(instant); //2020-06-21T23:53:33.110587Z
LocalDateTime now = LocalDateTime.now(); //서버의 시스템 zone 기준
System.out.println(now); //2020-06-21T16:53:33.382303
LocalDateTime birthday = LocalDateTime.of(1997, Month.FEBRUARY, 10, 4, 30, 0); //원하는 날짜, 시간으로 만들 수 있다.
Instant.now()는 기본적으로 UTC/GMT 기준으로 시간이 출력된다. 항상 같은 기준으로 시간이 출력되기 때문에 주로 시간을 재거나 메소드 실행 시간을 비교할 때 사용한다.
반대로 LocalDateTime.now()는 현재 서버의 시스템 zone 정보를 기준으로 시간이 출력된다. 내가 로컬에서 돌릴 때에는 한국 시간대이지만, 서버에 올라간 뒤에는 다르게 보일 수 있다.
ZoneId zone = ZoneId.systemDefault(); //시스템에 등록된 로컬 기준 확인
System.out.println(zone); //America/Los_Angeles
ZonedDateTime zonedDateTime = instant.atZone(); //로컬 기준의 시간으로 출력
System.out.println(zonedDateTime); //2020-06-21T16:55:08.549881-07:00[America/Los_Angeles]
ZonedDateTime nowInKorea = ZonedDateTime.now(ZoneId.of("Asia/Seoul")); //지정한 zone 기준의 시간으로 출력
System.out.println(nowInKorea); //2020-06-22T08:53:20.301361a+03:00[Asia/Seoul]
ZonedDateTime과 LocalDateTime, Instant는 서로 변환이 가능하다.
기간 표현
//period
LocalDate today = LocalDate.now();
LocalDate nextYearBirthday = LocalDate.of(2025, Month.FEBRUARY, 10);
Period period = Period.between(today, nextYearBirthday);
System.out.println(period.getDays()); //21
Period until = today.until(nextYearBirthday);
System.out.println(until.get(ChronoUnit.DAYS)); //21
period는 두 날짜를 비교하 개념 (사람용 기간 비교)
//duration
Instant now = Instant.now();
Instant plus = now.plus(10, ChronoUnit.SECONDS);
Duration between = Duration.between(now, plus);
System.out.println(between.getSeconds()); //10
duration은 두 인스턴스를 비교하는 개념 (기계용 기간 비교)
파싱/포매팅
//formatting
LocalDateTime now = LocalDateTime.now();
System.out.println(now); //2020-06-21T17:13:34.808073
DateTimeFormatter formatter1 = DateTimeFormatter.ISO_LOCAL_TIME;
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("MM/dd/yyyy");
System.out.println(now.format(formatter1)); //2020-06-21
System.out.println(now.format(formatter2)); //06/21/2020
LocalDateTime을 문자로 출력할 때 원하는 포맷으로 출력되도록 세팅한다.
//parsing
LocalDate date = LocalDate.parse("02/10/1997", DateTimeFormatter.ofPattern("MM/dd/yyyy"));
System.out.println(date); //1997-02-10
레거시 API 지원
java 8의 API와 기존(8 이전) API와 호환 가능하다.
Date date = new Date(); //asis
Instant instant = date.toInstant(); //asis를 tobe로 호환
Date newDate = Date.from(instant); //tobe를 asis로 호환
GregorianCalendar cal = new gregorianCalendar(); //asis
LocalDateTime now = cal.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); //asis를 tobe로 호환
GregorianCalendar newCal = GregorianCalendar.from(now); //tobe를 asis로 호환
ZoneId zoneId = TimeZone.getTimeZone("PST").toZoneId(); //asis를 tobe로 호환
TimeZone timeZone = TimeZone.getTimeZone(zoneId); //tobe를 asis로 호환
더 공부해보자
- ChronoUnit
이 포스팅은 더 자바, Java 8 강의를 수강하며 작성되었습니다.
더 자바, Java 8 강의 | 백기선 - 인프런
백기선 | 자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합
www.inflearn.com
'Java' 카테고리의 다른 글
[Java8] Executors (0) | 2024.08.28 |
---|---|
[Java8] Concurrent Programming (0) | 2024.08.27 |
[Java8] Optional API (0) | 2024.08.05 |
[Java8] Optional (0) | 2024.08.01 |
[Java8] Stream API (0) | 2024.07.31 |