결론 : 날짜 관련 코드짤 때 Date, Calendar 쓰지 마세요!
Date(jdk 1.0), Calendar(jdk 1.1) 클래스의 문제점
- 불변 객체가 아님.
setter가 존재하므로 Calendar나 Date 객체가 여러 객체에서 공유되면 한 곳에서 바꾼 값이 다른 곳에 영향을 미칠 수 있음.
- int 상수 필드의 남용.
CalendarSECOND 같은 상수 필드때문에, 여기에 Calendar.JUNE 같은 엉뚱한게 들어가도 컴파일 시점에 확인할 방법이 없음.
- 헷갈리는 월 지정.
Date 클래스에서 1월을 0부터 표현하며, Calendar에서도 마찬가지. 따라서 1582년 10월 4일은 다음과 같이 작성해야 하며 당연히 휴먼에러가 많이 나옴.
calendar.set(1582, 9, 4);
- 일관성 없는 요일 상수
Date와 Calendar 두 클래스 사이에 요일 지정값에 일관성이 없음.
calendar.set(2014, Calendar.JANUARY, 1);
calendar.get(Calendar.DAY_OF_WEEK); // 4 (=Calendar.WEDNESDAY)
calendar.getTime().getDay(); // 3 (getTime()시 Date 객체 획득 후 getDay() 호출)
- Date와 Calendar의 불편한 역할 분담
jdk 1.0에서는 Date가 날짜 연산을 지원하는 유일한 클래스였음. jdk 1.1에서 Calendar가 추가되면서 Date의 많은 기능이 deprecated되었지만, 날짜 연산을 위해 Calendar객체를 생성하고 다시 Calendar에서 Date를 생성하는 등 불필요한 중간 객체가 생성됨.
// 흔히 "자바 yyyymmdd" 처럼 검색하면 구글 상단 검색에 뜨는 자료들의 형태
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar calendar = Calendar.getInstance(); //Calendar
String today = sdf.format(calendar.getTime());// getTime()의 결과는 Date 객체
- 등등
시간대의 ID를 “Asia/Seoul” 대신 “Seoul/Asia”로 해도 오류가 발생하지 않음.
java.sql.Date클래스는 상위 클래스인 java.util.Date와 클래스명이 동일함.
기존 Date, Calendar 대체 라이브러리들 (java 8 이전)
- joda-time, Time and Money Code Library, CalendarDate, date4j 등
- 클린코드 16장에 나온 SerialDate도 이런 것 중 하나.
- 이하 jdk8에서 개선된 날짜 API는 joda-time에 가장 많은 영향을 받음.
- 현재 jdk8 이상을 쓴다면 얘네도 쓸 이유 없음.
jdk 8 에서 개선된 날짜 API
- 전부 불변 객체.
- 종류
- [prefix]Date : 날짜정보 (2023-02-05)
- [prefix]Time : 시간정보 (20:46:12)
- [prefix]DateTime : 날짜+시간 (2023-02-05 20:46:12)
- [prefix]
- Local : 타임존 개념이 필요없는 날짜 정보. 일반적으로 사용.
- Offset : (Local+ZoneOffset) UTC 기준으로 시간 표현. 우리나라면 UTC +09:00. DB나 네트워크 통신 적합
- Zoned : (Offset+ZoneRegion) Timezone (Asia/Seoul) 포함. Asia/Seoul과 Asia/Tokyo 모두 UT+9. DST(Daylight Saving Time; 일광 절약 시간제) 정보 포함. 글로벌 서비스에 적합.
- prefix는 예를들어서 [prefix]Date는 LocalDate, OffsetDate, ZonedDate가 있음을 나타냄.
- 사용 예시
/**
* LocalDate
*/
// 현재 날짜 기준(now) 생성
LocalDate localDate = LocalDate.now(); // 2023-02-05 (현재 날짜)
localDate.getYear(); // 2023
localDate.getMonthValue(); // 2
localDate.getDayOfMonth(); // 5
// 원하는 날짜 기준 생성
LocalDate.of(2023, 2, 5); // 2023-02-05 (원하는 날짜 넣기)
// String 형태의 날짜 기준 생성
LocalDate.parse("2023-02-05");
LocalDate.parse("20230205", DateTimeFormatter.BASIC_ISO_DATE); // BASIC_ISO_DATE은 날짜쪽에 대해서만 사용 가능.
// 원하는 포맷으로 String 출력
LocalDate.of(2023, 2, 5).format(DateTimeFormatter.ISO_LOCAL_DATE); // 2023-02-05 (LocalDate toString 시 default 형태)
LocalDate.of(2023, 2, 5).format(DateTimeFormatter.BASIC_ISO_DATE); // 20230205
LocalDate.of(2023, 2, 5).format(DateTimeFormatter.ofPattern("yyMMdd")); // 230205
/**
* LocalTime
*/
// 현재 시각 기준 생성
LocalTime localTime = LocalTime.now();
localTime.getHour(); // 22
localTime.getMinute(); // 29
localTime.getSecond(); // 46
localTime.getNano(); // 663539100
// 원하는 시각 기준 생성
localTime.of(22, 32); // 22:32
localTime.of(22, 32, 10); // 22:32:10
localTime.of(22, 32, 10, 1234); // 22:32:10.000001234
// String 형태의 날짜 기준 생성
LocalTime.parse("22:32:10.000001234"); // 22:32:10.000001234
// 원하는 포맷으로 String 출력 (BASIC_ISO_DATE 포맷은 사용 불가)
LocalTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME);
LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")); // 22:37:32
/**
* LocalDateTime
*/
// 현재 날짜 기준(now) 생성
LocalDateTime localDateTime = LocalDateTime.now(); // 2023-02-05T22:38:32.033365200
localDateTime.getYear(); // 2023
localDateTime.getMonthValue(); // 2
localDateTime.getDayOfMonth(); // 5
localDateTime.getHour(); // 22
localDateTime.getMinute(); // 38
localDateTime.getSecond(); // 32
localDateTime.getNano(); // 33365200
// 원하는 날짜 기준 생성
LocalDateTime.of(2023, 2, 5, 22, 38, 32); // 2023-02-05T22:38:32
// String 형태의 날짜 기준 생성
LocalDateTime.parse("2023-02-05T22:38:32"); // 2023-02-05T22:38:32
LocalDateTime.parse("2023-02-05 22:38:32", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2023-02-05T22:38:32
// 원하는 포맷으로 String 출력
LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME); // 2023-02-05T22:44:05.5575867
LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE); // 20230205
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2023-02-05 22:44:29
/**
* 날짜 관련 연산
*/
LocalDate.now().plusDays(1).getDayOfWeek(); // MONDAY (now()는 2023-02-05로 일요일임.)
LocalTime.now().minusHours(1).getHour(); // 21 (현재 오후 10시임)
LocalDateTime.now().minusYears(5); //2018-02-05T22:51:04.753285700
LocalDate.now().isAfter(LocalDate.parse("2023-02-04")); // true (현재는 2023-02-05)
LocalDateTime.now().isBefore(LocalDateTime.now().plusDays(1)); // true
- 스프링부트에서 사용 예시
@RestController
@RequestMapping("/status")
public class StatusCheckController {
@GetMapping
public ResponseEntity<String> serverStatusCheck(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
return ResponseEntity.ok(date.format(DateTimeFormatter.BASIC_ISO_DATE));
}
}
// Repository (data jpa) 에서 start~end에 가입된 Member 리스트 리턴
List<Member> findByRegisterDateBetween(LocalDate start, LocalDate end);
references
https://d2.naver.com/helloworld/645609
https://jeong-pro.tistory.com/163
https://www.baeldung.com/java-zoneddatetime-offsetdatetime
https://sujl95.tistory.com/86
https://www.daleseo.com/java8-local-date-time/
'Development > Java' 카테고리의 다른 글
[객사오 정리] 1장. 협력하는 객체들의 공동체 (0) | 2023.04.26 |
---|---|
기본적인 자바 람다(Lambda) (0) | 2023.04.03 |
자바에서 N개짜리 배열 생성은 O(N)이 걸린다. (C++, C 도 마찬가지) (2) | 2023.03.13 |
자바에서 문자열 합칠 때 '+' 연산을 쓰지 마세요! (StringBuilder, StringJoiner, String.join, StringBuffer) (0) | 2023.03.07 |
[이펙티브 자바 정리] 2장 - 객체 생성과 파괴 (2) | 2023.02.17 |
댓글