diff --git a/src/java/org/apache/commons/lang/time/DateUtils.java b/src/java/org/apache/commons/lang/time/DateUtils.java index f042237a1..79c13f0aa 100644 --- a/src/java/org/apache/commons/lang/time/DateUtils.java +++ b/src/java/org/apache/commons/lang/time/DateUtils.java @@ -621,6 +621,51 @@ private static void modify(Calendar val, int field, boolean round) { throw new ArithmeticException("Calendar value too large for accurate calculations"); } + if (field == Calendar.MILLISECOND) { + return; + } + + // ----------------- Fix for LANG-59 ---------------------- START --------------- + // see http://issues.apache.org/jira/browse/LANG-59 + // + // Manually truncate milliseconds, seconds and minutes, rather than using + // Calendar methods. + + Date date = val.getTime(); + long time = date.getTime(); + boolean done = false; + + // truncate milliseconds + int millisecs = val.get(Calendar.MILLISECOND); + if (!round || millisecs < 500) { + time = time - millisecs; + if (field == Calendar.SECOND) { + done = true; + } + } + + // truncate seconds + int seconds = val.get(Calendar.SECOND); + if (!done && (!round || seconds < 30)) { + time = time - (seconds * 1000L); + if (field == Calendar.MINUTE) { + done = true; + } + } + + // truncate minutes + int minutes = val.get(Calendar.MINUTE); + if (!done && (!round || minutes < 30)) { + time = time - (minutes * 60000L); + } + + // reset time + if (date.getTime() != time) { + date.setTime(time); + val.setTime(date); + } + // ----------------- Fix for LANG-59 ----------------------- END ---------------- + boolean roundUp = false; for (int i = 0; i < fields.length; i++) { for (int j = 0; j < fields[i].length; j++) { @@ -689,7 +734,9 @@ private static void modify(Calendar val, int field, boolean round) { roundUp = offset > ((max - min) / 2); } //We need to remove this field - val.set(fields[i][0], val.get(fields[i][0]) - offset); + if (offset != 0) { + val.set(fields[i][0], val.get(fields[i][0]) - offset); + } } throw new IllegalArgumentException("The field " + field + " is not supported"); diff --git a/src/test/org/apache/commons/lang/time/DateUtilsTest.java b/src/test/org/apache/commons/lang/time/DateUtilsTest.java index 7e0b4b1e6..4b5e8f7a2 100644 --- a/src/test/org/apache/commons/lang/time/DateUtilsTest.java +++ b/src/test/org/apache/commons/lang/time/DateUtilsTest.java @@ -882,6 +882,81 @@ public void testTruncate() throws Exception { assertEquals(0, cal.get(Calendar.HOUR)); } + /** + * Tests for LANG-59 + * + * see http://issues.apache.org/jira/browse/LANG-59 + */ + public void testTruncateLang59() throws Exception { + + // Set TimeZone to Mountain Time + TimeZone MST_MDT = TimeZone.getTimeZone("MST7MDT"); + TimeZone.setDefault(MST_MDT); + DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z"); + format.setTimeZone(MST_MDT); + + Date oct31_01MDT = new Date(1099206000000L); + + Date oct31MDT = new Date(oct31_01MDT.getTime() - 3600000L); // - 1 hour + Date oct31_01_02MDT = new Date(oct31_01MDT.getTime() + 120000L); // + 2 minutes + Date oct31_01_02_03MDT = new Date(oct31_01_02MDT.getTime() + 3000L); // + 3 seconds + Date oct31_01_02_03_04MDT = new Date(oct31_01_02_03MDT.getTime() + 4L); // + 4 milliseconds + + assertEquals("Check 00:00:00.000", "2004-10-31 00:00:00.000 MDT", format.format(oct31MDT)); + assertEquals("Check 01:00:00.000", "2004-10-31 01:00:00.000 MDT", format.format(oct31_01MDT)); + assertEquals("Check 01:02:00.000", "2004-10-31 01:02:00.000 MDT", format.format(oct31_01_02MDT)); + assertEquals("Check 01:02:03.000", "2004-10-31 01:02:03.000 MDT", format.format(oct31_01_02_03MDT)); + assertEquals("Check 01:02:03.004", "2004-10-31 01:02:03.004 MDT", format.format(oct31_01_02_03_04MDT)); + + // ------- Demonstrate Problem ------- + Calendar gval = Calendar.getInstance(); + gval.setTime(new Date(oct31_01MDT.getTime())); + gval.set(Calendar.MINUTE, gval.get(Calendar.MINUTE)); // set minutes to the same value + assertEquals("Demonstrate Problem", gval.getTime().getTime(), oct31_01MDT.getTime() + 3600000L); + + // ---------- Test Truncate ---------- + assertEquals("Truncate Calendar.MILLISECOND", + oct31_01_02_03_04MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.MILLISECOND)); + + assertEquals("Truncate Calendar.SECOND", + oct31_01_02_03MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.SECOND)); + + assertEquals("Truncate Calendar.MINUTE", + oct31_01_02MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.MINUTE)); + + assertEquals("Truncate Calendar.HOUR_OF_DAY", + oct31_01MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.HOUR_OF_DAY)); + + assertEquals("Truncate Calendar.HOUR", + oct31_01MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.HOUR)); + + assertEquals("Truncate Calendar.DATE", + oct31MDT, DateUtils.truncate(oct31_01_02_03_04MDT, Calendar.DATE)); + + + // ---------- Test Round (down) ---------- + assertEquals("Round Calendar.MILLISECOND", + oct31_01_02_03_04MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.MILLISECOND)); + + assertEquals("Round Calendar.SECOND", + oct31_01_02_03MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.SECOND)); + + assertEquals("Round Calendar.MINUTE", + oct31_01_02MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.MINUTE)); + + assertEquals("Round Calendar.HOUR_OF_DAY", + oct31_01MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.HOUR_OF_DAY)); + + assertEquals("Round Calendar.HOUR", + oct31_01MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.HOUR)); + + assertEquals("Round Calendar.DATE", + oct31MDT, DateUtils.round(oct31_01_02_03_04MDT, Calendar.DATE)); + + // restore default time zone + TimeZone.setDefault(defaultZone); + } + /** * Tests the iterator exceptions */ @@ -978,14 +1053,6 @@ public void testMonthIterator() throws Exception { dateParser.parse("December 2, 2001")); } - // Tests LANG-59 - public void testLang59() throws Exception { - // truncate 2004-10-31 01:00:00 MDT - Date oct31_01MDT = new Date(1099206000000L); - Date result = DateUtils.truncate(oct31_01MDT, Calendar.HOUR_OF_DAY); - assertEquals(oct31_01MDT, result); - } - /** * This checks that this is a 7 element iterator of Calendar objects * that are dates (no time), and exactly 1 day spaced after each other.