자바 8에 새로운 날짜와 시간 API가 생긴 이유
mutable
하기 때문에 thead safe하지 않다.타입 안정성
이 없고, 월이 0부터 시작한다거나..
)자바 8에서 제공하는 Date-Time API
JSR-310 스팩의 구현체
를 제공한다.
기계용 시간 (machine time)
과 인류용 시간(human time)
으로 나눌 수 있다.
기계용 시간은 EPOCK (1970년 1월 1일 0시 0분 0초)부터 현재까지의 타임스탬프를 표현한다.
인류용 시간은 우리가 흔히 사용하는 연,월,일,시,분,초
등을 표현한다.
타임스탬프는 Instant를 사용한다.
특정 날짜(LocalDate), 시간(LocalTime), 일시(LocalDateTime)를 사용할 수 있다.
기간을 표현할 때는 Duration (시간 기반)과 Period (날짜 기반)를 사용할 수 있다.
DateTimeFormatter를 사용해서 일시를 특정한 문자열로 포매팅할 수 있다.
참고
자바8 이전에 Date API와 포멧 라이브러리 SimpleDateFormat 그리고 Calendlar를 살펴보자.
: String <-> Date
// String <-> Date
Date nowDate = new Date();
System.out.println("포맷 지정 전 : " + nowDate);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy년 MM월 dd일");
//원하는 데이터 포맷 지정
String strNowDate = simpleDateFormat.format(nowDate);
System.out.println("포맷 지정 후 : " + strNowDate);
// 일반 현재 날짜 및 시간
//long 타입으로 System.currentTimeMillis() 데이터를 받아야합니다
long time = System.currentTimeMillis();
SimpleDateFormat dayTime = new SimpleDateFormat("yyyy.MM.dd hh:mm:ss E요일");
String str = dayTime.format(new Date(time));
// 24시 형태 현재 날짜 및 시간
long time = System.currentTimeMillis();
SimpleDateFormat dayTime = new SimpleDateFormat("yyyy.MM.dd kk:mm:ss E요일");
String str = dayTime.format(new Date(time));
: String에서 <-> Calendar
SimpleDateFormat -> Calendar 클래스 -> String 를 이용하여 타입 변환을 할 수 있다.
✔ 주의사항)
년, 월, 일 -1 해줘야 한다!
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class DateAdd {
public static void main(String[] args) throws Exception{
String date = "20200801";
//1년 후 날짜
String addYear = AddDate(date, 1, 0, 0);
//1달 후 날짜
String addMonth = AddDate(date, 0, 1, 0);
//1일 후 날짜
String addDay = AddDate(date, 0, 0, 1);
System.out.println(addYear); //20210801
System.out.println(addMonth); //20200901
System.out.println(addDay); //20200802
}
private static String AddDate(String strDate, int year, int month, int day) throws Exception {
SimpleDateFormat dtFormat = new SimpleDateFormat("yyyyMMdd");
Calendar cal = Calendar.getInstance();
Date dt = dtFormat.parse(strDate);
cal.setTime(dt);
cal.add(Calendar.YEAR, year);
cal.add(Calendar.MONTH, month);
cal.add(Calendar.DATE, day);
return dtFormat.format(cal.getTime());
}
}
위의 예제와 같이 Calendar 객체의 add() 메서드를 사용하면 원하는 날짜를 계산할 수 있다.
만약 특정일 기준으로 원하는 날짜만큼 빼려면
원하는 만큼의 년, 월, 일에 -(마이너스)를 붙여 add 메서드 파라미터에 세팅한다.
//year 년 전
cal.add(Calendar.YEAR, -year);
//month 월 전
cal.add(Calendar.MONTH, -month);
//month 일 전
cal.add(Calendar.DATE, -day);
Or
//위의 메소드에 적용하면 다음과 같이 파라메터를 세팅하면된다.
//일년 전
AddDate(date, -1, 0, 0);
//한달 전
AddDate(date, 0, -1, 0);
//하루 전
AddDate(date, 0, 0, -1);
private String getFolderYesterDay() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1);
String str = sdf.format(cal.getTime());
return str.replace("-", File.separator);
}
: String <-> LocalDateTime
// String -> LocalDateTime
// @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
// @JsonFormat -> POST 방식일때!
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
@PastOrPresent // 현재까지 입력가능
private LocalDateTime startAt;
// @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
@PastOrPresent // 현재까지 입력가능
private LocalDateTime endAt;
private String startDate;
private String endDate;
// Stirng -> LocalDateTime
public LocalDateTime getStartDate() {
if (StringUtils.hasText(startDate)) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
return LocalDateTime.parse(startDate + "T00:00:00", formatter);
}
return null;
}
public LocalDateTime getEndDate() {
if (StringUtils.hasText(endDate)) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
return LocalDateTime.parse(endDate + "T23:59:59", formatter);
}
return null;
}
간혹 시간 필드에서 응답시 이런 값을 붙이면서 보내주는 경우가 있다.
이부분은 Date와 Time 구분값
이라 생각해주면 된다!
단순 띄어쓰기에서 더 확실한 구분값을 짓기 위해서다.
pattern = "yyyy-MM-dd'T'HH:mm:ss"
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
private LocalDateTime createdAt;
이 어노테이션이 적용된 필드의 값은 현재 또는 과거(or미래)의 시점에 해당되는 날짜 또는 시간
이어야 하는 validation이다!
@PastOrPresent
private LocalDateTime startTime;
LocalDate a = LocalDateTime.of(2012, 6, 30, 12, 00);
LocalDate b = LocalDateTime.of(2012, 7, 1, 12, 00);
a.isBefore(b) == true // b 이전이냐?
a.isBefore(a) == false
b.isBefore(a) == false
// 2. 쿠폰 유효기간 체크
if(now.isBefore(formatterLocalDateTime(couponDto.getBeginDt())) ||
now.isAfter(formatterLocalDateTime(couponDto.getEndDt()))) {
return CouponDto.CouponDetails.builder()
.couponStatus(CouponDto.CouponStatus.UNABLE)
.build();
}
간격을 두고 사이에 있는 조회하고 싶으면 startDay에 00:00:00
, endDay에 23:59:59
를 두고 설정해주어야 한다. 설정해주는 코드는 다음과 같다.
public LocalDateTime getParsedSearchDate1() {
return LocalDateTime.parse(searchDate1 + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
public LocalDateTime getParsedSearchDate2() {
return LocalDateTime.parse(searchDate2 + " 23:59:59", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
참고 - https://hianna.tistory.com/613
Date를 LocalDate 또는 LocalDateTime 객체로 변환하기 위해서 다음의 3단계를 거칩니다.
1) Date -> Instant
2) Instant -> ZonedDateTime
3) ZonedDateTime -> LocalDate, LocalDateTime
// 날짜 변환
// Date -> LocalDateTime
Date date = new Date();
LocalDateTime dateTime = date.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
System.out.println(dateTime); // 2022-12-09T20:28:36.436
// LocalDateTime -> Date
LocalDateTime localDateTime = LocalDateTime.now();
Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
Date date1 = Date.from(instant);
System.out.println(date1); // Fri Dec 09 20:28:36 KST 2022
스프링에서 어노테이션(@JsonFormat, @DateTimeFormat)을 이용한 JSON 객체 Date타입을 String으로 직렬화하는 어노테이션들이다. 둘이 비슷한 기능인데, 이번 기회에 정리해 볼려고 한다.
사용 설명에 앞서 @JsonFormat, @DateTimeFormat에 대해 정확한 정보를 찾다가 아래에 포스팅을 찾아는데 설명이 잘 되어있어서 들어가서 한번 정독하고 사용하면 더 도움이 될 듯 하다.
[참고] https://jojoldu.tistory.com/361
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime startDate;
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime endDate;
Spring MVC에서는 @DateTimeFormat, Spring REST API시에는 @JsonFormat을 사용해야 되는 것 같은데?
Spring Boot 2.0에서는 JSR 310이 의존성 기본적으로 되어있음
그래서 아래와 같이 따로 의존성을 추가 할 필요가 없다.
compile('com.fasterxml.jackson.datatype:jackson-datatype-jsr310')
@JsonFormat은 Jackson의 어노테이션 > 우선순위가 더 높아
@DateTimeFormat은 Spring의 어노테이션
- (GET) @DateTimeFormat
- (POST) @DateTimeFormat or @JsonFormat 사용하자
날짜 어노테이션