본문 바로가기
자바

[Java] 자바8의 새로운 날짜 관련 클래스들 - java.time 패키지 (LocalDate, LocalTime, LocalDateTime, ZonedDateTime)

by 책 읽는 개발자_테드 2021. 12. 13.
반응형

자바 java.time 패키지 사용하기

학습 목표

 · 날짜와 시간 객체

 · 날짜와 시간에 대한 정보 얻기

 · 날짜와 시간 조작하기

    - 날짜와 시간 빼기와 더하기

    - 날짜와 시간 변경하기

    - 날짜와 시간 비교하기

     -날짜 포맷 정하기


 

 · 자바 7 이전까지는 Date, Calendar, SimpleDateFormatter 클래스를 이용해서 날짜와 시간 정보를 처리했지만,

여러 단점이 존재했다.

 

TODO: 단점 정리

 

하지만 Date 클래스는 단순히 특정 시점의 날짜 정보를 저장하는 역할만을 하며, 대부분의 메소드가 Deprecated되었다. 또한 Calendar도 날짜 와 시간 정보를 얻는 것 외에 날짜와 시간은 조작하거나 비교하는 기능이 부족했다.

 

이러한 기존 버전의 문제점을 해결하기 위해서 자바 8부터 날짜와 시간을 나타내는 여러 API가 java.time 패키지에 추가되었다. 

 

패키지 설명
java.time 날짜와 시간을 나타내는 API인 LocalDate, LocalTime, LocalDateTime, ZoneDateTime을 포함한다. 이 클래스들은 ISO-8601에 정의된 달력 시스템에 기초한다.
java.time.chrono ISO-8601에 정의된 달력 시스템 이외에 다른 달력 시스템이 필요할 때 사용할 수 있는 API 포함
java.time.format 날짜와 시간을 파싱하고 포맷팅하는 API 포함
java.time.temporal 날짜와 시간을 연산하기 위한 보조 API 포함
java.time.zone 타임존을 지원하는 API 포함

 

날짜와 시간 객체

 

java.time 패키지에는 날짜와 시간을 표현하는 5개의 클래스가 있다.

클래스명 설명
LocalDate 로컬 날짜 클래스
LocalTime 로컬 시간 클래스
LocalDateTime 로컬 날짜 및 시간 클래스(LocalDate + LocalTime)
ZonedDateTime 특정 타임존의 날짜와 시간 클래스
Instant 특정 시점의 Time-Stamp 클래스

 

LocalDate

 

로컬 날짜 클래스로 날짜 정보만 저장할 수 있으며, 두 가지 정적 메소드가 정의되어있다. now()는 컴퓨터의 현재 날짜 정보를 저장한 LocalDate 객체를 리턴한다. of()는 매개값으로 주어진 날짜 정보를 저장한 LocalDate 객체를 리턴한다.

 

LocalDate currDate = LocalDate.now();
LocalDate targetDate = LocalDate.of(int year, int month, int dayOfMonth);

 

LocalTime

 

로컬 시간 클래스로 시간 정보만 저장할 수 있으며, 두 가지 정적 메소드가 정의되어있다. now()는 컴퓨터의 현재 시간 정보를 저장한 LocalTime 객체를 리턴한다. of()는 매개 값으로 주어진 시간 정보를 저장한 LocalTime 객체를 리턴한다.

 

LocalTime currTime = LocalTime.now();
LocalTime targetTime = LocalTime.of(int hour, int minute, int second, int nanoOfSecond);

 

LocalDateTime

 

LocalDate와 LocalTime을 결합한 클래스라고 할 수 있으며, 날짜와 시간 정보를 모두 저장 한다. 위에 설명한 클래스들 처럼 두 가지 정적 메소드가 정의되어 있다. now()는 컴퓨터의 현재 날짜와 시간 정보를 저장한 LocalDateTime 객체를 리턴하고, of()는 매개값으로 주어진 날짜와 시간 정보를 저장한 LocalDateTime 객체를 리턴한다.

 

LocalDateTime currDateTime = LocalDateTime.now();
LocalDateTime targetTime = LocalDateTime.of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond);

 

ZonedDateTime

 

ISO-8601 달력 시스템에서 정의하고 있는 타임존(time-zone)의 날짜와 시간을 저장하는 클래스다. 저장 형태는 다음과 같다.

 

 2021-01-27T17:38:45.183099+09:00[Asia/Seoul] 

 

이 처럼 타임존에 대한 정보(존오프셋[존아이디])가 추가된다. 존오프센(ZoneOffset)은 협정세계시(UTC:Universal Time Coordinated)와 차이 나는 시간을 말한다.

 

두 가지 정적 메소드가 정의되어 있으며, ZonedDateTime 은 now() 정적 메소드에 ZoneId를 매개값으로 주고 얻을 수 있다. ZoneId는 Of() 메소드로 얻을 수 있는데, of()의 매개값은 java.util.TimeZone의 getAvailableIDs() 메소드가 리턴하는 유효한 값 중 하나다.

 

import java.time.ZonedDateTime;
import java.time.ZoneId;

public class Main {
    public static void main(String[] args){
        ZonedDateTime utcDateTime = ZonedDateTime.now(ZoneId.of("UTC"));
        ZonedDateTime londonDateTime = ZonedDateTime.now(ZoneId.of("Europe/London"));
        ZonedDateTime seoulDateTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));

        System.out.println(utcDateTime);
        System.out.println(londonDateTime);
        System.out.println(seoulDateTime);
    }
}

 

Instant

 

특정 시간의 타임 스탬프로 사용된다. 주로 특정한 두 시점 간의 시간적 우선순위를 따질 때 사용한다. java.util.Date와 유사하지만, Date는 로컬 컴퓨터의 현재 날짜와 시간을 기준으로, Instant는 협정세계시(UTC)를 기준으로 한다는 차이점이 있다.

 

아래 코드에서 isBefore(), isAfter()는 시간의 앞뒤 여부를 확인하고, until()은 두 시점 간의 차이를 리턴한다.

 

public class Main {

   public static void main(String [] args) throws InterruptedException {

       Instant instant1 = Instant.now();
       Thread.sleep(1000);

       Instant instant2 = Instant.now();

       if(instant1.isBefore(instant2)){ System.out.printf("instant1이 빠르다."); }
       else if(instant2.isAfter(instant1)){ System.out.printf("instant1이 느리다."); }
       else{ System.out.printf("동일한 시간이다."); }

       System.out.printf("차이(nanos): " + instant1.until(instant2, ChronoUnit.NANOS));
   }
}

main() 메소드 실행 결과

 

날짜와 시간에 대한 정보 얻기

 

LocalDate와 LocalTime은 프로그램에서 날짜와 시간 정보를 이용할 수 있는 메소드를 제공한다.



클래스 리턴 타입 메소드 설명
LocalDate int getYear()
Month getMonth() Month 열거값
int getMonthValue()
int getDayOfYear 일년의 몇 번째 일
LocalDate int getDayOfMonth() 월의 몇 번쨰 일
DayOfWeek getDayOfWeek() 요일
boolean isLeapYear() 윤년 여부
LocalTime int getHour() 시간
int getMinute()
int getSecond()
int getNano() 나노초 리턴

 

LocalDateTime과 ZonedDateTime은 날짜와 시간 정보를 모두 갖고 있어 위 표에 대부분의 메소드를 가지고 있다.

 

단, isLeapYear()는 LocalDate에만 있기 때문에 toLocalDate() 메소드로 LocalDate로 변환 후 사용할 수 있다.

 

ZonedDateTime은 시간존에 대한 정보를 제공하는 메소드를 추가적으로 갖는다.



클래스 리턴 타입 메소드 설명
ZonedDateTime ZoneId getZone() 존아이디 리턴
ZoneOffset getOffset() 존오프셋(시차) 리턴




날짜와 시간 조작하기

날짜와 시간 빼기와 더하기

클래스 리턴 타입 메소드 매개변수
LocalDate
LocalDateTime
ZonedDateTime
LocalDate
LocalDateTime
ZonedDateTime
long

 

import java.time.LocalDateTime;

public class Main {

   public static void main(String [] args)   {

       LocalDateTime now = LocalDateTime.now();
       System.out.println(now.toString()); //현재
       System.out.println(now.minusYears(1L).toString()); //년 뺴기
       System.out.println(now.minusMonths(1L).toString()); //달 뺴기
       System.out.println(now.minusDays(1L).toString()); // 일 빼기
       System.out.println(now.minusWeeks(1L).toString()); // 주 빼기
       System.out.println(now.plusYears(1L).toString()); // 년 더하기
       System.out.println(now.plusMonths(1L).toString()); //달 더하기
       System.out.println(now.plusWeeks(1L).toString()); //주 더하기
       System.out.println(now.plusDays(1L).toString()); //일 더하기

       System.out.println(now.minusHours(1L).toString()); //시간 뺴기
       System.out.println(now.minusMinutes(1L).toString()); //분 빼기
       System.out.println(now.minusSeconds(1L).toString()); //초 빼기
       System.out.println(now.minusNanos(1L).toString()); //나노초 빼기
       System.out.println(now.plusHours(1L).toString()); // 시간 더하기
       System.out.println(now.plusMinutes(1L).toString()); //분 더하기
       System.out.println(now.plusSeconds(1L).toString()); // 초 더하기
       System.out.println(now.plusNanos(1L).toString()); //나노초 더하기
   }
}

 

main 메소드 실행 결과

 

날짜와 시간 변경하기

 

아래 예제의 메소드는 LocalDate, LocalDateTime, ZonedDateTime 클래스에서 모두 사용할 수 있으며, 각각의 클래스로 사용한 메소드는  LocalDate, LocalDateTime, ZonedDateTime를 리턴합니다.

 


import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;

public class Main {

   public static void main(String [] args)   {

       LocalDateTime now = LocalDateTime.now();
       System.out.println(now);


       //직접 변경
       LocalDateTime target = now
               .withYear(2024)     //년 변경
               .withMonth(10)      //월 변경
               .withDayOfMonth(5)  //월의 일 변경
               .withHour(13)       //시간 변경
               .withMinute(30)     //분 변경
               .withSecond(20)     //초 변경
               .withNano(300);     //나노초 변경

       System.out.println(target);

       //년의 일 변경(1월 1일 기준으로 매개변수 만큼 더한 날을 출력)
       target = target.withDayOfYear(350);
       System.out.println(target);

       //연도 상대 변경
       target = now.with(TemporalAdjusters.firstDayOfYear()); //이번 해의 첫 일
       System.out.println(target);

       target = now.with(TemporalAdjusters.lastDayOfYear()); //이번 해의 마지막 일
       System.out.println(target);

       target = now.with(TemporalAdjusters.firstDayOfNextYear()); //다음 해의 첫 일
       System.out.println(target);

       //월 상대 변경
       target = now.with(TemporalAdjusters.firstDayOfMonth()); //이번 달의 첫 일
       System.out.println(target);
     
       target = now.with(TemporalAdjusters.lastDayOfYear()); //이번 달의 마지막 일
       System.out.println(target);

       target = now.with(TemporalAdjusters.firstDayOfNextMonth()); //다음 달의 첫 일
       System.out.println(target);

       //요일 상대 변경
       target = now.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); //이번 달의 첫 월요일
       System.out.println(target);

       target = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); //돌아오는 월요일
       System.out.println(target);

       target = now.with(TemporalAdjusters.previous(DayOfWeek.MONDAY)); //지난 월요일
       System.out.println(target);
   }
}

 

main 메소드 실행 결과

 

With(TemporalAdjuster adjuster) 메소드는 현재 날짜를 기준으로 해의 첫 번째 일 또는 마지막 일, 달의 첫 번째 일 또는 마지막 일, 달의 첫 번째 요일, 지난 요일 및 돌아오는 요일 등 상대적인 날짜를 리턴한다.

 

매개값은 TemporalAdjuster 타입으로 TemporalAdjusters의 정적 메소드를 호출하면 얻을 수 있다.

 

날짜와 시간 비교하기

 

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.temporal.ChronoUnit;

public class Main {

   public static void main(String [] args)   {

       LocalDateTime startDateTime = LocalDateTime.of(2021,1,28,9,0,0);
       System.out.println(startDateTime);

       LocalDateTime endDateTime = LocalDateTime.of(2023,3,31,18,0,0);
       System.out.println(endDateTime+"\n");

       if(startDateTime.isBefore(endDateTime)){    //이전 날짜인지 비교
           System.out.println("진행중\n");

       }else if(startDateTime.isEqual(endDateTime)){ //동일 날짜인지 비교
           System.out.println("종료하기\n");

       }else if(startDateTime.isAfter(endDateTime)){ //이후 날짜인지 비교
           System.out.println("종료됨\n");
       }

       //-------------------------------------------------------------------------------

       //종료까지 남은 시간은 두 가지 방법으로 구현 가능하다. until 메소드와 between 메소드
       
       System.out.println("종료까지 남은 시간");
       long remainYear = startDateTime.until(endDateTime, ChronoUnit.YEARS);   //전체 년 차이
       long remainMonth = startDateTime.until(endDateTime, ChronoUnit.MONTHS);   //전체 달 차이
       long remainWeek = startDateTime.until(endDateTime, ChronoUnit.WEEKS);   //전체 주 차이
       long remainDay = startDateTime.until(endDateTime, ChronoUnit.DAYS);   //전체 주 차이
       long remainHour = startDateTime.until(endDateTime, ChronoUnit.HOURS);   //전체 일 차이
       long remainMinute = startDateTime.until(endDateTime, ChronoUnit.MINUTES);   //전체 년 차이
       long remainSecond = startDateTime.until(endDateTime, ChronoUnit.SECONDS);   //전체 년 차이

       System.out.println("연:"+remainYear);
       System.out.println("월:"+remainMonth);
       System.out.println("주:"+remainWeek);
       System.out.println("일:"+remainDay);
       System.out.println("시간:"+remainHour);
       System.out.println("분:"+remainMinute);
       System.out.println("초:"+remainSecond);

       remainYear = ChronoUnit.YEARS.between(startDateTime, endDateTime);
       remainMonth = ChronoUnit.MONTHS.between(startDateTime, endDateTime);
       remainWeek = ChronoUnit.WEEKS.between(startDateTime, endDateTime);
       remainDay = ChronoUnit.DAYS.between(startDateTime, endDateTime);
       remainHour = ChronoUnit.HOURS.between(startDateTime, endDateTime);
       remainMinute = ChronoUnit.MINUTES.between(startDateTime, endDateTime);
       remainSecond = ChronoUnit.SECONDS.between(startDateTime, endDateTime);

       System.out.println("연:"+remainYear);
       System.out.println("월:"+remainMonth);
       System.out.println("주:"+remainWeek);
       System.out.println("일:"+remainDay);
       System.out.println("시간:"+remainHour);
       System.out.println("분:"+remainMinute);
       System.out.println("초:"+remainSecond+"\n");

       //-------------------------------------------------------------------------------

       //Priod 클래스로 날짜 차이를 구할 수 있다.
       System.out.println("종료까지 남은 기간");
       Period period = Period.between(startDateTime.toLocalDate(), endDateTime.toLocalDate());
       System.out.print("남은 기간: " + period.getYears() + "년 ");
       System.out.print(period.getMonths() + "달");
       System.out.println(period.getDays() + "일\n");

       //-------------------------------------------------------------------------------

       //Duration 클래스로 시간 차이를 구할 수 있다.
       Duration duration = Duration.between(startDateTime.toLocalTime(), endDateTime.toLocalTime());
       System.out.println("남은 초: " + duration.getSeconds());
       System.out.println("남은 나노 초: " + duration.getNano());

   }
}

Period와 Duration은 날짜와 시간의 양을 나타내는 클래스들이다. 

 

between() 메소드는 Period, Duration 클래스와 ChronoUnit 열거 타입에 존재한다. Period와 Duration의 between은 년, 달, 일, 초의 단순 차이를 리턴하고, ChronoUnit 열거 타입의 between()은 전체 시간을 기준으로 차이를 리턴한다. 

 

예를 들어 2023년 1월과 2024년 3월의 달의 차이를 구하면, Period의 between()은 2를 리턴하고, ChronoUnit.MONTHS.between()은 14를 리턴한다.

 

main 메소드 실행 결과



시간상 날짜와 시간을 파싱과 포맷팅하는 부분은 정리하지 못하여 추후에 추가합니다.

 

날짜 포맷 정하기

다음과 같이 DateTimeFormatter 클래스를 통해 날짜의 포맷을 정할 수 있다.

 

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String formattedString = LocalDateTime.now().format(formatter);

 

출처

이것이 자바다

자바의신

반응형

댓글