[Java] java.time패키지(1)
by Choi HyeSun
java.time패키지
-
날짜와 시간을 다루기 위한 Date와 Calendar 단점을 해소하기 위한 패키지(JDK 1.8~)
-
하위 패키지 4가지
- 패키지 수는 많지만 실제 사용되는 핵심 클래스들은 그리 많지 않음
-
특징 : 불변(immutable)
-
날짜나 시간을 변경하는 메서드들 : 기존의 객체 자체를 변경하는 대신 항상 변경된 새로운 객체를 반환
-
기존 Calendar클래스는 변경이 가능하므로, 멀티 쓰레드 환경에서 안전하지 못함
-
-
새로운 패키지가 도입되었음에도 불구하고, 앞으로도 기존에 작성된 프로그램과의 호환성 때문에 Date와 Calendar는 여전히 사용될 것
패키지 | 설명 |
---|---|
java.time | 날짜와 시간을 다루는데 필요한 핵심 클래스들을 제공 |
java.time.chrono | 표준(ISO)이 아닌 달력 시스템을 위한 클래스들을 제공 |
java.time.format | 날짜와 시간을 파싱하고, 형식화하기 위한 클래스들을 제공 |
java.time.temporal | 날짜와 시간의 필드(field)와 단위(unit)을 위한 클래스들을 제공 |
java.time.zone | 시간대(time-zone)와 관련된 클래스들을 제공 |
쓰레드에 안전(thread-safe)하지 않은 것
-
멀티 쓰레드 환경에서는 동시에 여러 쓰레드가 같은 객체에 접근할 수 있음
-
변경 가능한 객체는 데이터가 잘못될 가능성이 있음 → 쓰레드에 안전하지 않음
Calendar VS java.time패키지’s 클래스
-
Calendar : 날짜와 시간, 시간대를 하나로 표현
-
java.time : 날짜와 시간을 별도의 클래스로 구분
-
LocalDate : 날짜
-
LocalTime : 시간
-
LocalDateTime : 날짜 & 시간
-
ZonedDateTime : 날짜 & 시간 & 시간대(time-zone)
-
Date VS java.time패키지’s 클래스
-
Date
-
Instant : 타임스탬프
- 타임스탬프? 날짜와 시간을 초 단위(정확히는 나노초)로 표현한 것
날짜와 시간을 하나의 정수로 표현할 수 있으므로, 날짜와 시간의 차이를 계산하거나 순서를 비교하는데 유리
데이터베이스에 많이 사용
- 타임스탬프? 날짜와 시간을 초 단위(정확히는 나노초)로 표현한 것
java.time패키지의 핵심 클래스
-
날짜를 더 세부적으로 다룰 수 있는 클래스
- Year, YearMonth, MonthDay
-
날짜와 시간의 간격을 표현하기 위한 클래스
-
Period : 두 날짜 간의 차이 표현
-
Duration : 시간의 차이를 표현
-
java.time패키지’s 클래스 - now(), of()
-
java.time 패키지에 속한 클래스의 객체를 생성하는 가장 기본적인 방법
-
now() : 현재 날짜와 시간을 저장하는 객체를 생성
-
of() 단순히 해당 필드의 값을 순서대로 지정하면 됨
- 클래스마다 다양한 종류의 of()가 정의
-
예제
java.time패키지’s 인터페이스
-
Temporal, TemporalAccessor, TemporalAdjuster 인터페이스를 구현
-
날짜와 시간을 표현하기 위한 클래스
-
(예) LocalDate, LocalTime, LocalDateTime, ZonedDateTime
-
-
TemporalAmount 인터페이스를 구현
- Duration, Period
-
매개변수의 타입이 Temporal로 시작할 경우, 대부분 날짜와 시작을 위한 것
- TemporalAmount인지 아닌지만 확인할 것
-
temporal과 chrono의 의미가 모두 시간(time)인데도, time 대신 굳이 해당 단어를 사용한 이유는 시간(시분초)와 더 큰 개념의 시간(년월시분초)를 구분하기 위함임
-
TemporalUnit 인터페이스 : 날짜와 시간의 단위를 정의해 놓은 것
- 열거형 ChronoUnit : TemporaUnit을 구현한 것
-
TemporalField 인터페이스 : 년, 월, 일 등 날짜와 시간의 필드를 정의해 놓은 것
- 열거형 ChronoField : TemporalField 인터페이스를 구현
java.time패키지’s 메서드
-
get(), get으로 시작하는 이름의 메서드
-
날짜와 시간에서 특정 필드의 값만을 얻을 때 이용
-
get() : int get(TemporalField field)
-
-
plus() 또는 minus()와 함께 열거형 ChronoUnit 사용
-
특정 날짜와 시간에서 지정된 단위의 값을 더하거나 뺄 때
-
plus() : LocalDate plus(long amountToAdd, TemporalUnit unit)
-
-
TemporalField나 TemporalUnit을 사용할 수 있는지 확인하는 메서드
-
boolean isSupported(TemporalUnit unit) // Temporal에 정의
-
boolean isSupported(TemporalField field) // TemporalAccessor에 정의
-
LocalDate와 LocalTime
-
java.time패키지의 가장 기본이 되는 클래스
- 나머지 클래스들은 이들의 확장임
LocalDate와 LocalTime - 생성
-
객체를 생성하는 방법
-
static now() : 현재의 날짜와 시간을 LocalDate와 LocalTime으로 각각 변환
-
static of() : 지정된 날짜와 시간으로 LocalDate와 LocalTime객체를 생성
-
LocalDate와 LocalTime - get(), getXXX()
-
특정 필드의 값 가져오기
-
특정 필드의 값을 가져올 때는 아래의 표에 있는 메서드를 사용
-
주의) Calendar와 다름!
-
java.time : 월(month) 1~12, 요일(weekday) 월1~일7
-
Calendar : 월(month) 9~11, 요일(weekday) 일1~토7
-
-
LocalDate 클래스 메서드
메서드 | 설명(1999-12-31 23:59:59) |
---|---|
int getYear() | 년도(1999) |
int getMonthValue() | 월(12) |
Month getMonth() | 월(DECEMBER) getMonth().getValue() = 12 |
int getDayOfMonth() | 일(31) |
int getDayOfYear() | 같은 해의 1월 1일부터 N번째 일(365) |
DayOfWeek getDayOfWeek() | 요일(FRIDAY) getDayOfWeek().getValue() = 5 |
int lengthOfMonth() | 같은 달의 총 일수(31) |
int lengthOfYear() | 같은 해의 총 일수(365), 윤년이면(366) |
boolean isLeapYear() | 윤년여부 확인(false) |
- LocalTime 클래스 메서드
메서드 | 설명(1999-12-31 23:59:59) |
---|---|
int getHour() | 시(23) |
int getMinute() | 분(59) |
int getSecond() | 초(59) |
int getNano() | 나노초(0) |
-
위의 메서드 외에도 get(), getLong()이 있음
-
원하는 필드를 직접 지정 가능
-
int get(TemporalField field)
-
long getLong(TemporalField field) → int를 넘을 때, 아래 상수 표의 *에 해당
-
-
상수
-
ChronoField에 정의된 모든 상수를 아래에 나열 (ChronoField.상수 형식으로 사용)
-
사용할 수 있는 필드는 클래스마다 다름
-
해당 클래스가 지원하지 않는 필드 사용시, UnsupportedTemporalTypeException 발생
-
(예) LocalDate는 날짜를 표현하기 위한 것으로, MINUTE_OF_HOUR과 같은 시간에 관련된 필드를 사용할 수 없음
-
종류
-
상수 | 의미 |
---|---|
ERA | 시대 |
YEAR_OF_ERA, YEAR | 년 |
MONTH_OF_YEAR | 월 |
DAY_OF_WEEK | 요일(1: 월요일, 2: 화요일, … , 7: 일요일) |
DAY_OF_MONTH | 일 |
AMPM_OF_DAY | 오전/오후 |
HOUR_OF_DAY | 시간(0~23) |
CLOCK_HOUR_OF_DAY | 시간(1~24) |
HOUR_OF_AMPM | 시간(0~11) |
CLOCK_HOUR_OF_AMPM | 시간(1~12) |
MINUTE_OF_HOUR | 분 |
SECOND_OF_MINUTE | 초 |
MILLI_OF_SECOND | 천분의 일초 (=10^-3초) |
MICRO_OF_SECOND * | 백만분의 일초 (=10^-6초) |
NANO_OF_SECOND * | 십억분의 일초(=10^-9초) |
DAY_OF_YEAR | 그 해의 N번째 날 |
EPOCH_DAY * | EPOCH(1970.1.1)부터 N번째 날 |
MINUTE_OF_DAY | 그 날의 N 번째 분(시간을 분으로 환산) |
SECOND_OF_DAY | 그 날의 N 번째 초(시간을 초로 환산) |
MILLI_OF_DAY | 그 날의 N 번째 밀리초(=10^-3초) |
MICRO_OF_DAY * | 그 날의 N 번째 마이크로초(=10^-6초) |
NANO_OF_DAY * | 그 날의 N 번째 나노초(=10^-9초) |
ALIGNED_WEEK_OF_MONTH | 그 달의 n번째 주(1~7일 1주, 8~14일 2주, …) |
ALIGNED_WEEK_OF_YEAR | 그 해의 n번째 주(1월 1~7일 1주, 8~14일 2주, …) |
ALIGNED_DAY_OF_WEEK_IN_MONTH | 요일(그 달의 1일을 월요일로 간주하여 계산) |
ALIGNED_DAY_OF_WEEK_IN_YEAR | 요일(그 해의 1월 1일을 월요일로 간주하여 계산) |
INSTANT_SECONDS | 년월일을 초단위로 환산 (1970-01-01 00:00:00 UTC를 0초로 계산) Instant에만 사용 가능 |
OFFSET_SECONDS | UTC와의 시차, ZoneOffset에만 사용 가능 |
PROLEPTIC_MONTH | 년월을 월단위로 환산 (2019년 8월 = 2019 * 12 + 8) |
-
필드의 값 변경하기 - with(), plus(), minus()
-
with() : 날짜와 시간에서 특정 필드 값을 변경할 때 with로 시작하는 메서드 사용
-
LocalDate withYear(int year)
LocalDate withMonth(int month)
LocalDate withDayOfMonth(int dayOfMonth)
LocalDate withDayOfYear(int dayOfYear)
LocalTime withHour(int hour)
LocalTime withMinute(int minute)
LocalTime withSecond(int second)
LocalTime withNano(int nanoOfSecond) -
with()를 이용하면 필드를 직접 지정할 수 있음
LocalDate with(TemporalField field, long newValue) -
필드를 변경하는 메서드들은 항상 새로운 객체를 생성해서 반환하기 때문에 대입 연산자를 반드시 같이 써줘야 함
(예시 - O) date = date.withYear(2020);
(예시 - X) date.withYear(2020);
-
-
plus() / minus() : 특정 필드에 값을 더하거나 빼는 plus() / minus()
-
LocalTime plus(TemporalAmount amountToAdd)
LocalTime plus(long amountToAdd, TemporalUnit unit)
LocalDate plus(TemporalAmount amountToAdd)
LocalDate plus(long amountToAdd, TemporalUnit unit)
minus()도 위와 동일 -
plus로 만든 메서드
LocalDate plusYears(long yearsToAdd)
LocalDate plusMonths(long monthsToAdd)
LocalDate plusDays(long daysToAdd)
LocalDate plusWeeks(long weeksToAdd)
LocalTime plusHours(long hoursToAdd)
LocalTime plusMinutes(long minutesToAdd)
LocalTime plusSeconds(long secondsToAdd)
LocalTime plusNanos(long nanosToAdd)
-
-
LocalTime의 truncatedTo()
-
지정된 것보다 작은 단위 필드를 0으로 만들어버림
-
예시
LocalTime time = LocalTime.of(12, 34, 56); // 12시 34분 56초
time = time.truncatedTo(ChronoUnit.HOURS); // 시보다 작은 단위를 0으로 만듦
System.out.println(time); // 12:00 -
LocalDate는 사용할 수 없음 ← 년, 월, 일은 0이 될 수 없기 때문
-
LocalTime의 turncatedTo()의 매개변수는 아래 표 중 시간과 관련된 필드만 사용 가능
표 - 열거형 ChronoUnit에 정의된 상수 목록
-
TemporalUnit(ChronoUnit) | 설명 |
---|---|
FOREVER | Long.MAX_VALUE초(약 3천억년) |
ERAS | 1,000,000,000년 |
MILLENNIA | 1,000년 |
CENTURIES | 100년 |
DECADES | 10년 |
YEARS | 년 |
MONTHS | 월 |
WEEKS | 주 |
DAYS | 일 |
HALF_DAYS | 반나절 |
HOURS | 시 |
MINUTES | 분 |
SECONDS | 초 |
MILLIS | 천분의 일초(=10^-3) |
MICROS | 백만분의 일초(=10^-6) |
NANOS | 십억분의 일초(=10^-9) |
-
날짜와 시간의 비교 - isAfter(), isBefore(), isEqual()
-
ompareTo()
-
LocalDate와 LocalTime도 comareTo()가 적절히 오버라이딩 되어 있음
-
int result = date1.compareTo(date2); // 같으면 0, date1이 이전이면 -1, 이후면 1
-
-
보다 편리하게 비교할 수 있는 메서드들이 추가로 제공
-
boolean isAfter(ChronoLocalDate other)
-
boolean isBefore(ChronoLocalDate other)
-
boolean isEqual(ChronoLocaldate other) // LocalDate에만 있음
equals()가 있지만 isEqual()을 제공하는 이유
isEqual()은 오직 날짜만 비교
연표(chronology)가 다른 두 날짜를 비교하기 위해서
대부분의 경우 equals와 isEqual()의 결과는 같음
-
Instant
-
에포크 타임(EPOCH TIME, 1970-01-01 00:00:00 UTC)부터 경과된 시간을 나노초 단위로 표현
-
사람에게는 불편하지만, 단일 진법으로만 다루기 때문에 계산하기 쉬움
-
사람이 사용하는 날짜와 시간은 여러 진법이 섞여있기 때문에 계산이 어려움
-
-
생성 - now()와 ofEpochSecond 사용
-
Instant now1 = Instant.now();
-
Instant now2 = Instant.ofEpochSecond(now.getEpochSecond());
-
Instant now3 = Instant.ofEpochSecond(now.getEpochSecond(), nowgetNano());
-
-
필드에 저장된 값 가져오기
-
long epochSec = now.getEpochSecond();
-
int nano = now.getNano();
-
Instant는 시간을 초 단위와 나노초 단위로 나누어 저장
-
-
toEpochMilli()
-
오라클 데이터베이스의 타임스탬프(timestamp)처럼 밀리초 단위의 EPOCH TIME을 필요로 하는 경우를 위함
-
long toEpochMilli();
-
-
Instant vs LocalTime
-
Instant : 항상 UTC(+00:00)를 기준
-
LocalTime : 한국은 시간대가 ‘+09:00’이므로 Instant와 9시간의 시간차이가 발생
-
시간대를 고려해야하는 경우 OffsetDateTime을 사용하는 것이 더 나은 선택일 수 있음
-
-
UTC란?
-
‘Coordinated Universal Time’ : 세계 협정시
-
1972년 1월 1일부터 시행된 국제 표준시
-
이전에 사용되던 GMT(Greenwich Mean Time)와 UTC는 거의 같지만 UTC가 좀더 정확함
-
-
instant와 Date간의 변환
-
Instant는 기존의 java.util.Date를 대체하기 위한 것
-
Instant → Date
static Date from(Instant instant) -
Date → Instant
Instant toInstant()
-
LocalDateTime과 ZonedDateTime
-
LocalDateTime & ZonedDateTime
-
LocalDateTime ← LocalDate + LocalTime
-
ZonedDateTime ← LocalDateTime + 시간대
-
-
LocalDate와 LocalTime으로 LocalDateTime만들기1
-
now()
-
LocalDateTime today = LocalDateTime.now();
-
-
LocalDate와 LocalTime으로 LocalDateTime만들기2
-
of()
-
다양한 버전의 of()가 정의되어 있음
-
예) 2019년 9월 14일 8시 19분 13초
-
-
LocalDate와 LocalTime으로 LocalDateTime만들기3
- LocalDate와 LocalTime 합치기
-
LocalDateTime의 변환
- LocalDateTime을 LocalDate 또는 LocalTime으로 변환할 수 있음
-
LocalDateTime으로 ZonedDateTime 만들기
-
ZonedDateTime = LocalDateTime + 시간대(time-zone)
-
ZoneId
기존에는 TimeZone 시간대를 사용했으나, 새로운 time 패키지에서는 ZoneId라는 클래스를 사용
ZoneId는 일광 절약시간(DST, Daylight Saving Time)을 자동으로 처리해주기 때문에 더 편리함
사용 가능한 ZoneId의 목록은 ZoneId.getAvailableZonelds()로 얻을 수 있음 -
LocalDateTime → ZonedDateTime
LocalDateTime의 atZone()으로 시간대 정보(ZoneId)를 추가하면 됨 -
LocalDate → ZoneDateTime
LocalDate의 atStartOfDay()라는 메서드의 매개변수로 ZoneId를 지정하면 됨
시간이 0시 0분 0초로 세팅 -
특정 시간대의 시간을 알고 싶다면 (ex)뉴욕
ZonedDateTime.now().withZoneSameInstant(ZoneId.of(“America/New_York”));
-
-
ZoneOffset
-
UTC로부터 얼마만큼 떨어져있는지를 ZoneOffset으로 표현
-
서울은 +9, 즉 UTC보다 9시간(32400초 = 60 * 60 * 9초)가 빠름
-
-
ZonedDateTime의 변환
-
LocalDateTime처럼 날짜와 시간에 관련된 다른 클래스로 변환하는 메서드들을 가지고 있음
-
LocalDate toLocalDate()
LocalTime toLocalTime()
LocalDateTime toLocalDateTime()
offsetDateTime toOffsetDateTime()
long toEpochSecond()
Instant toInstant()
-
-
ZonedDateTime은 GregorianCalendar와 가장 유사함
-
ZonedDateTime → GregorianCalendar
GregorianCalendar from(ZonedDateTime zdt) -
GregorianCalendar → ZonedDateTime
ZonedDateTime toZonedDateTime()
-
OffsetDateTime
-
VS ZonedDateTime
-
ZonedDateTime : ZoneId로 구역을 표현
ZoneId : 일광절약시간처럼 시간대와 관련된 규칙들을 포함 -
OffsetDateTime : ZoneOffset을 사용
ZoneOffset : 단지 시간대를 시간의 차이로만 구분
→ 컴퓨터에게 일광절약시간처럼 계절별로 시간을 더했다 뺐다 하는 행위는 위험함
→ 아무런 변화 없이 일관된 시간체계를 유지하는 것이 더 안전함
-
-
같은 지역내의 컴퓨터간 데이터를 주고받을 때, 전송시간 : LocalDateTime이면 충분
다른 지역의(다른 시간대에) 존재하는 컴퓨터간의 통신 : OffSetDateTime이 필요 -
생성 1 - LocalDate + LocalTime + ZoneOffset
- 예시
-
생성 2 - ZonedDateTime → OffsetDateTime
-
ZonedDateTime.toOffsetDateTime()
-
예시
-
Subscribe via RSS