이전에 Date 클래스와 Calendar 클래스를 이용하여 날짜와 시간 데이터를 다루는 방법을 포스팅했었습니다.
이번에 포스팅할 java.time 패키지는 위 두 클래스의 단점을 보완한 패키지로 JDK1.8부터 추가되었습니다.
java.time 패키지
패키지 | 설명 |
java.time | 날짜와 시간을 다루는데 필요한 핵심 클래스들을 제공 |
java.time.chrono | 표준(ISO)이 아닌 달력 시스템을 위한 클래스들을 제공 |
java.time.format | 날짜와 시간을 파싱하고 형식화하기 위한 클래스들을 제공 |
java.time.temporal | 날짜와 시간의 필드(field)와 단위(unit)를 위한 클래스들을 제공 |
java.time.zone | 시간대(time-zone)와 관련된 클래스들을 제공 |
java.time 패키지는 위와 같이 4개의 하위 패키지를 가지고 있습니다.
위 패키지들은 String 클래스들 처럼 "불변(immutable)"이라는 것이라는 특징을 가집니다.
그래서 날짜와 시간을 변경하는 메서드들은 기존의 객체를 변경하지 않고 변경되면 항상 새로운 객체를 반환합니다.
멀티 쓰레드 환경에서 스레드의 안전하지 않음.
멀티 쓰레드 환경에서는 동시에 여러 쓰레드가 같은 객체에 접근할 수 있기 때문에, 변경 가능한 객체는 데이터가 잘못될 가능성이 있어 쓰레드에 안전하지 않다.
하지만 Calendar 클래스는 변경가능하기 때문에 멀티 쓰레드 환경에서 안전하지 못합니다.
java.time 패키지의 핵심 클래스들
클래스 | 설명 | 조합 |
LocalDate | 날짜 | |
LocalTime | 시간 | |
LocalDateTime | 날짜와 시간 | LocalDate + LocalTime |
ZonedDateTime | 날짜와 시간, 시간대 | LocalDateTime + 시간대 |
객체 생성하기 - now(), of()
java.time 패키지에 속한 클래스의 객체를 생성하는데 now()와 of()가 사용됩니다.
now()는 현재 날짜와 시간을 저장하는 객체를 생성하고,
of()는 해당 필드의 값을 순서대로 지정해주어 원하는 날짜와 시간으로 객체를 생성합니다.
// now()
LocalDate date = LocalDate.now(); // 2023-01-16
LocalTime time = LocalTime.now(); // 22:16:53.198942300
// of()
LocalDate date = LocalDate.of(2022, 12, 31); // 2022-12-31
LocalTime time = LocalTime.of(23, 59, 59); // 23:59:59
/* of()메서드의 매개변수
LocalDate of(int year, int month, int dayOfMonth)
LocalTime of(int hour, int min, int sec, int nanoOfSecond)
*/
특정 필드의 값 가져오기
java.time 패키지는 Calendar와 다르게 월은 (1 ~ 12)의 범위를 갖고, 요일은 월요일(1)부터 ~ 일요일(7)의 범위를 갖습니다.
LocalDate 메서드
메서드 | 반환 타입 | 설 명 |
getYear() | int | 년도(2022) |
getMonth() | Month | Month열거값 반환(DECEMBER, MARCH, JUNE ...) |
getMonthValue() | int | 월(12) |
getDayOfYear() | int | 일(31) |
getDayOfMonth() | int | 요일 |
getDayOfWeek() | DayOfWeek | Week 열거값 반환(MONDAY, FRIDAY ...) |
isLeapYear() | boolean | 윤년여부 확인(true, false) |
LocalTime 메서드
메서드 | 반환 타입 | 설 명 |
getHour() | int | 시(23) |
getMinute() | int | 분(59) |
getSecond() | int | 초(59) |
getNano() | int |
나노 초(.1984) |
get()과 getLong(). 메서드
위 메서드 외에도 get()과 getLong()이 있는데,
get()은 int형 정수 범위 내의 값을 가져올 때 사용하고, getLong()은 나노초 처럼 큰 값 long 타입을 가져올 때 사용합니다.
get() 메서드는 TemporalField의 열거형 값들을 매개변수로 함께 사용하는데 아래처럼 사용합니다.
// int get(TemporalField field)
// long getLong(TemporalField field)
LocalDate date = LocalDate.now();
today.get(ChronoField.YEAR)); // 2023
get()과 함께 사용하는 TemporalField의 열거값
TemporalField(열거값) | 설 명 |
ERA | 시대 |
YEAR | 연도 |
MONTH_OF_YEAR | 월 |
DAY_OF_MONTH | 일 |
DAY_OF_WEEK | 요일 (월요일:1, 화요일:2, ..., 일요일:7) |
AMPM_OF_DAY | 오전/오후 |
HOUR_OF_DAY | 시(0~23) : 밤 12시를 0으로 표현 |
CLOCK_HOUR_OF_DAY | 시(1~24) : 밤 12시를 24시로 표현 |
HOUR_OF_AMPM | 시(0~11) |
CLOCK_HOUR_OF_AMPM | 시(1~12) |
MINUTE_OF_HOUR | 분 |
SECOND_OF_MINUTE | 초 |
DAY_OF_YEAR | 해당 연도의 몇 번째 날 (1~365, 윤년이면 366) |
EPOCH_DAY | EPOCH(1970년 1월 1일)을 기준으로 몇 번째 날 |
필드 값 변경하기 - with(), plus(), minus()
with()
with를 사용하면 TemporalField의 열거값을 매개변수로 원하는 필드를 직접 지정하여 변경합니다.
// Local Date with(TemporalField field, long newValue)
LocalDate date = LocalDate.now();
System.out.println(date); // 2023-01-16
date = date.with(ChronoField.DAY_OF_WEEK, 3); // 요일을 수요일(3)로 변경
System.out.println(date); // 2023-01-18
with날짜, with시간
날짜와 시간에서 특정 필드 값을 변경하려면 with로 시작하는 메서드를 사용합니다.
LocalDate date = LocalDate.now(); // 오늘의 날짜
LocalTime time = LocalTime.now(); // 현재 시간
System.out.println(date); // 2023-01-16
System.out.println(date.withYear(2022)); // 2022-01-16
System.out.println(date.withMonth(12)); // 2023-12-16
System.out.println(date.withDayOfMonth(31)); // 2023-01-31
System.out.println(date.getDayOfYear()); // 16 (1월 1일 부터 16번째 일)
System.out.println(time); // 22:59:37.880646500
System.out.println(time.withHour(1)); // 01:59:37.880646500
System.out.println(time.withMinute(30)); // 22:30:37.880646500
System.out.println(time.withSecond(50)); // 22:59:50.880646500
System.out.println(time.withNano(3333)); // 22:59:37.000003333
plus(), minus()
plus(), minus()도 TemporalField의 열거값을 매개변수로 원하는 필드를 직접 지정하여 변경하고
앞에 with를 붙여서 사용하면 특정 필드 값을 변경합니다.
LocalDate date2 = LocalDate.now(); // 오늘의 날짜
LocalTime time2 = LocalTime.now(); // 현재 시간
System.out.println(date2); // 2023-01-16
System.out.println(date2.plus(3, ChronoUnit.DAYS)); // 2023-01-19
System.out.println(date2.plusDays(10)); // 2023-01-26
System.out.println(time2); // 23:07:48.202294900
System.out.println(time2.plusHours(3)); // 02:07:48.202294900
날짜와 시간 비교 - isAfter(), isBefore(), isEqual()
날짜와 시간을 비교할 때 compareTo()로도 비교할 수 있는데 같으면 0, 이전이면 음수, 이후면 양수를 반환합니다.
LocalDate date1 = LocalDate.of(2000, 12, 31);
LocalDate date2 = LocalDate.now(); // 2023-01-16
System.out.println(date1.compareTo(date2)); // -23
System.out.println(date1.isAfter(date2)); // false
System.out.println(date1.isBefore(date2)); // true
System.out.println(date1.isEqual(date2)); // false
equals()로도 두 날짜가 같은지 비교할 수도 있지만, equals()는 연표가 다르면 날짜가 같아도 false를 반환하기 때문에
날짜가 서로 같은지 확인하기 위해서는 isEquals()를 사용하는게 좋습니다.
LocalDateTime과 ZonedDateTime
LocalDateTime 만들기
LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime dt = LocalDateTime.of(date, time); // 2023-01-16T23:23:44.638521400
LocalDateTime dt2 = date.atTime(time); // 2023-01-16T23:23:44.638521400
LocalDateTime dt3 = time.atDate(date); // 2023-01-16T23:23:44.638521400
LocalDateTime dt4 = date.atTime(12, 30, 30); // 2023-01-16T12:30:30
LocalDateTime dt5 = time.atDate(LocalDate.of(2000, 12, 31)); // 2000-12-31T23:23:44.638521400
// LocalDateTime -> LocalDate, LocalTime으로 변환
LocalDateTime dt = LocalDateTime.of(2000, 12, 31, 12 ,30, 30);
LocalDate date = dt.toLocalDate();
LocalTime time = dt.toLocalTime();
ZonedDateTime 만들기
LocalDateTime dateTime = LocalDateTime.of(2000, 12, 31, 1, 30, 30);
ZoneId zid = ZoneId.of("Asia/Seoul"); // Asia/Seoul
ZonedDateTime zdt = dateTime.atZone(zid); // 2000-12-31T01:30:30+09:00[Asia/Seoul]
ZonedDateTime seoulTime = ZonedDateTime.now(); // 2023-01-16T23:31:49.875954900+09:00[Asia/Seoul]
ZoneId nyId = ZoneId.of("America/New_York"); // America/New_York
ZonedDateTime nyTime = ZonedDateTime.now().withZoneSameInstant(nyId); // 2023-01-16T09:31:49.876955600-05:00[America/New_York]
TemporalAdjusters
TemporalAdjusters 클래스는 지난 주 토욜일이 몇 일 인지, 이번 달의 4번째 일요일이 몇일인지와 같은 날짜를 계산할 수 있도록 도와줍니다.
단, TemporalAdjusters는 adjustInto()로 정의되어있는 추상 메서드를 구현하여 사용해야합니다.
TemporalAdjusters 메서드
메서드 | 설 명 |
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번째 요일 |
특정 날짜로 부터 2일 후의 날짜를 계산하는 예제
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import static java.time.DayOfWeek.*;
import static java.time.temporal.TemporalAdjusters.*;
class DayAfterTomorrow implements TemporalAdjuster {
@Override
// 날짜와 시간에 관련된 대부분의 클래스는 Temporal 인터페이스를 구현하였으므로 이 메서드의 매개변수가 될 수 있다
public Temporal adjustInto(Temporal temporal) {
return temporal.plus(2, ChronoUnit.DAYS); // 2일을 더한다.
}
}
class NewTimeEx3 {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
LocalDate date = today.with(new DayAfterTomorrow());
System.out.println(today);
System.out.println(date);
today.with(firstDayOfNextMonth()); // 다음 달의 첫 날
today.with(firstDayOfMonth()); // 이 달의 첫 날
today.with(lastDayOfMonth()); // 이 달의 마지막 날
today.with(firstInMonth(TUESDAY)); // 이 달의 첫번째 화요일
today.with(lastInMonth(TUESDAY)); // 이 달의 마지막 화요일
today.with(previous(TUESDAY)); // 지난 주 화요일
today.with(previousOrSame(TUESDAY)); // 지난 주 화요일(오늘 포함)
today.with(next(TUESDAY)); // 다음 주 화요일
today.with(nextOrSame(TUESDAY)); // 다음 주 화요일(오늘 포함)
today.with(dayOfWeekInMonth(4, TUESDAY)); // 이 달의 4번째 화요일
}
}
Period와 Duration
Period는 날짜의 차이를
Duration은 시간의 차이를 계산합니다.
두 날짜의 차이를 계산하는데 between() 메서드를 사용하는데 같은 역할을 하는 메서드로 utill()이 있습니다.
이 둘의 차이는 between()은 static 메서드이고, utill()은 인스턴스 메서드라는 것입니다.
// 날짜의 차이
LocalDate date1 = LocalDate.of(2020, 1, 1);
LocalDate date2 = LocalDate.of(2022, 12, 31);
Period pe = Period.between(date1, date2);
System.out.println("date1 = " + date1); // date1 = 2020-01-01
System.out.println("date2 = " + date2); // date2 = 2022-12-31
System.out.println("pe = " + pe); // pe = P2Y11M30D
System.out.println("YEAR = " + pe.get(ChronoUnit.YEARS)); // YEAR = 2
System.out.println("MONTH = " + pe.get(ChronoUnit.MONTHS)); // MONTH = 11
System.out.println("DAY = " + pe.get(ChronoUnit.DAYS)); // DAY = 30
// 시간의 차이
LocalTime time1 = LocalTime.of(10, 10, 20);
LocalTime time2 = LocalTime.of(12, 30, 50);
Duration du = Duration.between(time1, time2);
System.out.println("time1 = " + time1); // time1 = 10:10:20
System.out.println("time2 = " + time2); // time2 = 12:30:50
System.out.println("du = " + du); // du = PT2H20M30S
LocalTime tmpTime = LocalTime.of(0, 0).plusSeconds(du.getSeconds()); // 02:20:30
System.out.println("HOUR = " + tmpTime.getHour()); // HOUR = 2
System.out.println("MINUTE = " + tmpTime.getMinute()); // MINUTE = 20
System.out.println("SECOND = " + tmpTime.getSecond()); // SECOND = 30
System.out.println("NANO = " + tmpTime.getNano()); // NANO = 0
파싱과 포맷
날짜와 시간의 형식화에는 format() 메서드가 사용됩니다.
DateTimeFormatter에 상수로 정의된 형식들을 매개변수로 원하는 형식(문자열)으로 출력합니다.
LocalDate day = LocalDate.of(2022, 12, 31);
String d = day.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2022-12-31
String d2 = DateTimeFormatter.ISO_LOCAL_DATE.format(day); // 2022-12-31
또한 parse() 메서드를 이용하여 문자열 데이터를 날짜 데이터로 변환할 수도 있습니다.
LocalDate date = LocalDate.parse("2022-12-31"); // 2022년 12월 31일
LocalDate time = LocalTime.parse("23:59:59"); // 23시 59분 59초
출력형식 직접 정의하기
SimpleDateFormat 클래스 처럼 기호를 이용하여 DateTimeFormatter의 ofPattern()으로 원하는 출력형식을 직접 작성할 수 있습니다.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
'◼ JAVA' 카테고리의 다른 글
체크 예외(Exception)와 언체크 예외(RuntimeException) (0) | 2023.04.25 |
---|---|
JSP(JavaServer Pages)와 Servlet(서블릿)이란? (0) | 2023.03.08 |
[Java/자바] Scanner 클래스 (입력받기) (2) | 2023.01.05 |
[Java/자바] SimpleDateFormat 클래스 (날짜 데이터 출력) (0) | 2023.01.05 |
[Java/자바] 날짜와 시간 (Date, calendar) (0) | 2022.12.26 |