Programming/JAVA

LocalDate, LocalTime, LocalDateTime

통통만두 2020. 6. 4. 10:16
반응형

Java Date 관련 흐름순

[java.util.Date](http://java.util.Date)java.util.Calendarjava.time(org.joda.time)


Calender, Date 를 피하는 이유

  • 불변 객체가 아니다

    • set으로 변경이 가능하다는 점은 누군가 악의적으로 변경할 수 있기 때문에 get/set method에서 직접 Date 클래스를 사용하는 것이 위험하다
      • Calendar 객체나 Date 객체가 여러 객체에서 공유되면 한 곳에서 바꾼 값이 다른 곳에 영향을 미치는 부작용이 생길 수 있다.
    • C#, Python 같은 언어에서는 날짜 클래스가 한 번 생성된 이후에는 내부 속성을 변경할 수 없다.
  • 상수필드 남용

    • calendar.add(Calendar.SECOND, 2);
    • 첫 번째 파라미터에 Calendar.JUNE과 같이, 전혀 엉뚱한 상수가 들어가도 이를 컴파일 시점에서 확인할 방법이 없다.
    • Calendar 클래스에는 많은 int 상수가 쓰였는데, 월, 요일 지정 등에서도 많은 혼란을 유발한다.
  • 헷갈리는 월 지정

    • 1월을 0으로 표현하는 문제, Calendar.JUNE로 월을 지정하지만 실질적인 값은 5 (≠6)인 문제

    • JDK 1.0에서 Date 클래스는 1월을 0으로 표현했고, JDK 1.1부터 포함된 Calendar 클래스도 이러한 관례를 답습하였음.

    • 2020년 6월 4일을 표현하는 코드를 아래와 같이 쓰는 실수를 많은 개발자들이 반복하고 있음. (설마 나만?)

        // o
        calendar.set(2020, Calendar.JUNE, 4);
      
        // x
        calendar.set(2020, 6, 4);
      
        // 가독성을 높이기 위한 꼼수
        calendar.set(2020, 6 - 1, 4);
    • 2020년 12월 31을 지정하려고 했지만 결과는 2021년 1월 31일이 나온다. (컴파일러에서 오류를 반환하지 않기 때문에 이런 실수를 인지하기 더욱 어렵다)

        @Test
        public void PublicTest() throws Exception {
            Calendar calendar = Calendar.getInstance();
            calendar.set(2020, 12, 31);
      
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            System.out.println(sdf.format(calendar.getTime()));
        }
  • 일관성 없는 요일 상수

    • 어디서는 일요일이 0, 어디서는 일요일이 1
    • Calendar.get(Calendar.DAY_OF_WEEK) 함수에서 반환한 요일은 int값으로, 일요일이 1로 표현된다.
    • 보통 Calendar.SUNDAY 상수와 비교해서 확인한다. 그런데 calendar.getTime()메소드로 Date 객체를 얻어와서 Date.getDay() 메소드로 요일을 구하면 일요일은 0이 된다.
  • Date와 Calendar 객체의 역할 분담

    • 년, 월, 일 계산은 Date 클래스만으로는 부족해서 왔다갔다 하는 문제
    • Calendar객체를 생성하고 Date객체를 생성하는 프로세스를 거치기 때문에 번거롭고 생성비용이 높다.

LocalDate, LocalTime, LocalDateTime

  • 위와 같은 문제를 해결하기 위해서 JAVA 8 버전부터는 LocalDate, LocalTime, LocalDateTime 라는 클래스가 생겼다.
    • joda time의 영향을 받아 비슷하게 설계되었다고 한다.
    • 그 밖에 Time and Money 라이브러리나 ICU등 여러 오픈소스 라이브러리를 참고했다고 한다.
  • ISO 8601

사용법

현재 날짜 & 시간

@Test
public void PublicTest() throws Exception {
    // LocalDate
    LocalDate ld = LocalDate.now();
    System.out.println(ld); // 2020-06-04

    ld = LocalDate.of(2020, 6, 4);
    System.out.println(ld); // 2020-06-04

    // LocalTime
    LocalTime lt = LocalTime.now();
    System.out.println(lt); // 16:51:55.428

    // 4번째 인자는 nanoSecond, 생략 가능
    lt = LocalTime.of(14, 01, 02, 03);
    System.out.println(lt); // 14:01:02.000000003

    // LocalDateTime
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt); // 2020-06-03T17:00:04.808

    ldt = LocalDateTime.of(2020, 6, 3, 14, 1, 2, 3);
    System.out.println(ldt); // 2020-06-03T14:01:02.000000003
}

날짜계산

@Test
public void PublicTest() throws Exception {
    // plus, minus
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt); // 2020-06-03T17:08:20.876

    ldt = ldt.plusYears(1);
    System.out.println(ldt); // 2021-06-03T17:08:20.876

    ldt = ldt.plusMonths(1);
    System.out.println(ldt); // 2021-07-03T17:08:20.876

    ldt = ldt.plusWeeks(1);
    System.out.println(ldt); // 2021-07-10T17:08:20.876

    ldt = ldt.plusDays(1);
    System.out.println(ldt); // 2021-07-11T17:08:20.876

    ldt = ldt.plusHours(1);
    System.out.println(ldt); // 2021-07-11T18:08:20.876

    ldt = ldt.plusMinutes(1);
    System.out.println(ldt); // 2021-07-11T18:09:20.876

    ldt = ldt.plusSeconds(1);
    System.out.println(ldt); // 2021-07-11T18:09:21.876

    ldt = ldt.plusNanos(1);
    System.out.println(ldt); // 2021-07-11T18:09:21.876000001
}

날짜 & 시간 비교

@Test
public void PublicTest() throws Exception {
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt); // 2020-06-03T17:17:50.195

    LocalDateTime before = LocalDateTime.of(2019, 6, 4, 14, 1, 2);
    System.out.println(before); // 2019-06-04T14:01:02

    LocalDateTime after = LocalDateTime.of(2021, 6, 4, 14, 1, 2);
    System.out.println(after); // 2021-06-04T14:01:02

    System.out.println(ldt.isBefore(before)); // false
    System.out.println(ldt.isBefore(after)); // true

    System.out.println(ldt.isAfter(before)); // true
    System.out.println(ldt.isAfter(after)); // false

    System.out.println(ldt.equals(ldt)); // true
}

날짜 차이 계산

@Test
public void PublicTest() throws Exception {
    LocalDate start = LocalDate.now();
    System.out.println(start); // 2020-06-03

    LocalDate end = LocalDate.of(2021, 6, 1);
    System.out.println(end); // 2021-06-01

    Period period = Period.between(start, end);

    System.out.println(period.getYears()); // 0
    System.out.println(period.getMonths()); // 11
    System.out.println(period.getDays()); // 29
}

@Test
public void PublicTest() throws Exception {
    LocalDate start = LocalDate.now();
    System.out.println(start); // 2020-06-03

    LocalDate end = LocalDate.of(2021, 6, 1);
    System.out.println(end); // 2021-06-01

    System.out.println(ChronoUnit.DAYS.between(start, end)); // 363
}

시간 차이 계산

@Test
public void PublicTest() throws Exception {
    LocalTime start = LocalTime.now();
    System.out.println(start); // 18:42:48.674

    LocalTime end = LocalTime.of(23, 5, 11);
    System.out.println(end); // 23:05:11

    Duration duration = Duration.between(start, end);
    System.out.println(duration.getSeconds()); // 15742
}

날짜 포맷

@Test
public void PublicTest() throws Exception {
    LocalDateTime ldt = LocalDateTime.now();
    System.out.println(ldt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"))); // 2020-06-03 06:45:18
    System.out.println(ldt.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 hh시 mm분 ss초"))); // 2020년 06월 03일 06시 45분 18초
}

날짜 변환

@Test
public void PublicTest() throws Exception {
    String date = "2020-06-03";
    System.out.println(LocalDate.parse(date)); // 2020-06-03
    System.out.println(LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"))); // 2020-06-03
}

참고


[1] : https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html

반응형