본문 바로가기
JAVA

[JAVA] 자바 java.time API로 다양한 날짜와 시간 다루기

by 딘딘은딘딘 2023. 9. 25.
반응형

 

이번 포스팅은 날짜와 시간을 다루는

java.time API을 다뤄 본다.

 


1. LocalDate

LocalDate는 시간을 제외한 날짜를 표현하는 불변 객체이며 어떤 시간대 정보도 포함하지 않는다.

 

LocalDate localDate = LocalDate.of(2023, 9, 22);
LocalDate nowDate = LocalDate.now();        // 시스템 시계 상 현재 날짜
LocalDate localDate3 = LocalDate.parse("2023-09-05");   // 문자열 파싱

int year = localDate.getYear();             // 연도
Month month = localDate.getMonth();         // 월
int monthValue = localDate.getMonthValue(); // 현재 월 (정수)
int day = localDate.getDayOfMonth();        // 일
DayOfWeek dayOfWeek = localDate.getDayOfWeek();     // 요일
int len = localDate.lengthOfMonth();        // 현재 월의 마지막 일
boolean leap = localDate.isLeapYear();      // 윤년 여부


int compare = nowDate.compareTo(localDate); // 두 날짜의 차이


System.out.println("연 : " + year);                 // 연 : 2023
System.out.println("월 (문자) : " + month);          // 월 (문자) : SEPTEMBER
System.out.println("월 (정수) : " + monthValue);     // 월 (정수) : 9
System.out.println("일 : " + day);                  // 일 : 22
System.out.println("dayOfWeek : " + dayOfWeek);     // dayOfWeek : FRIDAY
System.out.println("len : " + len);                 // len : 30
System.out.println("leap : " + leap);               // leap : false
System.out.println("nowDate : " + nowDate);         // nowDate : 2023-09-25
System.out.println("두 날짜의 차이 : " + compare);     // 두 날짜의 차이 : 3
System.out.println("문자열 파싱 : " + localDate3);    // 문자열 파싱 : 2023-09-05

 

LocalDate의 경우 시간정보는 포함하고 있지 않으며 .of 메서드로 생성할 수 있다.

of 메서드의 인자는 정수형 타입의 년,월,일 을 받는다.

public static LocalDate of(int year, int month, int dayOfMonth)

 

위의 방식 말고도 TemporalField를 전달해 원하는 정보를 가져올 수도 있다.

(TemporalField는 시간 관련 객체에서 어떤 필드의 값에 접근할지 정의하는 인터페이스)

 

위에 선언한 localDate로 예제를 이어간다.

 

먼저 LocalDate의 get 메서드를 보면 파라미터로 TemporalField 인터페이스를 받으며,

 

ChronoField enum의 경우 TemporalField 인터페이스를 상속 받는다.

 

int chronoYear = localDate.get(ChronoField.YEAR);
int chronoMonth = localDate.get(ChronoField.MONTH_OF_YEAR);
int chronoday = localDate.get(ChronoField.DAY_OF_MONTH);

System.out.println("연 : " + chronoYear);                 // 연 : 2023
System.out.println("월 (정수) : " + chronoMonth);          // 월 (정수) : 9
System.out.println("일 : " + chronoday);                  // 일 : 22

위처럼 get 메서드에 ChronoField Enum을 사용해 원하는 값을 추출할 수 있다.

 

2. LocalTime

LocalDate가 날짜만 받는다면 LocalTime은 시간만 받는 클래스이다.

LocalTime 역시 .of 메서드로 생성할 수 있으며

파라미터는 시,분 OR 시,분,초 OR 시,분,초,나노초 

3가지의 오버로딩이 되어있다.

 

LocalTime localTime = LocalTime.of(13, 20, 50);
LocalTime localTime2 = LocalTime.parse("15:32:15");
LocalTime localTime3 = LocalTime.now();     // 현재시간
int hour = localTime.getHour();
int minute = localTime.getMinute();
int second = localTime.getSecond();

System.out.println("시 : " + hour);          // 시 : 13
System.out.println("분 : " + minute);        // 분 : 20
System.out.println("초 : " + second);        // 초 : 50
System.out.println("문자열 파싱 : " + localTime2); // 문자열 파싱 : 15:32:15
System.out.println("현재시간 : " + localTime3); // 현재시간 : 현재시간

 

문자열 파싱과 .of 메서드로 LocalTime을 생성하고 각 시/분/초를 뽑아냈다.

.parse 메서드를 사용할때 주의점은

파싱할 수 없는 경우 java.time.format.DateTimeParseException 예외가 발생하므로

사용시 예외처리가 필요하다

Ex)

LocalTime localTime2 = LocalTime.parse("15:32-15");		// 예외발생

3.LocalDateTime

LocalDateTime 은 LocalTime과 LocalDate가 복합된 클래스라고 보면 된다.

 

DateTimeFormatter를 생성

// parse를 위한 패턴 작성
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter dateTimeFormatter2 = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss");

 

 

다양한 방식의 LocalDateTime 객체를 생성

// LocalDateTime 생성
LocalDateTime ldt1 = LocalDateTime.of(localDate, localTime);    // ldt1 : 2023-09-22T13:20:50
LocalDateTime ldt2 = LocalDateTime.of(2023, 8, 21, 13,20,30);   // ldt2 : 2023-08-21T13:20:30
LocalDateTime ldt22 = LocalDateTime.of(2023, 8, 21, 13,20,30);   // ldt22 : 2023-08-21T13:20:30
LocalDateTime ldt3 = localDate.atTime(localTime);   // ldt3 : 2023-09-22T13:20:50
LocalDateTime ldt4 = localDate.atTime(8, 30, 10);   // ldt4 : 2023-09-22T08:30:10
LocalDateTime ldt5 = localTime.atDate(localDate);   // ldt5 : 2023-09-22T13:20:50
LocalDateTime ldt6 = LocalDateTime.parse("2023-08-20 13:20:20", dateTimeFormatter);   // ldt5 : 2023-09-22T13:20:50
LocalDateTime ldt7 = LocalDateTime.parse("2023.08.20 13:20:20", dateTimeFormatter2);   // ldt5 : 2023-09-22T13:20:50

System.out.println("ldt1 : " + ldt1);
System.out.println("ldt2 : " + ldt2);
System.out.println("ldt3 : " + ldt3);
System.out.println("ldt4 : " + ldt4);
System.out.println("ldt5 : " + ldt5);
System.out.println("ldt6 : " + ldt6);
System.out.println("ldt7 : " + ldt7);



/**
ldt1 : 2023-09-22T13:20:50
ldt2 : 2023-08-21T13:20:30
ldt3 : 2023-09-22T13:20:50
ldt4 : 2023-09-22T08:30:10
ldt5 : 2023-09-22T13:20:50
ldt6 : 2023-08-20T13:20:20
*/

 

● 두 날짜 객체의 비교 ( 기준 객체가 더 큰 경우 1  /  더 작은경우 -1  / 동일한 경우 0 )

int ldtConpare = ldt22.compareTo(ldt2);

System.out.println("ldtConpare : " + ldtConpare);   // ldtConpare : 1

 

● 시간 및 날짜 추출

// 시간 및 날짜 추출
LocalDate ldt2_dt = ldt2.toLocalDate(); // LocalDateTime의 날짜
LocalTime ldt2_tm = ldt2.toLocalTime(); // LocalDateTime의 시간

System.out.println("ldt2_dt : " + ldt2_dt);         // ldt2_dt : 2023-08-21
System.out.println("ldt2_tm : " + ldt2_tm);         // ldt2_tm : 13:20:30

 

● LocalDateTime의 조작

// LocalDateTime의 조작
LocalDateTime ldt2Weeks = ldt2.plusWeeks(3);      // 3주를 더한다.
LocalDateTime ldt2Days = ldt2.plusDays(3);        // 3일을 더한다.
LocalDateTime ldt2Hors = ldt2.plusHours(3);       // 3시간을 더한다.
LocalDateTime ldt2Minutes = ldt2.plusMinutes(3);  // 3분을 더한다.
LocalDateTime ldt2Years = ldt2.plusYears(3);      // 3년을 더한다.
LocalDateTime ldt2Seconds = ldt2.plusSeconds(30); // 30초를 더한다.
LocalDateTime ldt2minus = ldt2.minusDays(1);      // 1일을 뺀다.

System.out.println("ldt2Weeks : " + ldt2Weeks);     // ldt2Weeks : 2023-09-11T13:20:30
System.out.println("ldt2Days : " + ldt2Days);       // ldt2Days : 2023-08-24T13:20:30
System.out.println("ldt2Hors : " + ldt2Hors);       // ldt2Hors : 2023-08-21T16:20:30
System.out.println("ldt2Minutes : " + ldt2Minutes); // ldt2Minutes : 2023-08-21T13:23:30
System.out.println("ldt2Years : " + ldt2Years);     // ldt2Years : 2026-08-21T13:20:30
System.out.println("ldt2Seconds : " + ldt2Seconds); // ldt2Seconds : 2023-08-21T13:21
System.out.println("ldt2minus : " + ldt2minus); // ldt2minus : 2023-08-20T13:20:30

// LocalDateTime 객체를 지정한 포맷에 맞는 문자열로 출력
System.out.println("ldt2.format : " + ldt2.format(dateTimeFormatter)); // ldt2.format : 2023-08-21 13:20:30

각각 년,월,일 시,분,초 에 대한 연산을 수행한다.

날짜의 빼기 연산의 경우 minus를 사용하면 된다.

 

날짜 객체를 문자열로 출력하기 위해서는 formatter 객체를 생성한 뒤 

.format 메서드를 사용하면 된다.

 

● 두 날짜 사이의 간격

Duration betweenDuration = Duration.between(ldt6, ldt2);   // 두 시간 사이 간격 생성

long durationSeconds = betweenDuration.getSeconds();       // 두 Time 객체 간의 초 간격
long durationHours = betweenDuration.toHours();            // 두 Time 객체 간의 시간 간격
boolean twoTimesIsZero = betweenDuration.isZero();         // 두 Time 객체 간의 간격이 0인지 체크


System.out.println("durationSeconds : " + durationSeconds);     // durationSeconds : 86410
System.out.println("durationHours : " + durationHours);         // durationHours : 24
System.out.println("twoTimesIsZero : " + twoTimesIsZero);         // twoTimesIsZero : false

 

 

● with와 TemporalAdjusters 을 사용한 다양한 날짜 조정

// TemporalAdjusters
LocalDateTime wendsDayLdt = nowLdt.with(TemporalAdjusters.nextOrSame(DayOfWeek.WEDNESDAY));     // 현재 일을 포함한다.
LocalDateTime nextMonday = nowLdt.with(TemporalAdjusters.next(DayOfWeek.MONDAY));               // 현재 일을 건너뛴다.
LocalDateTime lastDayOfMonths = nowLdt.with(TemporalAdjusters.lastDayOfMonth());
LocalDateTime lastInMonthDayofWeek = nowLdt.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY));
LocalDateTime firstDayOfMonth = nowLdt.with(TemporalAdjusters.firstDayOfMonth());
LocalDateTime firstDayOfNextMonth = nowLdt.with(TemporalAdjusters.firstDayOfNextMonth());
LocalDateTime firstDayOfNextYear = nowLdt.with(TemporalAdjusters.firstDayOfNextYear());
LocalDateTime firstDayOfYear = nowLdt.with(TemporalAdjusters.firstDayOfYear());

System.out.println("현재 일시 : " + nowLdt);
System.out.println("다음 수요일 : " + wendsDayLdt);
System.out.println("다음 월요일 : " + nextMonday);
System.out.println("이번 달의 마지막 일 : " + lastDayOfMonths);
System.out.println("이번 달의 마지막 금요일 : " + lastInMonthDayofWeek);
System.out.println("이번 달의 첫번째 일 : " + firstDayOfMonth);
System.out.println("다음 달의 첫번째 일 : " + firstDayOfNextMonth);
System.out.println("내년의 첫번째 일 : " + firstDayOfNextYear);
System.out.println("올해의 첫번째 일 : " + firstDayOfYear);

 

● with와 TemporalAdjusters 을 사용한 다양한 날짜 조정 -> Custom 메서드 생성

/*
* 주어진 날짜의 다음날을 반환 해주는데
* 다음 날이 주말이면 그 다음주 월요일을 반환 해주는 커스텀 메서드 작성
* */
LocalDateTime fridayLdt = LocalDateTime.of(2023, 9, 22, 10,5,30);
LocalDateTime saturdayLdt = LocalDateTime.of(2023, 9, 23, 10,5,30);

// 함수형 인터페이스 TemporalAdjuster 선언
TemporalAdjuster fnTemporalAdjuster = (temporal -> {
    DayOfWeek dow = DayOfWeek.of(temporal.get(ChronoField.DAY_OF_WEEK));
    int dayToAdd = 1;
    if (dow == DayOfWeek.FRIDAY) dayToAdd = 3;
    else if (dow == DayOfWeek.SATURDAY) dayToAdd = 2;

    return temporal.plus(dayToAdd, ChronoUnit.DAYS);
});


LocalDateTime customTemporalAdjusters1 = fridayLdt.with(fnTemporalAdjuster);
LocalDateTime customTemporalAdjusters2 = saturdayLdt.with(fnTemporalAdjuster);
LocalDateTime customTemporalAdjusters3 = nowLdt.with(fnTemporalAdjuster);


System.out.println("금요일의 다음 날 : " + customTemporalAdjusters1);
System.out.println("토요일의 다음 날 : " + customTemporalAdjusters2);
System.out.println("오늘(월)의 다음 날 : " + customTemporalAdjusters3);


/*
금요일의 다음 날 : 2023-09-25T10:05:30
토요일의 다음 날 : 2023-09-25T10:05:30
오늘(월)의 다음 날 : 2023-09-26T17:31:32.907385
*/

 

주어진 날짜의 다음날을 반환해주는 커스텀 메서드이다.

다음 날이 주말인 경우 그 다음주 월요일을 반환 해준다.

 

LocalDateTime.with 메서드의 경우 함수형 인터페이스 TemporalAdjuster를 파라미터로 받는다.

TemporalAdjuster 인터페이스는 Temporal 정보를 반환해준다.

따라서 동작을 람다로 정의 한 후 Temporal을 리턴해주면 원하는 동작을 구현할 수 있다.

 


자바의 시간과 날짜에 대한 다양한 조작은 실무에서 유용하게 사용 할 수 있을 것 같다.

또한 TemporalAdjuster 인터페이스를 재정의 해 날짜 관련 Util을 만들거나

다양한 비즈니스 로직에 맞춰 구현할 수 있을 것 같다.

반응형