Development/JAVA

java 날짜 계산 (java.time)

foxy_wany 2020. 11. 3. 10:09

java에서는 날짜를 추출/계산 할 수있는 여러가지 방법이 있지만, 익히 알고 있는 방법은

Calendar 클래스를 이용하는 것이다. 

 

Calendar 은 JDK 1.1 부터 제공되었고 유용하게 사용되었으나, 몇가지 문제점이 있다. 

- Calendar 인스턴스는 불변 객체가 아니어서 수정이 가능하다. 

- 윤초와 같은 특별한 상황은 고려되지 않았다. 

- Calendar 은 1월 ~ 12월 을 0 ~ 11 로 표현해야 한다. 

 

JDK 8 이후 버전부터는 새로운 날짜와 시간 API 인 java.time 패키지를 제공한다. 

아래는 java.time 이하 에서 제공되는 하위 패키지 들이다. 

아래 내용은 gbsb.tistory.com/302에서 발췌하였다. 

 

- chrono : ISO-8601 에 정의된 표준달력 외 달력시스템 사용 시 이용되는 클래스 패키지

- format  : 날짜와 시간에 대한 데이터를 분석하고 형식화 하는 클래스 집합 패키지.

- temporal : 날짜와 시간에 대한 연산에 사용되는 보조 클래스들 집합 패키지

- zone : Time-Zone 에 관련 클래스 집합 패키지

 

패키지 외 여러 클래스들이 존재하지만 자주 사용되는 몇가지에 대해 정의한다. 

- LocalDate : 날짜를 표현 시 사용. ( 타임존에 따른 시간 변환 불가 )

- LocalTime : 시간을 표현 시 사용 ( 타임존에 따른 시간 변환 불가 )

- LocalDateTime : 날짜와 시간을 한 번에 표현 시 사용. ( 타임존에 따른 시간 변환 불가 )

- ZonedDateTime : 특정 Time-zone 에 해당하는 날짜와 시간을 다룰 때 사용.

- Instant : 특정 시점의 날짜와 nanosecond 단위로 표현하는 time-stamp 다룰 때 사용.

- Period : 두 날짜 사이의 차이를 표현 시 사용

- Duration : 두 시각 차이를 표현 시 사용

- TemporalField : 월(month-of-year) 과 시(hour-of-day) 와 같이 날짜, 시간과 관련된 필드를 정의한 인터페이스.

                     이 인터페이스를 구현하여 날짜와 시간을 나타낼 때 사용하는 열거체가 ChronoField 이다

 

앞서 얘기한데로 Canendar 클래스는 1월 - 0 , 12월 - 11 로 표현하였으나, 

java.time 부터는 1월 - 1, 12월 - 12로 표현한다.  또한 요일은 월요일 - 1, 일요일 - 7 로 

표기한다. 

 

[ LocalDate 생성 & 사용 ]

위 클래스들은 public 생성자를 제공하지 않기 때문에 객체를 생성할 때 now(), of(), parse() 과 같은 

정적 메소드를 사용해야 한다. 

LocalDate currDate = LocalDate.now();

System.out.println( "Today : " + currDate ); 

>>> 2020-11-06

LocalDate targetDate = LocalDate.of(1981, 2, 9);
System.out.println( "targetDate : " + targetDate );
>>> 1981-02-09

LocalDate parseDate = LocalDate.parse("1981-02-09");
System.out.println( "parseDate : " + parseDate );>>> 1981-02-09

now()  : 현재 날짜 또는 시간을 이용하여 새로운 객체를 생성 및 리턴

of()     : 지정한 날짜와 시간을 표현하는 객체를 생성 및 리턴

parse() : 지정한 날짜와 시간을 표현하는 객체를 생성 및 리턴

 

[ LocalTime 생성 & 사용 ]

- LocalDate 와 매우 유사하며, 기본 포멧은 HH:mm:ss.SSS  이다.

LocalTime currTime = LocalTime.now();

System.out.println( "currTime : " + currTime );

>>> 02:30:27.123

LocalTime seoulTime = LocalTime.now( ZoneId.of("Asiz/Seoul") ) );

System.out.println( "seoulTime : " + seoulTime );

>>> 02:30:27.124

LocalTime targetTime = LocalTime.parse( 02, 30, 10 );

System.out.println( "targetTime : " + targetTime );

>>> 02:30:10

LocalTime targetPlusTime = targetTime.plusHour(8);

System.out.println("targetPlusTime : " + targetPlusTime );

>>> 10:30:10

LocalTime targetMinusTime = targetTime.minusHour(1);

System.out.println( "targetMinumTime : " + targetMinumTime );
>>> 01:30:10

위 샘플들은 LocalDate, LocalTime 으로 분리하였으나 실제 사용되는 함두는 두 클래스(LocalDate, LocalTime)

다 유사하다.

즉, LocalDate 클래스에도 plusDays(..), plus(..), plusHours(..) 등 plusXxxx(...) 와 minusXxxx(...) 함수들이 제공된다. 

 

[ LocalDateTime 객체 생성 & 사용 ]

- LocalDateTime 클래스는 LocalDate 와 LocalTime 2개의 기능을 모두 제공하는 클래스라고 생각하면 된다. 

  또한 제공되는 함수도 유사하다. ( 기본 포멧은 yyyy-MM-ddTHH:mm:ss.SSS 이다. )

LocalDateTime currLDT = LocalDateTime.now();

System.out.println( "currLDT : " + currLDT );

>>> 2020-11-05T02:30:10.235

LocalDateTime targetLDT = LocalDateTime.parse("2020-11-05T02:30:10.123");

System.out.println( "targetLDT : " + targetLDT );

>>> 2020-11-05T02:30:10.123

LocalDateTime targetLDT2 = LocalDateTime.of(1891, 2, 9, 6, 10, 11);

System.out.println( "targetLDT2 : " + targetLDT2 );

>>> 1981-02-09T06:10:11

 

with() : 생성된 객체( Local.... ) 의 특정 필드값을 변경하기 위해서 사용.

 

- with(TemporalField field, long newValue) : LocalDate 객체에서 특정 필드(TemporalField) 를 새로운 값(long)

     으로 설정한 객체를 반환한다. 

- withYear(int year) : LocalData 객체에서 연도 필드를 새로운 값(int) 로 설정 및  반환

- withMonth(int month) : LocalDate 객체에서 월(MONTH_OF_YEAR) 필드를 새로운 값(int) 으로 설정 및 반환 

- withDayOfMonth(int dayOfMonth) : LocalData 객체에서 일(DAY_OF_MONTH) 필드를 새로운 값(int) 으로 설정 및 반환.

- withDayOfYear(int dayOfYear) : LocalDate 객체에서 DAY_OF_YEAR 필드를 새로운 값(int) 으로 설정 및 반환

 

LocalTime 에서는 with(...) withHour(...), withMinute(...), withSecond(...), withNano(...) 등이 존재한다. 

 

with..(...) 함수 외에 plus(), minus() 함수도 제공하고 있다. 

 

[ 날짜 & 시간 객체의 비교 ]

- isEqual() : 오직 날짜가 같은지 비교하며, LocalDate 클래스에서만 제공. ( boolean )

- isBefore() : 객체를 비교하여 이전인지 여부 확인 ( boolean )

- isAfter() : 객체를 비교하여 이후 인지 여부 확인 ( boolean )

 

 

[ java.time.Instant ]

 

Instant.now() 를 호출 시 현재 시간 Instant 를 획득할 수 있다. (UTC 기준 ISO 포멧이다.)

또한 초단위 또는 밀리초 단위의 Timestamp 값을 long 타입으로 얻을 수 있다. 

Instant currInstant = Instant.now();

System.out.println( "UTC+0 기준 현재 시간 : " + currInstant );
--> 2020-11-09T23:20:46.80619700Z    

( 현재 한국 시간은 2020-11-10 08:21 이다. )

System.out.println( "UTC+0 기준 현재 시간 Timestamp (초) : " + currInstant.getEpochSecond() );
--> 1604964046

System.out.println( "UTC+0 기준 현재 시간 Timestamp (밀리초) : " + currInstant.toEpochMilli() );
--> 1604964046806

 

 

Instant 클래스와 ZoneDateTime 클래스는 특정 메서드를 통해 객체가 서로 변환이 가능하다.

바로 atZone(..) 메소드와 toInstant() 메소드 이다. 

Instant currInstant = Instant.now();
ZoneDateTime zdt = currInstant.atZone( ZoneId.of("Asia/Seoul") );

System.out.println( "한국 현재 시간 : " + zdt );
--> 2020-11-10T08:21:45.403111800+09:00[Asiz/Seoul]

Instant korInstant = zdt.toInstant();
System.out.println( "한국 현재 시간 Instant: " + korInstant );
--> 2020-11-09T23:20:46.80619700Z

 

[ java.time.ZoneDateTime ]

ZoneDateTime 클래스는 타임존 또는 시차가 적용되는 필요한 날짜와 시간정보를 나타낼 때 사용하면 용이하다. 

public 생성자는 제공하지 않으며, 정적 메소드를 통하여 객체를 생성한다. 

또한 format() 메소드에 DateTimeFormatter 객체를 사용하여 원하는 문자열로 포멧 변환이 가능하다.

DateTimeFormatter 은 ofPattern(...) 을 이용하여 원하는 포멧을 정의하거나 DateTimeFormatter 에서 제공하는

정적 필드( Ex: DateTimeFormatter.ISO_DATE_TIME.. ) 를 이용할 수 있다. 

날짜 시간에 대한 언어 표현은 withLocale( Locale.~ ) 함수를 사용하여 다양하게 구현할 수 있다. 

System.out.println( "한국 현재 시간 ZoneDateTime: " + ZoneDateTime.now() );
--> 2020-11-10T08:38:51.751179300+09:00[Asia/Seoul]

String zdtFormat = ZoneDateTime.now().format( DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss") );
System.out.println( "ZoneDateTime.format: " + zdtFormat  );
--> 2020.11.10 08:43:49

DateTimeForamtter dtf_1 = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss a z");
System.out.println("DTF_1 : " + ZoneDateTime.now().format( dtf_1 ) );
--> 2020.11.10 08:43:49 오전 KST

DateTimeForamtter dtf_2 = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss a z").withLocale(Locale.ENGLISH);
System.out.println("DTF_2 : " + ZoneDateTime.now().format( dtf_2 ) );
--> 2020.11.10 08:43:49 AM KST

 

[ ZoneId & ZoneOffset ]

ZoneId 는 타임존으로 지정한 지역의 시간코드, ZoneOffset 은 UTC 기준 시간차 (양수/음수) 를 나타낸다.

예로 한국 서울의 ZoneId : Asia/Seoul & ZoneOffSet : +0900 이다. 

System.out.println( "ZoneOffset.of('+09:00') : " + ZoneDateTime.now(ZoneOffset.of("+09:00")) );
--> 2020-11-10T08:38:51.751179300+09:00

System.out.println( "ZoneDateTime.format: " + ZoneDateTime.now( ZoneId.of("Asia/Seoul")  );
--> 2020-11-10T08:38:51.751179300+09:00[Asiz/Seoul]