Calendar 


Calendar 클래스는 추상클래스이기 때문에 직접 객체를 생성할 수 없고, 메서드를 통해서 완전히 구현된 클래스의 인스턴스를 얻어야 한다.


Calendar today = Calendar.getInstance();


System.out.println(today.get(Calendar.YEAR); 

System.out.println(today.get(Calendar.MONTH);    // ( 0~11 0:1월)

System.out.println(today.get(Calendar.DATE);

System.out.println(today.get(Calendar.DAY_OF_WEEK);    // ( 1~7 1:일요일)


처음 보고 드는 생각. 뭔가 되게 복잡해보이고 왜 월은 0부터 했는데 일은 또 1부터 시작하며 요일에 시작이 일요일이라 헷갈리네..

또 메소드만으로 불러지지 않고 클래스 변수를 매개변수로 사용하여 복잡해 보인다는 느낌을 받았다.



이번에 공부하면서 알아보니 


Date는 jdk 1.0 Calendar는 1.1 부터 써왔던 클래스이고 사용되어지면서 단점이 극명하게 드러나 

다들 기본 API를 외면한체 Joda-Time이라는 오픈소스 라이브러리를 사용한다고 한다. 


기존 Calendar, Date 클래스의 문제점


1. 불변객체가 아니다

- set으로 변경이 가능하다. 여러 객체에서 Calendar이나 Date 객체가 공유되면 한 곳에서 바꾼 값이 다른 곳 에서도 바뀌는 부작용이 생길 수있다.

- 멀티 쓰레드 환경에서 안전하지 못하다.


2. 윤초와 같은 특별한 상황을 고려하지 않았다.


3. 상수 필드 남용

calendar.add( Calendar.SECOND , 2 ); Calendar.JUNE 과같이 의미없는 상수가 들어가도 컴파일 시점에서 걸러낼 수가 없다.


4. 헷갈리는 월 지정

- 위에서 본 것 처럼 월의 시작이 0이다. 그래서 6월을 쓰려면 calendar.set(1582, 5 , 4); 이렇게 써야하고

calendar.set(1582, Calendar.JUNE , 4); 같은 상수도 만들었다 (Calendar.JUNE는 5가 찍힌다.)

일부러 가독성을 높이기위해 calendar.set(1582, 6-1 , 4); 이렇게 쓰기도 한다고 한다.


5. 일관성 없는 요일 상수

Calendar는 일요일부터 1~7 이고 Date는 일요일이 0으로 시작한다. 두 클래스는 빈번히 서로를 불러내는데 두 클래스 사이의 일관성이 없다.

6. 겹치는 클래스명

-java.util.Date  java.sql.Date클래스는 전자의 상속을 받는 클래스다. 그런데 클래스명이 같다니...

이렇게 단점이 많았다고 한다.

 그 후 jdk1.8 버전에서 드디어 개선된 java.time패키지가 등장했다. Joda-Time 라이브러리의 영향을 많이 받았고 기존 클래스들의 단점을 극복한 새로운 API라고 한다.

 아직 Date와 Calendar가 쓰이긴 하지만 구지 앞으로 안쓰여질 클래스에대해 깊게 알아보는것 보다 앞으로 쓰여질 클래스에 대해 공부하는것이 낫겠다 생각이들어 java.time패키지를 알아보려 한다.



- java.time패키지의 핵심 클래스


LocalDate : 날짜를 나타내는 클래스

LocalTime : 시간을 나타내는 클래스

LocalDateTime : 날짜, 시간을 모두 표현하는 클래스

ZonedDateTime : LocalDateTime + 시간대까지 표현하는 클래스.


Period : 두 날짜간의 차이를 표현하기 위한 클래스

Duration : 두 시간의 차이를 표현하기 위한 클래스


이들 클래스의 특징은 String클래스처럼 불변이라는 것이다. 

그래서 날짜나 시간을 변경하는 메서드들은 기존의 객체를 변경하는 대신 항상 새로운 객체를 반환한다.



- 객체 생성하기 


 now() 현재 날짜와 시간을 저장하는 객체 생성


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.time.*;
 
public class DateTime {
    public static void main(String args[]) {
    
        
        LocalDate date = LocalDate.now();
        LocalTime time = LocalTime.now();
        LocalDateTime dateTime = LocalDateTime.now();
        ZonedDateTime zonedDateTime = ZonedDateTime.now();
        
        System.out.println(date); //2018-10-01        
        System.out.println(time); //21:45:54.575
        System.out.println(dateTime); //2018-10-01T21:45:54.575
        System.out.println(zonedDateTime); //2018-10-01T21:45:54.576+09:00[Asia/Seoul]        
    }
}
cs

 of() 설정해주기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.time.*;
 
public class DateTime {
    public static void main(String args[]) {
    
        LocalDate date = LocalDate.of(20150318);
        LocalTime time = LocalTime.of(113030);
        LocalDateTime dateTime = LocalDateTime.of(date, time);
        ZonedDateTime zonedDateTime = ZonedDateTime.of(dateTime, ZoneId.of("Asia/Seoul"));
        
        System.out.println(date); //2015-03-18
        System.out.println(time); //11:30:30
        System.out.println(dateTime); //2015-03-18T11:30:30
        System.out.println(zonedDateTime); //2015-03-18T11:30:30+09:00[Asia/Seoul]
    }
}
 
cs




- LocalDate와 LocalTime


 java.time 패키지의 가장 기본이 되는 클래스

위에서 보았던 now() of()는 static 메서드로 구현

of()는 다음과 같이 여러가지로 오버로딩 되어있음

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


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

public static LocalTime of(int hour,int minute)


public static LocalTime of(int hour, int minute, int second)


public static LocalTime of(int hour,int minute, int second, int nanoOfSecond)



-특정 필드의 값 가져오기 getxxx()


간단히 get~() 메서드로 호출이 가능하다. Calendar와 달리 월의 범위는 1~12이고 요일도 월~일 1~7 이다.



                     LocalDate  

 int getYear() 

 년도(2000)

 int getMonthValue()

 월(12) 

 Month getMonth() 

 월(DECEMBER) 

 int getDayOfMonth()

 일(31)

 int getDayOfYear()

 같은 해의 1월 1일부터 몇번째 일(365)

 DayOfWeek getDayOfWeek()

 요일(FRIDAY) .getValue()=5

 int lengthOfMonth()

 같은 달의 총 일수(31)

 int LengthOfYear()

 같은 해의 총 일수(365), 윤년이면 366

 boolean isLeapYear()

 윤년여부 확인 (false) 


LocalTime

 int getHour()

 시(23) 

 int getMinute() 

 분(59) 

 int getSecond() 

 초(59) 

 int getNano() 

 나노초(0) 


등이 있다..


필드의 값 변경하기 widh(), plus(), minus()


LocalDate withYear(), Month(), DayOfMonth()....

LocalTime withHour(), Minute(), Second().....  를 이용해 개별 필드 값을 변경 할 수 있다.


위에서 언급 했다시피 객체의 값을 변경하는 것이 아닌 새로운 객체를 생성해서 반환하므로 대입연산자가 필요하다.


1
2
date = date.withYear(2012); // 년도를 2012년으로 변경
time = time.withHour(12);  // 시간을 12시로 변경
cs


LocalDate plusYears(), Months(), Days(), Weeks() ...
LocalTime plusHours, Minutes(), Seconds(), ... 

마찬가지로 대입연사자 필요

1
2
date = date.plusYears(10); // 10년 후
time = time.plusHours(5); // 5시간 후
cs



-날짜와 시간의 비교 isAfter(), isBefore(), isEqual()


예제를 보면 이해가 간다.


1
2
3
4
5
6
7
8
9
10
LocalDate date = LocalDate.of(20150318);
LocalDate date2 = LocalDate.of(20150319);
 
boolean b = date.isAfter(date2);
boolean b1 = date.isBefore(date2);
boolean b2 = date.isEqual(date2);
 
    System.out.println(b); //false
    System.out.println(b1); //true
    System.out.println(b2); //false
cs




-LocalDateTiem과 ZonedDateTime


LocalDate           +      LocalTime        ->  LocalDateTime

LocalDateTime     +     시간대              -> ZonedDateTime


객체 생성법은 위에서 처럼 now() of()를 사용하면 된다.

역시 다양하게 of()가 오버라이딩 되어있다.



LocalDateTime은 LocalDate와 LocalTime을 합쳐서 만들 수도 있지만

만든걸 각각 변환 할 수도 있다.


1
2
3
4
5
6
7
8
9
LocalDate date = LocalDate.of(20150318);
LocalTime time = LocalTime.of(113030);
 
LocalDateTime lo = LocalDateTime.of(date, time); // 만들기
LocalDateTime lo = date.atTime(time); //atTime()
LocalDateTime lo = time.atDate(date); //atDate()
    
LocalDate date = lo.toLocalDate(); // todate
LocalTime time = lo.toLocalTime(); // totime
cs



ZonedDateTime 역시 합쳐서 또는 분해가 된다..


1
2
3
LocalDateTime lo = LocalDateTime.now(); 
ZoneId zid = ZoneId.of("Asia/Seoul");
ZonedDateTime zdt = lo.atZone(zid); //atZone으로 합치기
cs

1
2
3
LocalDate date = zdt.toLocalDate(); 
LocalTime time = zdt.toLocalTime(); 
LocalDateTime lo = zdt.toLocalDateTime(); // 분해
cs


-OffsetDateTime
ZonedDateTime 과의 차이점은 일광절약시간 (써머타임/하절기에 표준시를 원래 시간보다 한 시간 앞당긴 시간을 쓰는 것을 말한다) 규칙을 
포함 하느냐 안하느냐의 차이다. 컴퓨터에게 계절별로 시간을 더했다 뺐다 하는건 안좋으므로 변화 없이 시간체계를 유지하는 것이 안전하다.

1
OffsetDateTime odt = zdt.toOffsetDateTime();
cs



TemporalAdjusters


이달의 4번째 금요일은 며칠인지, 지난 주 토요일이 며칠인지와 같은 날짜계산을 하기위한 클래스이다.


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번째 요일
LocalDate with(TemporalAdjuster adjuster) 메소드를 사용
1
2
LocalDate today = LocalDate.now();
LocalDate nextMonday = today.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
cs
이런식으로 사용하면 된다.


Period와 Duration 클래스


앞서 잠시 언급한 것과 같이 Period는 날짜의 차이를 Duration은 시간의 차이를 계산하기 위한 클래스이다.


- between() static 메소드이며 다음과 같이 구현한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
        LocalDate date = LocalDate.of(20150318);
        LocalDate date2 = LocalDate.of(20180420);
        LocalTime time = LocalTime.of(113030);
        LocalTime time2 = LocalTime.of(123550);
        
        Period pe = Period.between(date, date2);
        Duration du = Duration.between(time, time2);
        
        System.out.println(pe.getYears()); //3
        System.out.println(pe.getMonths()); //1
        System.out.println(pe.getDays()); //2
        
        System.out.println(du.getSeconds()); // 3920
cs


애석하게도 Duration에는 시, 분 차이를 구하는 메소드가 없다.. getSeconds() 와 getNano() 뿐이다.

물론 나누고 계산해서 시 분 을 구할 수 있겠지만 불편하다.


1
2
3
4
5
    LocalTime se = LocalTime.of(00).plusSeconds(du.getSeconds());
        int hour = se.getHour();
        int min = se.getMinute();
        int sec = se.getSecond();
        int nano = se.getNano();
cs

이 방법을 사용하면 된다. (그나저나 왜 안만들었지?;;)



- between()와 비슷한 기능을하는 until()도 있다. 다른점은 이 메서드는 인스턴스 메서드라는 것이다.


1
2
3
4
5
6
Period pe = Period.between(date, date2);
Period pe2 = date.until(date2);
long a = date.until(date2, ChronoUnit.DAYS);
 
Duration du = Duration.between(time, time2);
long b = time.until(time2, ChronoUnit.HOURS); //아까와는 다르게 시간과 분을 바로 구할 수 있다.
Time은 until()을 써보자!!
cs

until()메서드는 Duration 반환 타입이 없다. 매개변수가 2개인 until()은 D-Day를 구할때 좋다. 



- of(), with() / 역시 이 클래스에도 설정, 변경 메서드인 of와 with가 있다!





1
2
Period pe = pe.withYears(2);//, Months(), Days()
Duration du = du.withSeconds(2); //Nanos()
cs



아까 LocalDateTime과 ZonedDateTime처럼 to메서드로 변환이 가능하다.


1
2
3
4
5
6
7
8
9
10
    LocalDate date = LocalDate.of(20150318);
    LocalDate date2 = LocalDate.of(20180420);
    LocalTime time = LocalTime.of(113030);
    LocalTime time2 = LocalTime.of(123550);
    
    Period pe = Period.between(date, date2);
    Duration du = Duration.between(time, time2);
        
    System.out.println(pe.toTotalMonths()); //37
    System.out.println(du.toHours());       //1
cs

 Period

 long toTotalMonths()

 년원일을 월단위로 변환 후 반환(일 단위 무시)

Duration

 long toDays()

 일단위로 변환해서 반환

 long toHours()

 시간단위로 변환해서 반환

 long toMinutes()

 분단위로 변환해서 반환

 long toMillis()

 천분의 일초 단위로 변환해서 반환

 long toNanos()

 나노초 단위로 변환해서 반환




참조


https://d2.naver.com/helloworld/645609


+ Recent posts