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 {