[JAVA] 두 날짜 사이의 기간 구하기
1. 전체 예시 코드
public void betweenDates() {
/* 기간 시작일 */
String startDateStr = "2021-02-11";
/* 기간 마감일 */
String endDateStr = "2021-04-11";
/**
* 1) SimpleDateFormat 객체를 생성하면서 Date 데이터를 읽을 수 있는 패턴을 입력한다.
*/
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
try {
/**
* 2) 생성한 SimpleDateFormat 객체의 parse 메소드를 통해 String을 Date로 형 변환한다.
*/
Date startDate = formatter.parse(startDateStr);
Date endDate = formatter.parse(endDateStr);
/**
* 3) Date의 getTime 메소드를 통해 해당 날짜들을 milliseconds로 변환한 다음
* 마감일에서 시작일을 뺀다.
* 어느 것이 마감일이고 시작일인지 모를 경우, Math.abs()를 통해 절댓값을 구한다.
*/
long duration = endDate.getTime() - startDate.getTime();
long durationAbsolute = Math.abs(startDate.getTime() - endDate.getTime());
/**
* 4) milliseconds에 1000, 60 , 60, 24를 나눠서 일 단위로 바꾼다.
*/
long days = duration / (1000 * 60 * 60 * 24);
/**
* 5. 필요할 경우 int로 캐스팅한다.
*/
int daysInt = Math.toIntExact(days);
/* 출력 */
System.out.println("startDate: " + startDate);
System.out.println("endDate: " + endDate);
System.out.println("duration: " + duration);
System.out.println("durationAbsolute: " + durationAbsolute);
System.out.println("long days: " + days);
System.out.println("int days: " + daysInt);
} catch (ParseException e) {
e.printStackTrace();
}
}
2. 과정
[조건] String 타입의 두 날짜 데이터가 있을 경우
/* 기간 시작일 */
String startDateStr = "2021-02-11";
/* 기간 마감일 */
String endDateStr = "2021-04-11";
1) SimpleDateFormat 객체를 패턴과 함께 생성한다.
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
String 타입에서 Date 타입으로, Date 타입에서 String 타입으로 변환하는 것을 지원하는 SimpleDateFormat 클래스를 import한다. 그리고 formatter라는 변수에 SimpleDateFormat 객체를 생성한다.
SimpleDateFormat은 생성할 때 인수로 패턴을 넣으면 해당 패턴에 맞춰 데이터를 해석한다. Date 타입으로 변환하고자 하는 String 타입에 맞춰 패턴을 작성한다.
https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html
2) parse 메소드를 이용해 String은 Date 타입으로 변환한다.
try {
Date startDate = formatter.parse(startDateStr);
Date endDate = formatter.parse(endDateStr);
} catch (ParseException e) {
e.printStackTrace();
}
*1, *2는 헷갈리지 않기 위한 표시
생성된 객체(formatter)에서 parse 메소드(*1)를 이용하면 String 타입이 Date 타입으로 변환되어 리턴된다.
여기서 이용되는 parse 메소드(*1)는 SimpleDateFormat에서 정의된 것이 아니다. SimpleDateFormat은 DateFormat이라는 추상 클래스를 상속받기 때문에 DateFormat에 구현되어 있는 parse 메소드(*1)를 사용할 수 있다.
SimpleDateFormat에도 parse 메소드(*2)를 오버라이드해서 정의했지만 해당 메소드는 String 뿐만 아니라 ParsePosition이라는 두 개의 인수를 가지는 또 다른 parse 메소드(*2)를 오버라이드 한 것이다. 따라서 현재 사용하는 메소드는 DateFormat의 String 인수 하나를 받는 parse 메소드(*1)를 사용한 것이다.
이 메소드(*1)는 ParseExpection를 throws 하므로 try - catch 문 안에 들어간다.
https://docs.oracle.com/javase/8/docs/api/java/text/DateFormat.html#parse-java.lang.String-
3) Date 타입으로 변환된 두 데이터에 getTime 메소드를 사용한 뒤, 두 값의 차이를 구한다.
어느 쪽이 더 큰지 모르겠다면 Math.abs 를 이용해 절댓값을 구한다.
long duration = endDate.getTime() - startDate.getTime();
long durationAbsolute = Math.abs(startDate.getTime() - endDate.getTime());
getTime 메소드는 해당 Date일로부터 1970년 1월 1일 00:00:00 GMT까지의 시간을 밀리 세컨드로 구한다.
따라서 1970년 1월1일부터 기간 시작일까지의 시간, 1970년 1월 1일부터 기간 마감일까지의 시간을 빼면 시작일부터 마감일까지의 시간을 밀리 세컨드로 구할 수 있다.
4) 밀리세컨드 변환값을 일 단위로 바꾼다.
long days = duration / (1000 * 60 * 60 * 24);
초 : 밀리세컨드 / 1000
분 : 초 / 60
시간 : 분 / 60
일 : 시간 / 24
를 이용하면 일 단위를 구할 수 있다.
+5) 필요에 따라 int로 변환한다.
int daysInt = Math.toIntExact(days);
long을 int로 변환할 경우 overflow가 일어날 수 있으므로 overflow가 일어나는 경우 exception을 발생시키는 Math.toIntExact메소드를 사용할 수 있다.
int daysInt = (int) days;
그럴 염려가 없는 경우는 간단하게 캐스팅할 수 있다.
3. 출력 결과
흔한 실수(사실 내가 한 실수) :
SimpleDateFormat 객체를 생성할 때 패턴 입력에 유의한다. 'yyyy-mm-dd'로 입력할 경우 m은 분'minuate' 이므로 정확한 계산이 되지 않는다.
댓글