java.time패키지
java.time | 날짜와 시간을 다루는데 필요한 핵심 클래스들을 제공 |
java.time.chrono | 표준(ISO)이 아닌 달력 시스템을 위한 클래스들을 제공 |
java.time.format | 날짜와 시간을 파싱하고, 형식화하기 위한 클래스들을 제공 |
java.time.temporal | 날짜와 시간의 필드(field)와 단위(unit)을 위한 클래스들을 제공 |
java.time.zone | 시간대(time-zone)와 관련된 클래스들을 제공 |
해당 패키지의 가장 큰 특징은 String클래스처럼 '불변(immutable)'이라는 것이다. 그래서 날짜나 시간을 변경하는 메서드들은 기존의 객체를 변경하는 대신 항상 변경된 새로운 객체를 반환한다. (기존 Calendar클래스는 변경 가능하므로, 멀티 쓰레드 환경에서 안전하지 못하다.)
멀티 쓰레드 환경에서는 동시에 여러 쓰레드가 같은 객체에 접근할 수 있기 때문에, 변경 가능한 객체는 데이터가 잘못될 가능성이 있으며, 이를 쓰레드에 안전(thread-safe)하지 않다고 한다.
LocalDate클래스와 LocalTime클래스
java.time패키지의 가장 기본이 되는 클래스로 나머지 클래스는 이들의 확장이다.
객체의 생성은 다음과 같이 한다.
now() : '현재'의 날짜 또는 시간으로 객체 생성
of() : '지정된' 날짜와 시간으로 객체 생성
-필드 값 가져오기 get()
int get(TemporalField field) long getLong(TemporalField field) |
해당 날짜 객체의 명시된 필드의 값을 int형이나 long형으로 반환함. |
int getYear() | 해당 날짜 객체의 연도(YEAR) 필드의 값을 반환함. |
Month getMonth() | 해당 날짜 객체의 월(MONTH_OF_YEAR) 필드의 값을 Month 열거체를 이용하여 반환함. |
int getMonthValue() | 해당 날짜 객체의 월(MONTH_OF_YEAR) 필드의 값을 반환함. (1~12) |
int getDayOfMonth() | 해당 날짜 객체의 일(DAY_OF_MONTH) 필드의 값을 반환함. (1~31) |
int getDayOfYear() | 해당 날짜 객체의 일(DAY_OF_YEAR) 필드의 값을 반환함. (1~365, 윤년이면 366) |
DayOfWeek getDayOfWeek() | 해당 날짜 객체의 요일(DAY_OF_WEEK) 필드의 값을 DayOfWeek 열거체를 이용하여 반환함. |
int get(TemporalField field) long getLong(TemporalField field) |
해당 시간 객체의 명시된 필드의 값을 int형이나 long형으로 반환함. |
int getHour() | 해당 시간 객체의 시(HOUR_OF_DAY) 필드의 값을 반환함. |
int getMinute() | 해당 시간 객체의 분(MINUTE_OF_HOUR) 필드의 값을 반환함. |
int getSecond() | 해당 시간 객체의 초(SECOND_OF_MINUTE) 필드의 값을 반환함. |
int getNano() | 해당 시간 객체의 나노초(NANO_OF_SECOND) 필드의 값을 반환함. |
-필드값 변경하기 with() / plus() / minus()
필드를 변경하는 메서드들은 항상 새로운 객체를 생성해서 반환하므로 대입 연산자를 같이 사용해야한다는 것을 잊지 말자.
with() : 원하는 필드를 직접 지정
LocalDate with(TemporalField field, long newValue) | 해당 날짜 객체에서 특정 필드를 전달된 새로운 값으로 설정한 새로운 날짜 객체를 반환함. |
LocalDate withYear(int year) | 해당 날짜 객체에서 연도(YEAR) 필드를 전달된 새로운 값으로 설정한 새로운 날짜 객체를 반환함. |
LocalDate withMonth(int month) | 해당 날짜 객체에서 월(MONTH_OF_YEAR) 필드를 전달된 새로운 값으로 설정한 새로운 날짜 객체를 반환함. |
LocalDate withDayOfMonth(int dayOfMonth) | 해당 날짜 객체에서 일(DAY_OF_MONTH) 필드를 전달된 새로운 값으로 설정한 새로운 날짜 객체를 반환함. |
LocalDate withDayOfYear(int dayOfYear) | 해당 날짜 객체에서 DAY_OF_YEAR 필드를 전달된 새로운 값으로 설정한 새로운 날짜 객체를 반환함. |
LocalTime with(TemporalField field, long newValue) | 해당 시간 객체에서 특정 필드를 전달된 새로운 값으로 설정한 새로운 시간 객체를 반환함. |
LocalTime withHour(int hour) | 해당 시간 객체에서 시(HOUR_OF_DAY) 필드를 전달된 새로운 값으로 설정한 새로운 시간 객체를 반환함. |
LocalTime withMinute(int minute) | 해당 시간 객체에서 분(MINUTE_OF_HOUR) 필드를 전달된 새로운 값으로 설정한 새로운 시간 객체를 반환함. |
LocalTime withSecond(int second) | 해당 시간 객체에서 초(SECOND_OF_MINUTE) 필드를 전달된 새로운 값으로 설정한 새로운 시간 객체를 반환함. |
LocalTime withNano(int nanoOfSecond) | 해당 시간 객체에서 나노초(NANO_OF_SECOND) 필드를 전달된 새로운 값으로 설정한 새로운 시간 객체를 반환함. |
plus() : 원하는 필드를 지정값 만큼 증가시킴
minus() : 원하는 필드를 지정값 만큼 감소시킴
-날짜와 시간 비교 isAfter() / isBefore() / isEqual()
LocalDate와 LocalTime 클래스에도 객체를 비교할 수 있는 compareTo() 메소드가 오버라이딩되어 있다. 하지만 더욱 편리하게 날짜와 시간 객체를 서로 비교할 수 있도록 다음과 같은 메소드를 제공한다.
isEqual() : equals() 메소드와는 달리 오직 날짜만을 비교함 (LocalDate 클래스에서만 제공)
isBefore() : 두 개의 날짜와 시간 객체를 비교하여 현재 객체가 명시된 객체보다 앞선 시간인지를 비교함
isAfter() : 두 개의 날짜와 시간 객체를 비교하여 현재 객체가 명시된 객체보다 늦은 시간인지를 비교함
Instant클래스
Instant는 에포크 타임부터 경과된 시간을 나노초 단위로 표현한다.
사람에겐 불편하겠지만, 단일 진법으로만 다루기 때문에 계산하기 쉽다.
Instant 사용법
생성 방법은 now()와 ofEpochSecond()를 사용한다.
필드값을 가져올 때는 getEpochSecond() 또는 getNano()를 사용한다.
Instant는 항상 UTC를 기준으로 하기 때문에, LocalTime과 차이가 있을 수 있다. 시간대를 고려해야하는 경우 OffsetDateTime을 사용하는 것이 더 나은 선택일 수 있다.
Instant와 Date간의 변환
Instant는 기존의 java.util.Date를 대체하기 위한 것이며, JDK1.8부터 Date에 Instant로 변환할 수 있는 새로운 메서드가 추가되었다.
static Date = from(Instant instant) // Instant -> Date
Instant toInstant() // Date -> Instant
LocalDateTime클래스와 ZonedDateTime클래스
LocalDate와 LocalTime을 합쳐 놓은 것이 LocalDateTime이고,
LocalDateTime에 시간대(time zone)을 추가한 것이 ZonedDateTime이다.
LocalDate + LocalTime -> LocalDateTime
LocalDateTime + 시간대 -> ZonedDateTime
LocalDate와 LocalTime으로 LocalDateTime만들기
LocalDate date = LocalDate.of(2021,8,13);
LocalTime time - LocalTime.of(23,2,21);
LocalDateTime dt = LocalDateTime.of(date,time);
LocalDateTime dt2 = date.atTime(time);
LocalDateTime dt3 = time.atDate(date);
...
LocalDateTime dt4 = date.atStartOfDay();
물론 LocalDateTime에도 날짜와 시간을 직접 지정할 수 있는 다양한 버젼의 of()와 now()가 정의되어있다.
그 반대로 LocalDateTime을 LocalDate또는 LocalTime으로 변환할 수 있다.
LocalDateTime dt = LocalDateTime.of(2021,8,13,9,00,00);
LocalDate date = dt.toLocalDate();
LocatTime time = dt.toLocalTime();
LocalDateTime으로 ZonedDateTime 만들기
LocalDateTime에 시간대(time-zone)을 추가하면, ZonedDateTime이 된다.
이 때 시간대는 ZoneId라는 클래스를 사용하는데, ZoneId는 일광 절약시간(DST)을 자동적으로 처리해주므로 편리하다.
LocalDateTime에 atZone()으로 시간대 정보를 추가하면, ZonedDateTime을 얻을 수 있다.
ZoneId zid = ZoneId.of("Asia/seoul");
ZonedDateTime zdt = dateTime.atZone(zid);
ZonedDateTime도 LocalDateTime처럼 날짜와 시간에 관련된 다른 클래스로 변환하는 메서드들을 가지고 있다.
LocalDate toLocalDate()
LocalTime toLocalTime()
LocalDateTime toLocalDateTime()
OffsetDateTime toOffsetDateTime()
long toEpochSecond()
Instant toInstant()
OffsetDateTime
ZonedDateTime은 ZonedId로 구역을 표현하는데, ZoneId가 아닌 ZoneOffset(UTC로부터 얼마만큼 떨어져 있는지를 ZoneOffSet으로 표현한다. 서울은 '+9'이다.)을 사용하는 것이 OffsetDateTime이다.
ZoneId는 일광절약시간처럼 시간대와 관련된 규칙들을 포함하고 있는데에 반해
ZoneOffset은 단지 시간대를 시간의 차이로만 구분하기 때문에 일관된 시간체계를 유지할 수 있다.
ZonedDateTime zdt = ZondedDateTime.of(date, time, zid);
OffsetDateTime odt = offsetDateTime.of(date, time, krOffset);
TemporalAdjusters
앞서 plus(), minus()와 같은 메서드로 날짜와 시간을 계산할 수 있었다. 하지만 지난 주 토요일이 며칠인지, 또는 이번 달의 3번째 금요일은 며칠인지와 같은 날짜계산을 plus(), minus()로 하기엔 불편함이 있다.
이같은 표현처럼 자주 쓰일만한 날짜 계산들을 대신 해주는 메서드를 정의해놓은 것이 TemporalAdjusters클래스이다.
LocalDate today = LocalDate.now();
LocalDate nextMoney = 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번째 요일 |
Period와 Duration
Period는 날짜의 차이를, Duration은 시간의 차이를 계산하기 위한 것이다.
날짜 - 날짜 = Period
시간 - 시간 = Duration
-between()
두 날짜의 차이를 나타내는 Period와 Duration은 between()으로 얻을 수 있다.
LocalDate date1 = LocalDate.of(2021, 8, 13);
LocalDate date1 = LocalDate.of(2023, 2, 21);
Period pe = Period.between(date1, date2);
LocalTime time1 = LocalTime.of(00, 00, 00);
LocalTime time2 = LocalTime.of(09, 10, 20);
Duration du = Duration.between(time1, time2);
-get()
Period, Duration에서 특정 값을 얻을 때는 get()을 사용한다.
long year = pe.get(ChronoUnit.YEARS);
long month = pe.get(ChronoUnit.MONTHS);
long day = pe.get(ChronoUnit.DAYS);
long sec = du.get(ChronoUnit.SECONDS);
long nano = du.get(ChronoUnit.NANOS);
그런데, Duration에는 getHours(), getMinites()같은 메서드가 없기때문에 따로 계산을 통해 얻을 수 있다.
-untill()
until()은 between()과 거의 같은 일을 한다. 단지 between()은 staic메서드이고, until()은 인스턴스 메서드라는 차이만 있다.
Period는 년월일을 분리해서 저장하기 때문에, D-day를 구하려는 경우에는 두 개의 메개변수를 받는 until()을 사용하는 것이 낫다.
날짜가 아닌 시간에도 until()을 사용할 수 있지만, Duration을 반환하는 until()은 없다.
Period pe = today.until(myBirthday)
long dday = today.until(myBirthday, ChronoUnit.DAYS);
-of(), with()
사용법은 앞서 LocalDate와 LocalTime과 같다.
ofXX()를 통해 값을 설정할 수 있고, whithXX()를 통해 값을 변경할 수 있다.
-사칙연산, 비교연산, 기타 메서드
plus(), minus()외에 곱셈과 나눗셈을 위한 메서드도 있다.
음수인지 확인하는 isNegative()와 0인지 확인하는 isZero()도 있다. 이러한 메서드를 이용해 두 날짜 또는 시간을 비교할 때, 어느 쪽이 앞인지 또는 같은지를 알 수 있다.
그 외에도 부호를 반대로 변경하는 negate(), 부호를 없애는 abs(), Period에 월(month)의 값이 12를 넘지 않도록 변경해주는 normalized()등 다양한 메서드가 존재한다.
-다른 단위로 변환 toXXX()
이름이 'to'로 시작하는 메서드들이 있는데, 이 들은 Period와 Duration을 다른 단위의 값으로 변환하는데 사용된다.
get()은 특정 필드의 값을 그대로 가져오는 것이지만, 아래의 메서드들은 특정 단위로 변환한 결과를 반환한다는 차이가 있다.
파싱과 포맷
형식화(formatting)와 관련된 클래스들은 java.time.format패키지에 들어있는데, 이 중에서 DateTimeFormatter가 핵심이다.
이 클래스에는 자주 쓰이는 다양한 형식들을 기본적으로 정의하고 있으며, 그 외의 형식이 필요하다면 직접 정의해서 사용할 수 있다.
LocalDate date = LocalDate.of(2021, 8, 13);
String yyyymmdd = DateTimeFormatter.ISO_LOCAL_DATE.format(date);
String yyyymmdd = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
날짜와 시간의 형식화에는 위와 가이 format()이 사용되는데, 이 메서드는 DateTimeFormatter뿐만 아니라 LocalDate나 LocalTime같은 클래스에도 같은 기능을 하니 상황에 따라 편한 쪽을 선택할 수 있다.
ofLocalizedDate(dateStyle) | Formatter with date style from the locale | '2011-12-03' |
ofLocalizedTime(timeStyle) | Formatter with time style from the locale | '10:15:30' |
ofLocalizedDateTime(dateTimeStyle) | Formatter with a style for date and time from the locale | '3 Jun 2008 11:05:30' |
ofLocalizedDateTime(dateStyle,timeStyle) | Formatter with date and time styles from the locale | '3 Jun 2008 11:05' |
BASIC_ISO_DATE | Basic ISO date | '20111203' |
ISO_LOCAL_DATE | ISO Local Date | '2011-12-03' |
ISO_OFFSET_DATE | ISO Date with offset | '2011-12-03+01:00' |
ISO_DATE | ISO Date with or without offset | '2011-12-03+01:00'; '2011-12-03' |
ISO_LOCAL_TIME | Time without offset | '10:15:30' |
ISO_OFFSET_TIME | Time with offset | '10:15:30+01:00' |
ISO_TIME | Time with or without offset | '10:15:30+01:00'; '10:15:30' |
ISO_LOCAL_DATE_TIME | ISO Local Date and Time | '2011-12-03T10:15:30' |
ISO_OFFSET_DATE_TIME | Date Time with Offset | 2011-12-03T10:15:30+01:00' |
ISO_ZONED_DATE_TIME | Zoned Date Time | '2011-12-03T10:15:30+01:00[Europe/Paris]' |
ISO_DATE_TIME | Date and time with ZoneId | '2011-12-03T10:15:30+01:00[Europe/Paris]' |
ISO_ORDINAL_DATE | Year and day of year | '2012-337' |
ISO_WEEK_DATE | Year and Week | 2012-W48-6' |
ISO_INSTANT | Date and Time of an Instant | '2011-12-03T10:15:30Z' |
RFC_1123_DATE_TIME | RFC 1123 / RFC 822 | 'Tue, 3 Jun 2008 11:05:30 GMT' |
로케일에 종속된 형식화
DateTimeFormatter의 static메서드
- ofLocalizedDate()
- ofLocalizedTime()
- ofLocalized DateTime()
은 로케일(locale)에 종속된 포맷터를 생성한다.
출력형식 직접 정의하기
DateTimeFormatter의 ofPattern()으로 원하는 출력형식을 직접 작성할 수도 있다.
DateFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
공식 문서에 나와있는 출력 형식에 사용되는 기호의 목록이다.
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use
문자열을 날짜와 시간으로 파싱하기
문자열을 날짜 또는 시간으로 변환하려면 static메서드 parse()를 사용한다.
parse()는 오버로딩된 메서드가 여러 개 있는데, 그중에서 다음의 2개가 자주 쓰인다.
static LocalDateTime parse(CharSequence text)
static LocallDateTime parse(CharSequance text, DateTimeFormatter formatter)
DateTimeFormatter에 상수로 정의된 형식을 사용할 때는 다음과 같이 한다.
LocalDate date= LocalDate.parse("2016-01-02", DateTimeFormatter.ISO_LOCAL_DATE);
자주 사용되는 기본적인 형식의 문자열은 ISO_LOCAL_DATE와 같은 형식화 상수를 사용하지 않고도 파싱이 가능하다.
LocalDate newDate = LocalDate.parse("2001-01-01");
LocalTime newTime = LocalTime.parse("23:59:59);
LocalDateTime newDateTime = LocalDateTime.parse("2001-01-01T23:59:59);
다음과 같이 ofPattern()을 이용해서 파싱을 할 수도 있다.
DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime endOfYear = LocalDateTime.parse("2015-12-31 23:59:59", pattern);
'Java > 날짜와 시간 & 형식화' 카테고리의 다른 글
[Java] 형식화 클래스(Format클래스) - 코딩밥상 (0) | 2023.02.21 |
---|---|
[Java] Calendar와 Date 클래스 - 코딩밥상 (0) | 2023.02.20 |