낙서
날짜(Date) 0x01
剛宇
2009. 2. 28. 13:17
지난 시간에 배운 지식을 기반으로 날짜유틸 클래스를 만들어보자.
1. Date를 Calendar로 변환하기
2. 년, 월, 일, 시, 분, 초를 입력받아 날짜형(Date)으로 만들기.
3. 문자열을 입력받아 날짜형(Date)으로 만들기.
- toDate(String) 메소드는 필자같은 게으름뱅이를 위해서 만듯것인데, 별로 추천할 메소드는 못된다.
4. 기준일의 다음주 월요일 가져오기.
5. 기준일이 속한 월의 마지막 날짜 가져오기.
6. 두 날짜(년/월/일)가 동일한지 비교하기.
7. 두 날짜간의 기간 구하기.
8. 두 날짜의 기간을 패턴에 의해서 년/월/일/시/분/초/밀리초로 출력하기.
- 별다른 방법이 생각나지 않는다. 그래서 숫자를 셀때 손가락을 구부리듯이, 기간 사이의 월수를 하나씩 세어서 계산하는 방법을 사용하겠다. 혹시 좋은 알고리즘이 있으면 알려주길 바란다.
- 일단 패턴을 분석을 하기 위한 토큰을 만들어 보겠다.
- 이 날짜패턴토큰을 가지고 기간을 구하는 메소드를 만들어 보자.
- 근데 잘보면, 버그가 존재한다. 귀찮아서 수정은 안하겠다. 한번 연구해보도록.
1. Date를 Calendar로 변환하기
/** * <p><code>java.util.Date</code>를 <code>java.util.Calendar</code>로 변환한다.</p> * * @param date * @return */ public static Calendar toCalendar(Date date) { Calendar calendar = Calendar.getInstance(); calendar.setTime(date); return calendar; }- 지난 시간에 배운거라서 별로 어려운게 없다.
2. 년, 월, 일, 시, 분, 초를 입력받아 날짜형(Date)으로 만들기.
/** * <p>년, 월, 일, 시, 분, 초를 입력받아 날짜형(Date)으로 변환한다.</p> * * @param year 년 * @param month 월(1-12) * @param day 일 * @param hour 시 * @param min 분 * @param sec 초 * @return */ public static Date toDate(int year, int month, int day, int hour, int minute, int second) { Calendar calendar = Calendar.getInstance(); calendar.set(year, month - 1, day, hour, minute, second); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTime(); } /** * <p>년, 월, 일, 시, 분을 입력받아 날짜형(Date)으로 변환한다.(0초로 설정된다)</p> * * @param year 년 * @param month 월(1-12) * @param day 일 * @param hour 시 * @param minute 분 * @return */ public static Date toDate(int year, int month, int day, int hour, int minute) { return toDate(year, month, day, hour, minute, 0); } /** * <p>년, 월, 일, 시을 입력받아 날짜형(Date)으로 변환한다.(0시 0분 0초로 설정된다)</p> * * @param year 년 * @param month 월(1-12) * @param day 일 * @return */ public static Date toDate(int year, int month, int day) { return toDate(year, month, day, 0, 0, 0); }- 지난 시간에 배운거라서 별로 어려운게 없다.
3. 문자열을 입력받아 날짜형(Date)으로 만들기.
// 자동 변환용 패턴 설정 protected static final String[] patterns = { "yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH", "yyyy-MM-dd", "yyyyMMddHHmmssSSS", "yyyyMMddHHmmss", "yyyyMMddHHmm", "yyyyMMddHH", "yyyyMMdd", "yyMMdd" }; private static DateFormat getDateFormat(String pattern) { return new SimpleDateFormat(pattern); } /** * <p>문자열을 입력받아 패턴에 맞게 날짜형(Date)으로 변환하여 반환한다.</p> * * <pre> * DateUtils.toDate("2008-11-11 06:15:00", "yyyy-MM-dd HH:mm:ss") = Date@"2008-11-11 06:15:00" * DateUtils.toDate("Time is Gold", "yyyy-MM-dd HH:mm:ss") = ParseException * DateUtils.toDate(null, *) = ParseException * </pre> * * @param dateStr * @param pattern * @return * @throws ParseException 변환을 실패할때 발생 */ public static Date toDate(String dateStr, String pattern) throws ParseException { if (dateStr == null) { return null; } DateFormat dateFormat = getDateFormat(pattern); Date date = dateFormat.parse(dateStr); if (dateStr.equals(dateFormat.format(date))) { return date; } else { throw new ParseException(MessageUtils.format( "Out of bound date:\"{0}\" with format \"{1}\"", dateStr, pattern), 0); } } /** * <p>문자열을 입력받아 패턴에 맞게 날짜형(Date)으로 변환하여 반환한다.</p> * <p>(변환을 실패할때는 기본 날짜를 반환한다.<)/p> * * <pre> * DateUtils.toDate("2008-11-11 06:15:00", "yyyy-MM-dd HH:mm:ss", *) = Date@"2008-11-11 06:15:00" * DateUtils.toDate("Time is Gold", "yyyy-MM-dd HH:mm:ss", toDate("2008-11-11 06:15:00")) = Date@"2008-11-11 06:15:00" * DateUtils.toDate(null, *, toDate("2008-11-11 06:15:00")) = Date@"2008-11-11 06:15:00" * </pre> * * @param dateStr * @param pattern * @param defaultDate 기본 날짜 * @return */ public static Date toDate(String dateStr, String pattern, Date defaultDate) { if (dateStr == null) { return defaultDate; } try { DateFormat dateFormat = getDateFormat(pattern); Date date = getDateFormat(pattern).parse(dateStr); if (dateStr.equals(dateFormat.format(date))) { return date; } } catch (ParseException e) { } return defaultDate; } /** * <p>문자열을 입력받아 날짜형(Date)으로 변환한다.</p> * * <pre> * DateUtils.toDate(null) = null * DateUtils.toDate("2008-11-11") = Date@"2008-11-11 00:00:00" * DateUtils.toDate("2008-11-11 06:15:00") = Date@"2008-11-11 06:15:00" * </pre> * * <h4>지원하는형식</h4> * <ul> * <li>yyyy-MM-dd HH:mm:ss.SSS * <li>yyyy-MM-dd HH:mm:ss * <li>yyyy-MM-dd HH:mm * <li>yyyy-MM-dd HH * <li>yyyy-MM-dd * <li>yyyyMMddHHmmssSSS * <li>yyyyMMddHHmmss * <li>yyyyMMddHHmm * <li>yyyyMMddHH * <li>yyyyMMdd * </ul> * @param dateStr * @return * @throws IllegalArgumentException 지원하지 않는 형식일 경우 */ public static Date toDate(String dateStr) throws IllegalArgumentException { if (dateStr == null) { return null; } dateStr = StringUtils.trim(dateStr); int dateStrLen = dateStr.length(); Date date = null; for (String pattern : patterns) { if (dateStrLen == pattern.length()) { DateFormat dateFormat = getDateFormat(pattern); try { date = dateFormat.parse(dateStr); if (dateStr.equals(dateFormat.format(date))) { return date; } else { date = null; } } catch (ParseException e) { date = null; } } } if (date == null) { throw new IllegalArgumentException(MessageUtils.format( "Illegal Argument Date String \"{0}\"", dateStr)); } return date; }- 날짜형(Date)을 문자열로 출력할때 SimpleDateFormat 클래스를 사용한것처럼, 문자열을 날짜형으로 변환할때도 SimpleDateFormat 클래스를 사용하면 손쉽게 변환할 수 있다. parse(String) 메소드를 사용하면 날짜형으로 변환이 되는데, 경험상으로봐서는 별로 정확(?)하지만 않다. 그래서 데이터가 정확한지 확인하는 아래 코드를 추가한 것이다.
Date date = dateFormat.parse(dateStr); if (dateStr.equals(dateFormat.format(date))) { }- 즉, 날짜형으로 변환한 값을 다시 해당 포맷으로 변환하면, 처음에 입력한 값과 같아야한다는것이다.
- toDate(String) 메소드는 필자같은 게으름뱅이를 위해서 만듯것인데, 별로 추천할 메소드는 못된다.
4. 기준일의 다음주 월요일 가져오기.
/** * <p>해당 일을 기준으로 명시된 요일의 다음 날짜를 계산한다.</p> * * <pre> * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.SUNDAY) = "2008-07-20 06:15:00" * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.MONDAY) = "2008-07-21 06:15:00" * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.SATURDAY) = "2008-07-26 06:15:00" * </pre> * * @param date * @param dayOfWeek SUNDAY=1, MONDAY=2, ... SATURDAY=7 * @return */ public static Date getNextDate(Date date, int dayOfWeek) { Calendar cal = toCalendar(date); int amount = (7 - cal.get(Calendar.DAY_OF_WEEK)) + dayOfWeek; cal.add(Calendar.DAY_OF_MONTH, amount); return cal.getTime(); } /** * <p>해당 일을 기준으로 명시된 요일의 다음 날짜를 계산한다.</p> * * <pre> * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.SUNDAY, false) = "2008-07-20 06:15:00" * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.SUNDAY, true) = "2008-07-20 00:00:00" * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.MONDAY, false) = "2008-07-21 06:15:00" * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.MONDAY, true) = "2008-07-21 00:00:00" * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.SATURDAY, false) = "2008-07-26 06:15:00" * DateUtils.getNextDay(toDate("2008-07-19 06:15:00"), Calendar.SATURDAY, true) = "2008-07-26 00:00:00" * </pre> * * @param date * @param dayOfWeek SUNDAY=1, MONDAY=2, ... SATURDAY=7 * @param resetTime 시간 초기화 여부. <code>true</code>이면 시:분:초 0:0:0으로 설정한다. * @return */ public static Date getNextDate(Date date, int dayOfWeek, boolean resetTime) { Calendar cal = toCalendar(date); if (resetTime) { cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); } int amount = (7 - cal.get(Calendar.DAY_OF_WEEK)) + dayOfWeek; cal.add(Calendar.DAY_OF_MONTH, amount); return cal.getTime(); }- cal.get(Calendar.DAY_OF_WEEK) 메소드를 이용해서 요일을 구한다음, 그 차이만큼 연산한면 된다.
5. 기준일이 속한 월의 마지막 날짜 가져오기.
/** * <p>해당 날짜가 속한 달의 마지막 날짜를 계산한다.</p> * * <pre> * DateUtils.getLastDay(toDate("2008-07-19 06:15:00"), Calendar.SUNDAY) = "2008-07-31 06:15:00" * </pre> * * @param date * @return */ public static Date getLastDate(Date date) { Calendar cal = toCalendar(date); int amount = cal.getActualMaximum(Calendar.DAY_OF_MONTH) - cal.get(Calendar.DAY_OF_MONTH); cal.add(Calendar.DAY_OF_MONTH, amount); return cal.getTime(); } /** * <p>해당 날짜가 속한 달의 마지막 날짜를 계산한다.</p> * * <pre> * DateUtils.getLastDay(toDate("2008-07-19 06:15:00"), Calendar.SUNDAY, false) = "2008-07-31 06:15:00" * DateUtils.getLastDay(toDate("2008-07-19 06:15:00"), Calendar.SUNDAY, true) = "2008-07-31 00:00:00" * </pre> * * @param date * @param resetTime 시간 초기화 여부. <code>true</code>이면 시:분:초 0:0:0으로 설정한다. * @return */ public static Date getLastDate(Date date, boolean resetTime) { Calendar cal = toCalendar(date); if (resetTime) { cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); } int amount = cal.getActualMaximum(Calendar.DAY_OF_MONTH) - cal.get(Calendar.DAY_OF_MONTH); cal.add(Calendar.DAY_OF_MONTH, amount); return cal.getTime(); }- cal.getActualMaximum(int)을 이용해서 해당일의 마지막일을 구한다음 현재일과의 차이를 연산해주면 된다.
6. 두 날짜(년/월/일)가 동일한지 비교하기.
/** * <p>두 달력의 날짜(시간 무시)가 일치하는지 판단한다.</p> * * @param cal1 * @param cal2 * @return */ public static boolean isSameDay(Calendar cal1, Calendar cal2) { if (cal1 == null) { return (cal2 == null); } if (cal1 == cal2) { return true; } return (cal1.get(Calendar.ERA) == cal2.get(Calendar.ERA) && cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1 .get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR)); } /** * <p>두 날짜의 날짜(시간 무시)가 일치하는지 판단한다.</p * * @param date1 * @param date2 * @return */ public static boolean isSameDay(Date date1, Date date2) { if (date1 == null) { return (date2 == null); } if (date1 == date2) { return true; } return isSameDay(toCalendar(date1), toCalendar(date2)); }- 시간 부분은 무시하고 년/월/일만 비교하는 메소드이다. 소스를 보면 Calendar.ERA의 값도 비교하게 되는데, Calendar.ERA는 율리우스력을 나타낸다. 즉 AD 또는 BC. 그리고 월과 일을 두번 비교하는게 아니라 DAY_OF_YEAR를 써서 한번에 끝내주는 센스~.
7. 두 날짜간의 기간 구하기.
public static final long MILLIS_PER_SECOND = 1000; public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; /** * <p>시작일부터 종료일까지의 기간을 ms로 계산한다.</p> * * <pre> * DateUtils.getBetween(toDate("2008-11-11 23:59"), toDate("2008-11-12 23:58")) = 86340000 * DateUtils.getBetween(toDate("2008-11-11 23:59"), toDate("2008-11-12 23:59")) = 86400000 * DateUtils.getBetween(null, *) = 0 * DateUtils.getBetween(*, null) = 0 * </pre> * * @param from 시작일 * @param to 종료일 * @return 기간의 1/1000초(ms) */ public static long getBetween(Date from, Date to) { if (from == null || to == null) { return 0; } return to.getTime() - from.getTime(); } /** * <p>시작일부터 종료일까지의 차이를 일(day)로 계산한다. * (시간 단위로 계산하기 때문에 24시간이 지나지않으면 하루로 계산하지 않는다.)</p> * * <pre> * DateUtils.getDaysBetween(toDate("2008-11-11"), toDate("2008-11-13")) = 2 * DateUtils.getDaysBetween(toDate("2008-11-11"), toDate("2008-11-11")) = 0 * DateUtils.getDaysBetween(toDate("2008-11-12"), toDate("2008-11-11")) = -1 * DateUtils.getDaysBetween(toDate("2008-11-11 23:59"), toDate("2008-11-12 23:58")) = 0 * DateUtils.getDaysBetween(toDate("2008-11-11 23:59"), toDate("2008-11-12 23:59")) = 1 * DateUtils.getDaysBetween(null, *) = 0 * DateUtils.getDaysBetween(*, null) = 0 * </pre> * * @param from 시작일 * @param to 종료일 * @return 기간의 일(day)수 */ public static int getDaysBetween(Date from, Date to) { return (int) (getBetween(from, to) / MILLIS_PER_DAY); }- 두 날짜의 기간을 일/시/분/초/밀리초로 구하는것은 아주 싶다. 밀리초로 두 값의 차이를 구한다음 해당 단위로 나누면 끝이다. 그런데, 몇 월이 지났나 계산하는것은 머리를 아프게 한다. 한달은 28일/30일/31일 다르기에 우리를 괴롭힌다. 하지만 여기서 포기할 수는 없으니 한번 도전해보도록하자.
8. 두 날짜의 기간을 패턴에 의해서 년/월/일/시/분/초/밀리초로 출력하기.
- 별다른 방법이 생각나지 않는다. 그래서 숫자를 셀때 손가락을 구부리듯이, 기간 사이의 월수를 하나씩 세어서 계산하는 방법을 사용하겠다. 혹시 좋은 알고리즘이 있으면 알려주길 바란다.
- 일단 패턴을 분석을 하기 위한 토큰을 만들어 보겠다.
package kr.kangwoo.util.date; import java.util.ArrayList; import java.util.List; import kr.kangwoo.util.StringUtils; public class DatePatternToken { private Object value; private int count; public DatePatternToken(Object value) { this(value, 1); } public DatePatternToken(Object value, int count) { this.value = value; this.count = count; } public void increment() { count++; } public Object getValue() { return value; } public int getCount() { return count; } @Override public boolean equals(Object obj) { if (obj instanceof DatePatternToken) { DatePatternToken another = (DatePatternToken)obj; if (this.value.getClass() != another.value.getClass()) { return false; } if (this.count != another.count) { return false; } if (this.value instanceof StringBuilder || this.value instanceof StringBuffer) { return this.value.toString().equals(another.value.toString()); } else { return this.value.equals(another.value); } } return false; } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { return StringUtils.repeat(value.toString(), count); } /** * <p>해당 패턴을 분석하여 토큰을 만든다.</p> * * @param pattern * @return */ public static DatePatternToken[] getTokens(String pattern) { int patternLen = pattern.length(); List<DatePatternToken> list = new ArrayList<DatePatternToken>(patternLen); DatePatternToken previous = null; StringBuilder buffer = null; boolean inQuote = false; for (int i = 0; i < patternLen; i++) { char ch = pattern.charAt(i); if(inQuote && ch != '\'') { buffer.append(ch); continue; } if (ch == '\'') { // espace 문자 if(inQuote) { if (buffer.length() == 0) { // '' 일때 처리 buffer.append(ch); } buffer = null; inQuote = false; } else { buffer = new StringBuilder(); list.add(new DatePatternToken(buffer)); inQuote = true; } } else if (ch == 'y' || ch == 'M' || ch == 'd' || ch == 'H' || ch == 'm' || ch == 's' || ch == 'S') { String value = String.valueOf(ch); if (previous != null && previous.getValue().equals(value)) { previous.increment(); } else { DatePatternToken token = new DatePatternToken(value); list.add(token); previous = token; } buffer = null; } else { if (buffer == null) { buffer = new StringBuilder(); list.add(new DatePatternToken(buffer)); } buffer.append(ch); } } return (DatePatternToken[])list.toArray(new DatePatternToken[list.size()]); } /** * <p>패턴 존재유무를 파악하여 반환한다.</p> * * @param tokens * @return boolean[hasYears, hasMonths, hasDays, hasHours, hasMinutes, hasSeconds, hasMilliSeconds] */ public static boolean[] containsPattern(DatePatternToken[] tokens) { boolean[] result = new boolean[7]; for (DatePatternToken token : tokens) { Object value = token.getValue(); if ("y".equals(value)) { result[0] = true; } else if ("M".equals(value)) { result[1] = true; } else if ("d".equals(value)) { result[2] = true; } else if ("H".equals(value)) { result[3] = true; } else if ("m".equals(value)) { result[4] = true; } else if ("s".equals(value)) { result[5] = true; } else if ("S".equals(value)) { result[6] = true; } } return result; } }
- 이 날짜패턴토큰을 가지고 기간을 구하는 메소드를 만들어 보자.
/** * <p>시작일부터 종료일까지의 기간을 패턴에 맞게 출력한다.('0'으로 패딩한다.)</p> * * <pre> * DaetUtils.toString(toDate("1978-07-19 06:15:00.000"), toDate("2004-11-11 07:19:01.004"), "yyyy-MM-dd- HH:mm:ss.SSS") = "0026-03-23 01:04:01.004"; * DaetUtils.toString(toDate("1978-07-19 06:15:00.000"), toDate("2004-11-11 07:19:01.004"), "MM-dd- HH:mm:ss.SSS") = "315-23 01:04:01.004"; * </pre> * * @param startDate 시작일 * @param endDate 종료일 * @param pattern 패턴(yyyyMMddHHMmmssSSS) * @return * @since 1.1 */ public static String toString(Date startDate, Date endDate, String pattern) { return toString(startDate, endDate, pattern, true); } /** * <p>시작일부터 종료일까지의 기간을 패턴에 맞게 출력한다.</p> * * <pre> * DaetUtils.toString(toDate("1978-07-19 06:15:00.000"), toDate("2004-11-11 07:19:01.004"), "yyyy-MM-dd- HH:mm:ss.SSS", true) = "0026-03-23 01:04:01.004"; * DaetUtils.toString(toDate("1978-07-19 06:15:00.000"), toDate("2004-11-11 07:19:01.004"), "yyyy-MM-dd- HH:mm:ss.SSS", false) = "26-3-23 01:04:01.004"; * DaetUtils.toString(toDate("1978-07-19 06:15:00.000"), toDate("2004-11-11 07:19:01.004"), "MM-dd- HH:mm:ss.SSS", true) = "315-23 01:04:01.004"; * </pre> * * @param startDate 시작일 * @param endDate 종료일 * @param pattern 패턴(yyyyMMddHHMmmssSSS) * @param padWithZeros '0'을 패딩할지 여부 * @return * @since 1.1 */ public static String toString(Date startDate, Date endDate, String pattern, boolean padWithZeros) { if (pattern == null) { return null; } DatePatternToken[] tokens = DatePatternToken.getTokens(pattern); boolean[] contains = DatePatternToken.containsPattern(tokens); boolean hasYears = contains[0], hasMonths = contains[1], hasDays = contains[2]; boolean hasHours = contains[3], hasMinutes = contains[4], hasSeconds = contains[5]; boolean needRevert = false; if (startDate.compareTo(endDate) > 0) { Date tempDate = startDate; startDate = endDate; endDate = tempDate; needRevert = true; } int years = 0; int months = 0; int days = 0; int hours = 0; int minutes = 0; int seconds = 0; int milliseconds = 0; if (hasMonths) { Calendar start = toCalendar(startDate); Calendar end = toCalendar(endDate); hours = end.get(Calendar.HOUR_OF_DAY) - start.get(Calendar.HOUR_OF_DAY); minutes = end.get(Calendar.MINUTE) - start.get(Calendar.MINUTE); seconds = end.get(Calendar.SECOND) - start.get(Calendar.SECOND); milliseconds = end.get(Calendar.MILLISECOND) - start.get(Calendar.MILLISECOND); while (milliseconds < 0) { milliseconds += 1000; seconds -= 1; } while (seconds < 0) { seconds += 60; minutes -= 1; } while (minutes < 0) { minutes += 60; hours -= 1; } while (hours < 0) { hours += 24; days -= 1; } int endDay = end.get(Calendar.DAY_OF_MONTH); if (end.get(Calendar.YEAR) > start.get(Calendar.YEAR)) { years += (end.get(Calendar.YEAR) - (start.get(Calendar.YEAR) + 1)); months += end.get(Calendar.MONTH); days += endDay; end.set(start.get(Calendar.YEAR), 11, 1); end.set(Calendar.DAY_OF_MONTH, end .getActualMaximum(Calendar.DAY_OF_MONTH)); } endDay = end.get(Calendar.DAY_OF_MONTH); if (end.get(Calendar.MONTH) > start.get(Calendar.MONTH)) { months += (end.get(Calendar.MONTH) - (start.get(Calendar.MONTH) + 1)); days += endDay; if (days >= start.getActualMaximum(Calendar.DAY_OF_MONTH)) { months++; days -= start.getActualMaximum(Calendar.DAY_OF_MONTH); } end.set(end.get(Calendar.YEAR), start.get(Calendar.MONTH), 1); end.set(Calendar.DAY_OF_MONTH, end .getActualMaximum(Calendar.DAY_OF_MONTH)); } days += (end.get(Calendar.DAY_OF_MONTH) - start .get(Calendar.DAY_OF_MONTH)); if (days >= start.getActualMaximum(Calendar.DAY_OF_MONTH)) { months++; days -= start.getActualMaximum(Calendar.DAY_OF_MONTH); } if (hasYears) { if (months > 11) { years += (months / 12); months = (months % 12); } } else { if (years != 0) { months += (years * 12); years = 0; } } } else { long period = getBetween(startDate, endDate); days = (int) (period / MILLIS_PER_DAY); hours = (int) ((period % MILLIS_PER_DAY) / MILLIS_PER_HOUR); minutes = (int) ((period % MILLIS_PER_HOUR) / MILLIS_PER_MINUTE); seconds = (int) ((period % MILLIS_PER_MINUTE) / MILLIS_PER_SECOND); milliseconds = (int) (period % MILLIS_PER_SECOND); } if (!hasDays) { hours += 24 * days; days = 0; } if (!hasHours) { minutes += 60 * hours; hours = 0; } if (!hasMinutes) { seconds += 60 * minutes; minutes = 0; } if (!hasSeconds) { milliseconds += 1000 * seconds; seconds = 0; } String result = format(tokens, years, months, days, hours, minutes, seconds, milliseconds, padWithZeros); return needRevert ? "-" + result : result; } /** * <p>패턴에 맞게 기간을 문자열로 출력한다.</p> * * @param tokens * @param years * @param months * @param days * @param hours * @param minutes * @param seconds * @param milliseconds * @param padWithZeros * @return * @since 1.1 */ protected static String format(DatePatternToken[] tokens, int years, int months, int days, int hours, int minutes, int seconds, int milliseconds, boolean padWithZeros) { StringBuilder result = new StringBuilder(); for (DatePatternToken token : tokens) { Object value = token.getValue(); int count = token.getCount(); if (value instanceof StringBuilder) { result.append(value.toString()); } else { if ("y".equals(value)) { result.append(padWithZeros ? StringUtils.leftPad(Integer .toString(years), count, "0") : Integer .toString(years)); } else if ("M".equals(value)) { result.append(padWithZeros ? StringUtils.leftPad(Integer .toString(months), count, "0") : Integer .toString(months)); } else if ("d".equals(value)) { result.append(padWithZeros ? StringUtils.leftPad(Integer .toString(days), count, "0") : Integer .toString(days)); } else if ("H".equals(value)) { result.append(padWithZeros ? StringUtils.leftPad(Integer .toString(hours), count, "0") : Integer .toString(hours)); } else if ("m".equals(value)) { result.append(padWithZeros ? StringUtils.leftPad(Integer .toString(minutes), count, "0") : Integer .toString(minutes)); } else if ("s".equals(value)) { result.append(padWithZeros ? StringUtils.leftPad(Integer .toString(seconds), count, "0") : Integer .toString(seconds)); } else if ("S".equals(value)) { result.append(padWithZeros ? StringUtils.leftPad(Integer .toString(milliseconds), count, "0") : Integer .toString(milliseconds)); } } } return result.toString(); }
- 근데 잘보면, 버그가 존재한다. 귀찮아서 수정은 안하겠다. 한번 연구해보도록.