Date
와 Calendar
가 가진 문제점을 해결하기 위해서 JDK.18부터 java.time
패키지가 추가되었다.
이안에는 총 4개의 하위 패키지로 구성되어 있다.
java.time
- 날짜와 시간을 다루는데 필요한 핵심 클래스들을 제공
java.timechrono
- 표준(ISO)이 아닌 달력 시스템을 위한 클래스들을 제공
java.time.temporal
- 날짜와 시간을 파싱하고, 형식화하기 위한 클래스들을 제공
java.time.zone
- 시간대와 관련된 클래스들을 제공
Java.time패키지의 핵심 클래스
java.time
패키지의 특징으로는 날짜와 시간을 별도의 클래스로 분리해 놓았다.
그래서 다음과 같이 사용한다.
- 시간 :
LocalTime
클래스- 시간 - 시간 (기간) :
Duration
클래스
- 시간 - 시간 (기간) :
- 날짜 :
LocalDate
클래스- 날짜 - 날짜 (기간) :
Period
클래스
- 날짜 - 날짜 (기간) :
- 시간 + 날짜 :
LocalDateTime
클래스 - 시간대 + 시간 + 날짜 :
ZonedDateTime
이러한 클래스들을 사용하기 위해 객체를 생성해야하는데 생성하는 법은 다음과 같다.
LocalDate date = LocalDate.now(); // new를 사용하지 않음
LocalDate date = LocalDate.of(2021,09,18);
이런식으로 now()
와 of()
를 사용해서 객체를 생성할 수 있다. LocalDate
를 예로 들었지만 다른 클래스들도 이런방식으로 생성하면 된다.
이러한 now()
와 of()
는 static
메서드이다.
인터페이스의 종류
Temporal과 TemporalAmount
Temporal
은 보통 날짜와 시간을 위한것이라고 생각하면 좋다.
Temporal
,TemporalAccessor
,TemporalField
를 구현한 클래스LocalDate
,LocalTime
,LocalDateTime
,ZonedDateTime
,Instant
...
TemporalAmount
를 구현한 클래스Period
,Duration
TemporalUnit과 TemporalField
TemporalUnit
: 날짜와 시간의 단위를 정의해 놓은 인터페이스, 열거형ChronoUnit
으로 구성되어 있다.TemporalField
: 년, 월, 일 등 날짜와 시간의 필드를 정의해 놓은 것, 이것도 열거형ChronoField
가 이 인터페이스를 구성하였다.
다음과 같은 예시를 보면 이해가 빠르다. 다음 예시는 특정 필드의 값을 얻기위한 매서드인 get()
과 날짜와 시간을 빼거나 더하는 plus()
에 대한 정의 이다.
int get(TemporalField field)
LocalDate plus(long amountToAdd, TemporalUnit, unit);
이를 TemporalField
인지 TemporalUnit
에서 사용할 수 있는 열거형인지 판단하는 방법은 다음과 같다.
boolean isSupported(TemporalField field);
boolean isSupported(TemporalUnit unit);
LocalDate와 LocalTime
LocalDate
와 LocalTime
은 날짜와 시간을 나타낸다.
이러한 값들을 생성하는 of()
와 now()
메서드는 static
메서드로 특히 of()
에는 다양한 매개변수가 오버로딩 되어있다.
LocalTime
을 예로 들면 시간, 분, 초 뿐만이 아니라 나노초도 포함이 된다.
또한, 이들의 특징으로는 LocalTime
을 예로들자면 초만 단위로 지정해서 넣어줄 수 도 있다.
LocalTime time = LocalTime.ofSecondDay(86399); // 23시 59분 59초
또한, 시간과 표를 나타내는 것을 여러가지 방법이 있을 텐데 parse()
메서드를 통해서 문자열을 날짜로 변환할 수 있다.
LocalDate date = LocalDate.parse("1999-03-16"); // 1999년 3월 16일
LocalTime time = LocalTime.parse("23:59:59); // 23시 59분 59초
get메서드
보통 Java에서는 특정 필드를 가져올 때는 get()
특정필드를 수정하거나 값을 변경할 때는 set()
을 사용한다.
물론 LocalDate
와 LocalTime
에도 각 시간, 분, 초 이러한 단위들을 가져오는 get()
함수들이 다량 존재한다.
인텔리제이나, 이클립스같은 IDE의 기능을 사용하여 살펴보자.
몇개만 소개하자면 다음과 같다.
LocalDate date = LocalDate.now();
date.getYear() // int형 년도 반환
date.getMonthValue() // Month형 월 반환
date.getMonth() // int형 월 반환
....
LocalTime time = LocalTime.now();
time.getHour()// int형 시간 반환
time.getMinute() // int형 분 반환
time.getSecond() // int형 초 반환
time.getNano() // int형 나노초 반환
지금은 get()
만 사용했지만 기간이 너무 많거나 int형으로 표현이 안되는 값들은 getLong()
을 사용하여 반환받는 것이 맞다.
ChronoField
이러한 get()
을 하려할 때 메서드들의 매개변수로 사용할 수 있는 필드의 목록이다.
이는의 예시들을 다음과 같이 있다.
ERA // 시대
YEAR_OF_ERA , YEAR // 년
DAY_OF_WEEK // 요일 (1: 월요일, 2: 화요일)
MICRO_OF_SECOND * // 백만분의 일초
...
이러한 필드가 여러개가 있다. 사용할 때 주의해야할 점은
LocalDate
는 날짜인데 사용하기 초를 나타내는 필드를 사용할거나 이러면 사용할 수 없다.*
이 표시되어 있는 필드는 int타입을 넘어가는 필드 이므로getLong()
을 사용해야한다.
필드의 값 변경하는법
필드의 값을 변경하기 위한 메서드로는 with()
, plus()
, minus()
들이 존재한다.
날짜와 시간에서 특정 필드 값을 변경하려면, 다음과 같이 with
로 시작하는 메서드를 사용하면 된다.
이러한 메서드들은 보통 새로운 객체를 생성해서 반환므로 대입연산자를 사용해야한다.
date = date.withYear(2000); // 년도를 2000년으로 변경한다.
time = time.withHour(12); // 시간을 12시로 변경
truncatedTo()메서드
이 메서드는 LocalTime
에만 존재하며 LocalDate()
는 존재하지 않는다.
왜냐하면 truncatedTo()
는 짖ㅇ된 것보다 작은 단위의 필드를 모두 0으로 만드는 것이다.
LocalTime time = LocalTime.of(18, 12, 19); // 18시 12분 19초
time = time.turncatedTo(ChronoUnit.HOURS); // 18시 00분 00초가 된다.
이러한 기능이기 때문에 LocalDate()
에서 만약 년도를 기준으로한다면 월,일이 0이되는데
0인 월, 일은 없기 때문이다.
날짜와 시간의 비교
LocalDate
와 Localtime
의 날짜, 시간을 비교할 때는 compareTo()
를 이용하면 되지만 그것보다 편리하게 비교할 수 있는 메서드 들이 존재한다.
boolean isAfter (ChronoLocalDate other);
boolean isBefore (ChronoLocalDate other);
boolean isEqual (ChronoLocalDate other);
이중 isEqual
메서드는 LocalDate
에만 존재한다.
또한, isEqual
은 오직 날짜만 비교한다. 한마디로 2018년 12월 19일이나 2019년 12월 19일은 같다는 것이다.
equals()
는 모든 필드가 일치해야한다. 그러므로 2018년 12월 19일은 2019년 12월 19일은 다르다.
Instant
instant
는 에포크 타임으로부터 경과된 시간을 나노초 단위로 표현한다.
이는 우리가 보기는 어렵다. (ex 1970-01-01 00:00:00 UTC)
하지만 단일 진법으로만 다루기 때문에 계산이 편하다.
생성하는 방법은 두가지 방법이있다. now()
를 사용하는 것과 ofEpochSecond()
를 사용하는 것과 같다.
Instant now = Instant.now();
Instant now = Instant.ofEpochSecond(now.getEposchSeocnd());
Instant now = Instant.ofEpochSecond(now.getEpochSecond(), now.getNano());
이러한 Instant
는 Date
로 변환되거 반대의경우고 다음과 같은 메서들ㄹ 통해 변환할 수 있다.
from(Instant instant); // Instant -> Date
toInstant() // Date -> Instant
LocalDateTime과 ZonedDateTime
LocalDateTime
과 ZonedDateTime
은 무언가 둘다 합쳐놓은 것이다.
LocalDateTime
:LocalDate
+LocalTime
ZonedDateTime
:LoclaDateTime
+ 시간대
이렇게 구성이 되어있지만 이를 operand들을 합쳐서 결과를 생성할 있다.
물론 그냥 바로 생성해도 되지만 예를들자면 LocalDateTime
으로 바꾸려고하는데 LocalDate
는 현재 생성이 되있다면
LocalDate date = LocalDate.of(2018,12,19);
LocalDateTime dateTime = date.atTime(18,12,19);
이런 방식으로도 생성할 수 있고 물론 반대도 존재하고 혹은 아무것도 없을 때, 아니면 둘다 있을 때 도 생성이 가능하다 여러가지 방법이 있으니까 참고하자
LocalDate date = LocalDate.of(2018, 12, 19);
LocalTime time = LocalTime.of(99, 03, 16);
LocalDateTime dt = LocalDateTime.of(date, time);
LocalDateTime dt1 = date.atTime(time);
LocalDateTime dt2 = time.atDate(date);
LocalDateTime dt3 = date.atTime(12, 8, 14);
LocalDateTime dt4 = time.atDate(LocalDate.of(2018, 8, 14));
LocalDateTime dt5 = date.atStartOfDay();
반대로 LocalDateTime
을 LocalDate
또는 LocalTime
으로 변환할 수 있다.
LocalDateTime dt = LoclaDateTime.of(2018,12,19,99,3,16);
LocalDate date = dt.toLocalDate();
LocalTime time = dt.toLocalTime();
이를 LocalDateTime
으로 ZonedDateTime
으로 만들 수 있다.
왜냐하면 LocalDateTime
+ 시간대가 ZonedDateTime
이기 때문이다.
LocalDateTime dt = LoclaDateTime.of(2018,12,19,12,3,16);
ZoneId zid = ZoneId.of("Asia/Seoul");
ZonedDateTime zdt = dt.atZone(zid); // 2018-12-19T12:03:16+09:00[Asia/Seoul]
이런식으로 atZone
을 사용하여 시간대 정보를 넣어서 만들 수 있다.
이러한 ZonedDateTime
을 이용하여 특정 시간대의 시간을 알기 위해서는 다음과 같이 하면된다.
ZoneId nyId = ZoneId.of("America/New_York");
ZonedDateTime nyTime = ZonedDateTime.now().withZoneSameInstant(nyId);
TemporalAdjusters
이를 사용하면 특정 날짜 계산을 사용할 수 있다.
plus()
, minus()
를 사용해서도 계산 할 수 있지만 특정년도의 3주차 월요일 이런것들을 물어보면 계산하기 복잡하다 이러한 TemporalAdjusters
클래스를 용하여 편하게 계산 할 수 있다.
LocalDate today = LocalDate.now();
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
위와 같은 방식들을 사용해서 계산할 수 있다. 사용할 수 있는 것들은 다음것들 등이 있다.
firstDayOfNextYear() // 다음해의 첫 날
lastDayForYear() // 올 해의 마지막 날
firstInMonth (DayofWeek dayOfWeek) // 이번 달의 첫 번째 ? 요일
next (DayofWeek dayOfWeek) // 다음 ?요일(당일 미포함)
...
이러한 다양한 메소드들이 존재한다.
TemporalAdjusters직접 구현하기
이러한 TemporalAdjusters
를 직접 구현할 수도 있다.
TemporalAdjuster
인터페이스를 사용하면 쉽게 구현할 수 있다.
이 클래스는 다음과 같은 추상 메서드 하나만 정의되어 있고, 이 메서드만 구현하면 된다.
@FuntionalInterface
public interface TemporalAdjuster{
Temporal adjustInto(Temporal temporal);
}
이를 오버라이딩해서 자유롭게 구현하면 된다.
Period와 Duration
Period
는 날짜와의 차이를 나타내고
Duration
은 시간의 차이를 계산한다.
어떤 특정의 두 날짜와 시간을 구별하기 위해서는 between()
이라는 메소드를 사용하면 편하다.
LocalDate date1 = LocalDate.of(2018,12,19);
LocalDate date2 = LocalDate.now();
Period pe = Period.between(date1, date2);
Duration
과 Period
에서 특정 필드의 값을 얻기위해서는 LocalDate
나 LocalTime
과 마찬가지로 get()
을 사용한다.
하지만 이러한 값들을 Unit이나 Field를 가져올 때는 불안하거나 한정되어있다
그렇기 때문에 이를 안전하게 가져오기 위해서는 LocalTime
이나 LocalDate
로 변환해서 사용하는 것이 좋다.
이러한 Period
와 Duration
은 LocalTime
와 LocalDate
처럼
of()
나 with()
메서드를 사용할 수 있고 또한, plus()
처럼 사칙연산이나 isAfter()
처럼 비교연산을 하거나 다른단위로 변환할 수 있는 메서드들이 존재하니 참고하면 좋을 것 같다.
파싱과 포맷
여기서의 파싱은 날짜와 시간을 원하는 형식으로 출력하고 해석하는 방법이다.
내가 원하는 타입으로 format()
을 사용하여 형식을 정해줄 수도 있다.
이러한 종류는 다양하니 java api를 찾아보는 것도 좋을 것같다
문자열을 날짜와 시간으로 파싱하기
문자열을 날짜 또는 시간으로 변환하기 위해서는 static
메서드인 parse()
를 사용하면 가능하다.
이는 오버로딩된 메소드가 여러개가 존재한다. 예제를 보자
Local date = LocalDate.parse("2016-01-02", DateTimeFormatter.ISO_LOCAL_DATE);
DateTimeFormatter
에 저장된 형식을 사용하여 정의된 형식으로 출력할 수도 있다.
또한, ofPattern()
을 사용하여 파싱을 할 수도 있다.
ofPattern
을 사용하면 원하는 형식에 따라 날짜 시간 분을 읽어올 수 있다.
DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
Refernece
- 자바의 정석
'Java > Java' 카테고리의 다른 글
[Java/Study] 쓰레드 (1) (0) | 2021.09.24 |
---|---|
[Java/Study] 지네릭 타입 (0) | 2021.09.19 |
[Java/Study] 예외처리 (0) | 2021.09.14 |
[Java/Study] 내부 클래스 (0) | 2021.09.11 |
[Java/Study] 인터페이스 (0) | 2021.09.11 |