1. java.time 패키지
Date와 Calendar가 가지고 있던 단점들을 해소하기 위해 JDK 1.8부터 'java.time패키지'가 추가되었다.
이 패키지는 다음과 같이 4개의 하위 패키지를 가지고 있다.
패키지 | 설명 |
java.time | 날짜와 시간을 다루는데 필요한 핵심 클래스들을 제공 |
java.time.chrono | 표준(ISO)이 아닌 달력 시스템을 위한 클래스들을 제공 |
java.time.temporal | 날짜와 시간의 필드(field)와 단위(unit)을 위한 클래스들을 제공 |
java.time.zone | 시간대(time-zone)와 관련된 클래스들을 제공 |
위의 패키지들의 가장 큰 특징은 String클래스처럼 '불변(immutable)'이라는 것이다. 그래서 날짜나 시간을 변경하는 메서드들은
기존의 객체를 변경하는 대신 항상 변경된 새로운 객체를 반환한다. 기존 Calendar클래스는 변경 가능하므로, 멀티 쓰레드 환경에서
안전하지 못하다. 멀티 쓰레드에서는 여러 쓰레드가 같은 객체에 접근할 수 있기 때문에 변경 가능한 객체는 데이터가 잘못될 가능성이
있으며, 이를 쓰레드에 안전하지 않다고 한다.
2. java.time 패키지의 핵심 클래스
날짜와 시간을 하나로 표현되는 Calendar 클래스와 달리 , java.time패키지에서는 날짜와 시간을 별도의 클래스로 분리해 놓았다.
시간을 표현할 때는 LocalTime 클래스를 사용하고, 날짜를 표현할 때는 LocalDate클래스를 사용한다. 그리고 날짜와 시간이 모두
필요할 때는 LocalDateTime 클래스를 사용하면 된다.
LocalDate + LocalTime -> LocalDateTime
날짜 시간 날짜&시간
여기에 시간대(time-zone)까지 다뤄야 한다면, ZonedDateTime클래스를 사용하자.
LocalDateTime + 시간대 -> ZonedDateTime
Calendar는 ZonedDateTime처럼, 날짜와 시간 그리고 시간대까지 모두 가지고 있다. Date와 유사한 클래스로는 Instant가 있는데,
이 클래스는 날짜와 시간을 초 단위(정확히는 나노초)로 표현된다. 날짜와 시간을 초단위로 표현한 값을 타임스탬프(time-stamp)라
하는데, 이 값들은 날짜와 시간을 하나의 정수로 표현할 수 있으므로 날짜와 시간의 차이를 계산하거나 순서를 비교하는데 유리해서
데이터베이스에 많이 사용된다.
이외에도 날짜를 더 세부적으로 다룰 수 있는 Year, YearMonth, MonthDay와 같은 클래스도 있다.
2. LocalDate
와 LocalTime
LocalDate와 LocalTime은 java.time 패키지의 가장 기본이 되는 클래스이며, 나머지 클래스들은 이들의 확장이므로 두 클래스만을
잘 이해하고 나면 나머지는 아주 쉬워진다. 객체를 생성하는 방법은 현재의 날짜와 시간을 LocalDate와 LocalTime으로 각각 반환하는
now()와 지정된 날짜와 시간으로 객체를 생성하는 of()가 있으며, 둘 다 static 메서드이다.
LocalDate today = LocalDate.now(); // 오늘의 날짜
LocalTime now = LocalTime.now(); // 현재 시간
LocalDate birthDate = LocalDate.of(1999,12,31); // 1999년 12월 31일
LocalTime birthTime = LocalDate.of(23, 59, 59); // 23시 59분 59초
특정 필드의 값 가져오기
*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일부터 몇번째 일(365) |
int 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) |
필드의 값 변경하기 - with(), plus(), minus()
날짜와 시간에서 특정 필드 값을 변경하려면, 다음과 같이 with로 시작하는 메서드를 사용하면 된다.
LocalDate withYear(int year)
LocalDate withMonth(int moth)
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()를 사용하면, 원하는 필드를 직접 지정할 수 있다. 위의 메서드들은 모두 with()로 작성된 것이라는 것을 짐작할 수 있다.
앞서 언급한 것과 같이 필드를 변경하는 메서드들은 항상 새로운 객체를 생성해서 반환하므로 아래와 같이 대입 연산자를 같이
사용해야한다는 것을 잊으면 안된다.
date = date.withYear(2000); // 년도를 2000년으로 변경
time = time.withHour(12) // 시간을 12시로 변경
plust()로 만든 메서드는 다음과 같다.
LocalDate plustYears(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 secondToAdd)
LocalTime plusNanos(long nanoToAdd)
그리고 LocalTime의 truncateTo()는 지정된 것보다 작은 단위의 필드를 0으로 만든다.
LocalTime time = LocalTime.of(12, 34, 56); // 12시 34분 56초
time = time.truncatedTo(ChronoUnit.HOURS) // 시(hour)보다 작은 단위를 0으로.
System.out.println(time); // 12:00
날짜와 시간의 비교 - isAfter(),isBefore(),isEqual()
LocalDate와 LocalTime도 compareTo()가 적절히 오버라이딩 되어 있어서, 아래와 같이 compareTo()로 비교할 수 있다.
int result = date1.compareTo(date2); // 같으면 0, date1이 이전이면 -1, 이후면 1
하지만 보다 편리하게 비교할 수 있는 메서드들이 추가로 제공된다.
boolean isAfter (ChronoLocalDate other)
boolean isBefore (ChronoLocalDate other)
boolean isEqual (ChronoLocalDate other) // LocalDate에만 있음
예제1)
package Java2;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class NewTimeEx1 {
public static void main(String[] args) {
LocalDate today= LocalDate.now(); // 오늘의 날짜
LocalTime now = LocalTime.now(); // 현재 시간
LocalDate birthDate = LocalDate.of(1999,12,31);
LocalTime birthTime = LocalTime.of(23,59,59);
System.out.println("today = " + today);
System.out.println("now = " + now);
System.out.println("birthDate = " + birthDate);
System.out.println("birthTime = " + birthTime);
System.out.println(birthDate.withYear(2000)); // 2000/12/31
System.out.println(birthDate.plusDays(1)); // 2000/01/01
System.out.println(birthDate.plus(1, ChronoUnit.DAYS)); // 2000/01/01
// 23:59:59 -> 23:00
System.out.println(birthTime.truncatedTo(ChronoUnit.HOURS));
}
}
결과 :
3. LocalDateTime
LocalDate와 LocalTime으로 합쳐서 하나의 LocalDateTime을 만들 수 있다.
LocalDate date = LocalDate.of(2015, 12, 31);
LocalTime time = LocalTime.of(12, 34, 56);
LocalDateTime dt = LocalDateTime.of(date, time);
LocalDateTime dt2 = date.atTime(time);
LocalDateTime dt3 = time.atDate(date);
LocalDateTime dt4 = date.atTime(12,34,56);
LocalDateTime dt5 = time.atDate(LocalDate.of(2015, 12, 31));
LocalDateTime dt6 = date.atStartOfDay(); // dt 6 = date.atTime(0,0,0);
물론 LocalDateTime에도 날짜와 시간을 직접 지정할 수 있는 다양한 버젼의 of()와 now()가 정의되어 있다.
// 2015년 12월 31일 12시 34분 56초
LocalDateTime dateTime = LocalDateTime.of(2015, 12, 31, 12, 34, 56);
LocalDateTime today = LocalDateTime.now();
반대로 LocalDateTime을 LocalDate와 LocalTime으로 각각 변환할 수 있다.
LocalDateTime dt = LocalDateTime.of(2015, 12, 31, 12, 34, 56);
LocalDate date = dt.toLocalDate(); // LocalDateTime -> LocalDate
LocalTime time = dt.toLocalTime(); // LocalDateTime -> LocalTime
4. Period와 Duration
Period는 날짜의 차이를, Duration은 시간의 차이를 계산해준다.
between()
예를 들어 두 날짜 date1과 date2의 차이를 나타내는 Period는 between()으로 얻어올 수 있다.
LocalDate date1 = LocalDate.of(2014, 1, 1);
LocalDate date2 = LocalDate.of(2015, 12, 31);
Period pe = Period.between(date1, date2);
date1이 date2보다 날짜 상으로 이전이면 양수로, 이후면 음수로 Period에 저장된다.
그리고 시간차이를 구할 때는 Duration을 사용한다는 것을 제외하고는 Period와 똑같다 .
LocalTime time1 = LocalTime.of(00,00,00);
LocalTime time2 = LocalTime.of(12,34,56); // 12시 34분 56초
Duration du = Duration.between(time1, time2) ;
get()
Period, Duration에서 특정필드의 값을 얻을 때도 get()을 사용한다. 하지만 Period와 달리 Duration에는
getHours(),getMinutess() 같은 메서드가 없다.
long year = pe.get(ChronoUnit.YEARS) // int getYears()
long month = pe.get(ChronoUnit.MONTH) // int get Months()
long day = pe.get(ChronoUnit.DAY) // int get Days()
long sec = du.get(ChoronoUnit.SECONDS); // long getSeconds()
int nano = du.get(ChoronoUnit.NANOS); // int getNano()
of(),with()
Period에는 필드를 변경할 수 있는 of(), ofYears() , ofMonths(), ofWeeks(), ofDays()가 있고,
Duration에는 of(), ofHours(), ofMinutes(), ofSeconds() 등이 있다. 사용법은 앞서 LocalDate나 Time 과 같다.
Period pe = Period.of(1, 12, 31 ); // 1년 12개월 31일
Duration du = Duration.of(60, ChronoUnit.SECONDS) ; // 60초
Duration du = Duration.ofSeconds(60); // 위의 문장과 동일
of()가 생성에 초점이 맞춰져 있다면 with() 는 특정 필드의 값을 변경하는데 의의가 있다.
pe = pe.withYears(2); // 1년에서 2년으로 변경. withMonths(), withDays()
du = du.withSeconds(120) // 60초에서 120초로 변경, withNanos()
사칙 연산 , 비교 연산 ,기타 메서드
plus(), minus() 외에 곱셈과 나눗셈을 위한 메서드도 있다.
pe = pe.minusYeas(1)multipliedBy(2); // 1년을 빼고, 2배를 곱한다.
du = du.plusHours(1).dividiedBy(60); // 1시간을 더하고 60으로 나눈다.
Period에 나눗셈을 위한 메서드가 없는 이유는 날짜와 기간을 표현하기 위한 것이므로 나눗셈이 별로 유용하기 않기 때문이다.
다른 단위로 변환 - toTotalMonths(), toDays() , toHours(), toMinutes()
이름이 to로 시작하는 이 메서드들은 Period와 Duration을 다른 단위의 값으로 변환하는데 사용된다.
get()은 특정 필드의 값을 그대로 가져오지만, 위 메서드들은 특정단위로 결과를 반환한다.
예제 1)
package Java2;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.temporal.ChronoUnit;
public class NewTimeEx4 {
public static void main(String[] args) {
LocalDate date1 = LocalDate.of(2014, 1, 1);
LocalDate date2 = LocalDate.of(2015, 12, 31);
Period pe = Period.between(date1, date2);
System.out.println("date1= " + date1); System.out.println("date2= " + date2);
System.out.println("pe = " + pe);
System.out.println("YEAR = " + pe.get(ChronoUnit.YEARS)); System.out.println("MONTH = " + pe.get(ChronoUnit.MONTHS));
System.out.println("DAY = " + pe.get(ChronoUnit.DAYS));
LocalTime time1 = LocalTime.of(0, 0,0);
LocalTime time2 = LocalTime.of(12, 34,56); // 12시간 23분 56초
Duration du = Duration.between(time1, time2);
System.out.println("time1 = " + time1);
System.out.println("time2 = " + time2);
System.out.println("du = " + du);
LocalTime tmpTime = LocalTime.of(0,0).plusSeconds(du.getSeconds());
System.out.println("HOUR = " + tmpTime.getHour());
System.out.println("MINUTE = " + tmpTime.getMinute());
System.out.println("SECOND = " + tmpTime.getSecond());
System.out.println("NANO= " + tmpTime.getNano());
}
}
결과 :
'JAVA의 정석' 카테고리의 다른 글
ArrayList, HashSet, HashMap (0) | 2024.03.13 |
---|---|
컬렉션 프레임웍(Collections Framework) (0) | 2024.03.01 |
Calendar와 Date 클래스 (5) | 2024.02.28 |
래퍼(wrapper) 클래스와 오토박싱&언박싱 (0) | 2024.02.26 |
Math 클래스 (0) | 2024.02.24 |