You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ni...@apache.org on 2010/01/30 18:37:33 UTC
svn commit: r904825 - in /commons/proper/lang/branches/LANG_2_X/src:
main/java/org/apache/commons/lang/time/DateUtils.java
test/java/org/apache/commons/lang/time/DateUtilsTest.java
Author: niallp
Date: Sat Jan 30 17:37:32 2010
New Revision: 904825
URL: http://svn.apache.org/viewvc?rev=904825&view=rev
Log:
Port LANG-434 to 2.x branch - add ceiling() method to DateUtils
Modified:
commons/proper/lang/branches/LANG_2_X/src/main/java/org/apache/commons/lang/time/DateUtils.java
commons/proper/lang/branches/LANG_2_X/src/test/java/org/apache/commons/lang/time/DateUtilsTest.java
Modified: commons/proper/lang/branches/LANG_2_X/src/main/java/org/apache/commons/lang/time/DateUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/branches/LANG_2_X/src/main/java/org/apache/commons/lang/time/DateUtils.java?rev=904825&r1=904824&r2=904825&view=diff
==============================================================================
--- commons/proper/lang/branches/LANG_2_X/src/main/java/org/apache/commons/lang/time/DateUtils.java (original)
+++ commons/proper/lang/branches/LANG_2_X/src/main/java/org/apache/commons/lang/time/DateUtils.java Sat Jan 30 17:37:32 2010
@@ -31,7 +31,7 @@
*
* <p>DateUtils contains a lot of common methods considering manipulations
* of Dates or Calendars. Some methods require some extra explanation.
- * The truncate and round methods could be considered the Math.floor(),
+ * The truncate, ceiling and round methods could be considered the Math.floor(),
* Math.ceil() or Math.round versions for dates
* This way date-fields will be ignored in bottom-up order.
* As a complement to these methods we've introduced some fragment-methods.
@@ -127,6 +127,21 @@
public final static int RANGE_MONTH_MONDAY = 6;
/**
+ * Constant marker for truncating
+ */
+ private final static int MODIFY_TRUNCATE = 0;
+
+ /**
+ * Constant marker for rounding
+ */
+ private final static int MODIFY_ROUND = 1;
+
+ /**
+ * Constant marker for ceiling
+ */
+ private final static int MODIFY_CEILING= 2;
+
+ /**
* <p><code>DateUtils</code> instances should NOT be constructed in
* standard programming. Instead, the class should be used as
* <code>DateUtils.parse(str);</code>.</p>
@@ -585,7 +600,7 @@
}
Calendar gval = Calendar.getInstance();
gval.setTime(date);
- modify(gval, field, true);
+ modify(gval, field, MODIFY_ROUND);
return gval.getTime();
}
@@ -622,7 +637,7 @@
throw new IllegalArgumentException("The date must not be null");
}
Calendar rounded = (Calendar) date.clone();
- modify(rounded, field, true);
+ modify(rounded, field, MODIFY_ROUND);
return rounded;
}
@@ -692,7 +707,7 @@
}
Calendar gval = Calendar.getInstance();
gval.setTime(date);
- modify(gval, field, false);
+ modify(gval, field, MODIFY_TRUNCATE);
return gval.getTime();
}
@@ -717,7 +732,7 @@
throw new IllegalArgumentException("The date must not be null");
}
Calendar truncated = (Calendar) date.clone();
- modify(truncated, field, false);
+ modify(truncated, field, MODIFY_TRUNCATE);
return truncated;
}
@@ -753,6 +768,91 @@
throw new ClassCastException("Could not truncate " + date);
}
}
+
+ //-----------------------------------------------------------------------
+ /**
+ * <p>Ceil this date, leaving the field specified as the most
+ * significant field.</p>
+ *
+ * <p>For example, if you had the datetime of 28 Mar 2002
+ * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
+ * 2002 13:00:00.000. If this was passed with MONTH, it would
+ * return 1 Mar 2002 0:00:00.000.</p>
+ *
+ * @param date the date to work with
+ * @param field the field from <code>Calendar</code>
+ * or <code>SEMI_MONTH</code>
+ * @return the rounded date
+ * @throws IllegalArgumentException if the date is <code>null</code>
+ * @throws ArithmeticException if the year is over 280 million
+ */
+ public static Date ceiling(Date date, int field) {
+ if (date == null) {
+ throw new IllegalArgumentException("The date must not be null");
+ }
+ Calendar gval = Calendar.getInstance();
+ gval.setTime(date);
+ modify(gval, field, MODIFY_CEILING);
+ return gval.getTime();
+ }
+
+ /**
+ * <p>Ceil this date, leaving the field specified as the most
+ * significant field.</p>
+ *
+ * <p>For example, if you had the datetime of 28 Mar 2002
+ * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
+ * 2002 13:00:00.000. If this was passed with MONTH, it would
+ * return 1 Mar 2002 0:00:00.000.</p>
+ *
+ * @param date the date to work with
+ * @param field the field from <code>Calendar</code>
+ * or <code>SEMI_MONTH</code>
+ * @return the rounded date (a different object)
+ * @throws IllegalArgumentException if the date is <code>null</code>
+ * @throws ArithmeticException if the year is over 280 million
+ */
+ public static Calendar ceiling(Calendar date, int field) {
+ if (date == null) {
+ throw new IllegalArgumentException("The date must not be null");
+ }
+ Calendar ceiled = (Calendar) date.clone();
+ modify(ceiled, field, MODIFY_CEILING);
+ return ceiled;
+ }
+
+ /**
+ * <p>Ceil this date, leaving the field specified as the most
+ * significant field.</p>
+ *
+ * <p>For example, if you had the datetime of 28 Mar 2002
+ * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
+ * 2002 13:00:00.000. If this was passed with MONTH, it would
+ * return 1 Mar 2002 0:00:00.000.</p>
+ *
+ * @param date the date to work with, either <code>Date</code>
+ * or <code>Calendar</code>
+ * @param field the field from <code>Calendar</code>
+ * or <code>SEMI_MONTH</code>
+ * @return the rounded date
+ * @throws IllegalArgumentException if the date
+ * is <code>null</code>
+ * @throws ClassCastException if the object type is not a
+ * <code>Date</code> or <code>Calendar</code>
+ * @throws ArithmeticException if the year is over 280 million
+ */
+ public static Date ceiling(Object date, int field) {
+ if (date == null) {
+ throw new IllegalArgumentException("The date must not be null");
+ }
+ if (date instanceof Date) {
+ return ceiling((Date) date, field);
+ } else if (date instanceof Calendar) {
+ return ceiling((Calendar) date, field).getTime();
+ } else {
+ throw new ClassCastException("Could not find ceiling of for type: " + date.getClass());
+ }
+ }
//-----------------------------------------------------------------------
/**
@@ -760,10 +860,10 @@
*
* @param val the calendar
* @param field the field constant
- * @param round true to round, false to truncate
+ * @param modType type to truncate, round or ceiling
* @throws ArithmeticException if the year is over 280 million
*/
- private static void modify(Calendar val, int field, boolean round) {
+ private static void modify(Calendar val, int field, int modType) {
if (val.get(Calendar.YEAR) > 280000000) {
throw new ArithmeticException("Calendar value too large for accurate calculations");
}
@@ -784,7 +884,7 @@
// truncate milliseconds
int millisecs = val.get(Calendar.MILLISECOND);
- if (!round || millisecs < 500) {
+ if (MODIFY_TRUNCATE == modType || millisecs < 500) {
time = time - millisecs;
}
if (field == Calendar.SECOND) {
@@ -793,7 +893,7 @@
// truncate seconds
int seconds = val.get(Calendar.SECOND);
- if (!done && (!round || seconds < 30)) {
+ if (!done && (MODIFY_TRUNCATE == modType || seconds < 30)) {
time = time - (seconds * 1000L);
}
if (field == Calendar.MINUTE) {
@@ -802,7 +902,7 @@
// truncate minutes
int minutes = val.get(Calendar.MINUTE);
- if (!done && (!round || minutes < 30)) {
+ if (!done && (MODIFY_TRUNCATE == modType || minutes < 30)) {
time = time - (minutes * 60000L);
}
@@ -818,7 +918,7 @@
for (int j = 0; j < fields[i].length; j++) {
if (fields[i][j] == field) {
//This is our field... we stop looping
- if (round && roundUp) {
+ if (modType == MODIFY_CEILING || (modType == MODIFY_ROUND && roundUp)) {
if (field == DateUtils.SEMI_MONTH) {
//This is a special case that's hard to generalize
//If the date is 1, we round up to 16, otherwise
Modified: commons/proper/lang/branches/LANG_2_X/src/test/java/org/apache/commons/lang/time/DateUtilsTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/branches/LANG_2_X/src/test/java/org/apache/commons/lang/time/DateUtilsTest.java?rev=904825&r1=904824&r2=904825&view=diff
==============================================================================
--- commons/proper/lang/branches/LANG_2_X/src/test/java/org/apache/commons/lang/time/DateUtilsTest.java (original)
+++ commons/proper/lang/branches/LANG_2_X/src/test/java/org/apache/commons/lang/time/DateUtilsTest.java Sat Jan 30 17:37:32 2010
@@ -1171,6 +1171,243 @@
}
/**
+ * Tests various values with the ceiling method
+ */
+ public void testCeil() throws Exception {
+ // tests public static Date ceiling(Date date, int field)
+ assertEquals("ceiling year-1 failed",
+ dateParser.parse("January 1, 2003"),
+ DateUtils.ceiling(date1, Calendar.YEAR));
+ assertEquals("ceiling year-2 failed",
+ dateParser.parse("January 1, 2002"),
+ DateUtils.ceiling(date2, Calendar.YEAR));
+ assertEquals("ceiling month-1 failed",
+ dateParser.parse("March 1, 2002"),
+ DateUtils.ceiling(date1, Calendar.MONTH));
+ assertEquals("ceiling month-2 failed",
+ dateParser.parse("December 1, 2001"),
+ DateUtils.ceiling(date2, Calendar.MONTH));
+ assertEquals("ceiling semimonth-1 failed",
+ dateParser.parse("February 16, 2002"),
+ DateUtils.ceiling(date1, DateUtils.SEMI_MONTH));
+ assertEquals("ceiling semimonth-2 failed",
+ dateParser.parse("December 1, 2001"),
+ DateUtils.ceiling(date2, DateUtils.SEMI_MONTH));
+ assertEquals("ceiling date-1 failed",
+ dateParser.parse("February 13, 2002"),
+ DateUtils.ceiling(date1, Calendar.DATE));
+ assertEquals("ceiling date-2 failed",
+ dateParser.parse("November 19, 2001"),
+ DateUtils.ceiling(date2, Calendar.DATE));
+ assertEquals("ceiling hour-1 failed",
+ dateTimeParser.parse("February 12, 2002 13:00:00.000"),
+ DateUtils.ceiling(date1, Calendar.HOUR));
+ assertEquals("ceiling hour-2 failed",
+ dateTimeParser.parse("November 18, 2001 2:00:00.000"),
+ DateUtils.ceiling(date2, Calendar.HOUR));
+ assertEquals("ceiling minute-1 failed",
+ dateTimeParser.parse("February 12, 2002 12:35:00.000"),
+ DateUtils.ceiling(date1, Calendar.MINUTE));
+ assertEquals("ceiling minute-2 failed",
+ dateTimeParser.parse("November 18, 2001 1:24:00.000"),
+ DateUtils.ceiling(date2, Calendar.MINUTE));
+ assertEquals("ceiling second-1 failed",
+ dateTimeParser.parse("February 12, 2002 12:34:57.000"),
+ DateUtils.ceiling(date1, Calendar.SECOND));
+ assertEquals("ceiling second-2 failed",
+ dateTimeParser.parse("November 18, 2001 1:23:12.000"),
+ DateUtils.ceiling(date2, Calendar.SECOND));
+ assertEquals("ceiling ampm-1 failed",
+ dateTimeParser.parse("February 3, 2002 12:00:00.000"),
+ DateUtils.ceiling(dateAmPm1, Calendar.AM_PM));
+ assertEquals("ceiling ampm-2 failed",
+ dateTimeParser.parse("February 3, 2002 12:00:00.000"),
+ DateUtils.ceiling(dateAmPm2, Calendar.AM_PM));
+ assertEquals("ceiling ampm-3 failed",
+ dateTimeParser.parse("February 4, 2002 00:00:00.000"),
+ DateUtils.ceiling(dateAmPm3, Calendar.AM_PM));
+ assertEquals("ceiling ampm-4 failed",
+ dateTimeParser.parse("February 4, 2002 00:00:00.000"),
+ DateUtils.ceiling(dateAmPm4, Calendar.AM_PM));
+
+ // tests public static Date ceiling(Object date, int field)
+ assertEquals("ceiling year-1 failed",
+ dateParser.parse("January 1, 2003"),
+ DateUtils.ceiling((Object) date1, Calendar.YEAR));
+ assertEquals("ceiling year-2 failed",
+ dateParser.parse("January 1, 2002"),
+ DateUtils.ceiling((Object) date2, Calendar.YEAR));
+ assertEquals("ceiling month-1 failed",
+ dateParser.parse("March 1, 2002"),
+ DateUtils.ceiling((Object) date1, Calendar.MONTH));
+ assertEquals("ceiling month-2 failed",
+ dateParser.parse("December 1, 2001"),
+ DateUtils.ceiling((Object) date2, Calendar.MONTH));
+ assertEquals("ceiling semimonth-1 failed",
+ dateParser.parse("February 16, 2002"),
+ DateUtils.ceiling((Object) date1, DateUtils.SEMI_MONTH));
+ assertEquals("ceiling semimonth-2 failed",
+ dateParser.parse("December 1, 2001"),
+ DateUtils.ceiling((Object) date2, DateUtils.SEMI_MONTH));
+ assertEquals("ceiling date-1 failed",
+ dateParser.parse("February 13, 2002"),
+ DateUtils.ceiling((Object) date1, Calendar.DATE));
+ assertEquals("ceiling date-2 failed",
+ dateParser.parse("November 19, 2001"),
+ DateUtils.ceiling((Object) date2, Calendar.DATE));
+ assertEquals("ceiling hour-1 failed",
+ dateTimeParser.parse("February 12, 2002 13:00:00.000"),
+ DateUtils.ceiling((Object) date1, Calendar.HOUR));
+ assertEquals("ceiling hour-2 failed",
+ dateTimeParser.parse("November 18, 2001 2:00:00.000"),
+ DateUtils.ceiling((Object) date2, Calendar.HOUR));
+ assertEquals("ceiling minute-1 failed",
+ dateTimeParser.parse("February 12, 2002 12:35:00.000"),
+ DateUtils.ceiling((Object) date1, Calendar.MINUTE));
+ assertEquals("ceiling minute-2 failed",
+ dateTimeParser.parse("November 18, 2001 1:24:00.000"),
+ DateUtils.ceiling((Object) date2, Calendar.MINUTE));
+ assertEquals("ceiling second-1 failed",
+ dateTimeParser.parse("February 12, 2002 12:34:57.000"),
+ DateUtils.ceiling((Object) date1, Calendar.SECOND));
+ assertEquals("ceiling second-2 failed",
+ dateTimeParser.parse("November 18, 2001 1:23:12.000"),
+ DateUtils.ceiling((Object) date2, Calendar.SECOND));
+ assertEquals("ceiling ampm-1 failed",
+ dateTimeParser.parse("February 3, 2002 12:00:00.000"),
+ DateUtils.ceiling((Object) dateAmPm1, Calendar.AM_PM));
+ assertEquals("ceiling ampm-2 failed",
+ dateTimeParser.parse("February 3, 2002 12:00:00.000"),
+ DateUtils.ceiling((Object) dateAmPm2, Calendar.AM_PM));
+ assertEquals("ceiling ampm-3 failed",
+ dateTimeParser.parse("February 4, 2002 00:00:00.000"),
+ DateUtils.ceiling((Object) dateAmPm3, Calendar.AM_PM));
+ assertEquals("ceiling ampm-4 failed",
+ dateTimeParser.parse("February 4, 2002 00:00:00.000"),
+ DateUtils.ceiling((Object) dateAmPm4, Calendar.AM_PM));
+
+ assertEquals("ceiling calendar second-1 failed",
+ dateTimeParser.parse("February 12, 2002 12:34:57.000"),
+ DateUtils.ceiling((Object) cal1, Calendar.SECOND));
+ assertEquals("ceiling calendar second-2 failed",
+ dateTimeParser.parse("November 18, 2001 1:23:12.000"),
+ DateUtils.ceiling((Object) cal2, Calendar.SECOND));
+
+ assertEquals("ceiling ampm-1 failed",
+ dateTimeParser.parse("February 3, 2002 12:00:00.000"),
+ DateUtils.ceiling((Object) calAmPm1, Calendar.AM_PM));
+ assertEquals("ceiling ampm-2 failed",
+ dateTimeParser.parse("February 3, 2002 12:00:00.000"),
+ DateUtils.ceiling((Object) calAmPm2, Calendar.AM_PM));
+ assertEquals("ceiling ampm-3 failed",
+ dateTimeParser.parse("February 4, 2002 00:00:00.000"),
+ DateUtils.ceiling((Object) calAmPm3, Calendar.AM_PM));
+ assertEquals("ceiling ampm-4 failed",
+ dateTimeParser.parse("February 4, 2002 00:00:00.000"),
+ DateUtils.ceiling((Object) calAmPm4, Calendar.AM_PM));
+
+ try {
+ DateUtils.ceiling((Date) null, Calendar.SECOND);
+ fail();
+ } catch (IllegalArgumentException ex) {}
+ try {
+ DateUtils.ceiling((Calendar) null, Calendar.SECOND);
+ fail();
+ } catch (IllegalArgumentException ex) {}
+ try {
+ DateUtils.ceiling((Object) null, Calendar.SECOND);
+ fail();
+ } catch (IllegalArgumentException ex) {}
+ try {
+ DateUtils.ceiling("", Calendar.SECOND);
+ fail();
+ } catch (ClassCastException ex) {}
+ try {
+ DateUtils.ceiling(date1, -9999);
+ fail();
+ } catch(IllegalArgumentException ex) {}
+
+
+ // Fix for http://issues.apache.org/bugzilla/show_bug.cgi?id=25560
+ // Test ceiling across the beginning of daylight saving time
+ TimeZone.setDefault(zone);
+ dateTimeParser.setTimeZone(zone);
+
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 31, 2003 00:00:00.000"),
+ DateUtils.ceiling(date4, Calendar.DATE));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 31, 2003 00:00:00.000"),
+ DateUtils.ceiling((Object) cal4, Calendar.DATE));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 31, 2003 00:00:00.000"),
+ DateUtils.ceiling(date5, Calendar.DATE));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 31, 2003 00:00:00.000"),
+ DateUtils.ceiling((Object) cal5, Calendar.DATE));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 31, 2003 00:00:00.000"),
+ DateUtils.ceiling(date6, Calendar.DATE));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 31, 2003 00:00:00.000"),
+ DateUtils.ceiling((Object) cal6, Calendar.DATE));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 31, 2003 00:00:00.000"),
+ DateUtils.ceiling(date7, Calendar.DATE));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 31, 2003 00:00:00.000"),
+ DateUtils.ceiling((Object) cal7, Calendar.DATE));
+
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 30, 2003 03:00:00.000"),
+ DateUtils.ceiling(date4, Calendar.HOUR_OF_DAY));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 30, 2003 03:00:00.000"),
+ DateUtils.ceiling((Object) cal4, Calendar.HOUR_OF_DAY));
+ if (SystemUtils.isJavaVersionAtLeast(1.4f)) {
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 30, 2003 03:00:00.000"),
+ DateUtils.ceiling(date5, Calendar.HOUR_OF_DAY));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 30, 2003 03:00:00.000"),
+ DateUtils.ceiling((Object) cal5, Calendar.HOUR_OF_DAY));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 30, 2003 04:00:00.000"),
+ DateUtils.ceiling(date6, Calendar.HOUR_OF_DAY));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 30, 2003 04:00:00.000"),
+ DateUtils.ceiling((Object) cal6, Calendar.HOUR_OF_DAY));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 30, 2003 04:00:00.000"),
+ DateUtils.ceiling(date7, Calendar.HOUR_OF_DAY));
+ assertEquals("ceiling MET date across DST change-over",
+ dateTimeParser.parse("March 30, 2003 04:00:00.000"),
+ DateUtils.ceiling((Object) cal7, Calendar.HOUR_OF_DAY));
+ } else {
+ this.warn("WARNING: Some date ceiling tests not run since the current version is " + SystemUtils.JAVA_VERSION);
+ }
+ TimeZone.setDefault(defaultZone);
+ dateTimeParser.setTimeZone(defaultZone);
+
+ // Bug 31395, large dates
+ Date endOfTime = new Date(Long.MAX_VALUE); // fyi: Sun Aug 17 07:12:55 CET 292278994 -- 807 millis
+ GregorianCalendar endCal = new GregorianCalendar();
+ endCal.setTime(endOfTime);
+ try {
+ DateUtils.ceiling(endCal, Calendar.DATE);
+ fail();
+ } catch (ArithmeticException ex) {}
+ endCal.set(Calendar.YEAR, 280000001);
+ try {
+ DateUtils.ceiling(endCal, Calendar.DATE);
+ fail();
+ } catch (ArithmeticException ex) {}
+ endCal.set(Calendar.YEAR, 280000000);
+ Calendar cal = DateUtils.ceiling(endCal, Calendar.DATE);
+ assertEquals(0, cal.get(Calendar.HOUR));
+ }
+
+ /**
* Tests the iterator exceptions
*/
public void testIteratorEx() throws Exception {