From e99a544f9090e069d1cf44e105bf85039cc70501 Mon Sep 17 00:00:00 2001 From: finger Date: Sat, 17 May 2025 20:44:58 +0800 Subject: [PATCH 01/12] Add Date to LocalDateTime conversion methods and test cases This commit enhances the DateUtils class by adding two new methods to convert java.util.Date to java.time.LocalDateTime, supporting both default and specified time zones. The implementation leverages Java 8's date-time API for accurate time zone handling, including: - `toLocalDateTime(Date date)`: Uses the system's default time zone - `toLocalDateTime(Date date, TimeZone tz)`: Explicitly specifies time zone via TimeZone parameter Key improvements: - Proper null checking with Objects.requireNonNull() - Direct conversion via Instant and ZoneId for timezone accuracy - Maintains parity with existing DateUtils method naming conventions Test cases added in DateUtilsTest: - Default time zone conversion (Asia/Shanghai example) - Specified time zone conversion (America/New_York) - Null input validation (throws NullPointerException) - Daylight saving time handling (America/New_York DST case) - Extreme time zone test (Pacific/Kiritimati, UTC+14) These changes enable seamless integration between legacy Date APIs and modern LocalDateTime, while ensuring robust timezone handling across different environments. --- .../apache/commons/lang3/time/DateUtils.java | 25 ++++++++ .../commons/lang3/time/DateUtilsTest.java | 64 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java index 7ba125706ee..5afd96c8cc0 100644 --- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java +++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java @@ -18,6 +18,8 @@ import java.text.ParseException; import java.text.ParsePosition; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Calendar; import java.util.Date; import java.util.Iterator; @@ -1625,6 +1627,29 @@ public static Calendar toCalendar(final Date date, final TimeZone tz) { return c; } + /** + * Converts a {@link Date} into a {@link LocalDateTime}, using the default time zone. + * @param date the date to convert to a LocalDateTime + * @return the created LocalDateTime + * @throws NullPointerException if {@code date} is null + * @since 3.18 + */ + public static LocalDateTime toLocalDateTime(final Date date) { + return toLocalDateTime(date, TimeZone.getDefault()); + } + + /** + * Converts a {@link Date} into a {@link LocalDateTime} + * @param date the date to convert to a LocalDateTime + * @param tz the time zone of the {@code date} + * @return the created LocalDateTime + * @throws NullPointerException if {@code date} is null + * @since 3.18 + */ + public static LocalDateTime toLocalDateTime(final Date date, final TimeZone tz) { + return Objects.requireNonNull(date, "date").toInstant().atZone(ZoneId.of(tz.getID())).toLocalDateTime(); + } + /** * Truncates a date, leaving the field specified as the most * significant field. diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java index d6fba80f3cf..84c1bc6df5d 100644 --- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java @@ -28,6 +28,9 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -1285,6 +1288,67 @@ public void testToCalendarWithTimeZoneNull() { assertThrows(NullPointerException.class, () -> DateUtils.toCalendar(date1, null)); } + @Test + void shouldConvertDateToLocalDateTimeUsingDefaultTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); + + Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) + .atOffset(ZoneOffset.UTC) + .toInstant(); + Date date = Date.from(instant); + + LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 8, 0); + + assertEquals(expected, DateUtils.toLocalDateTime(date)); + } + + @Test + void shouldConvertDateToLocalDateTimeUsingSpecifiedTimeZone() { + Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) + .atOffset(ZoneOffset.UTC) + .toInstant(); + Date date = Date.from(instant); + + TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York"); + LocalDateTime expected = LocalDateTime.of(2022, 12, 31, 19, 0); + + assertEquals(expected, DateUtils.toLocalDateTime(date, newYorkTimeZone)); + } + + @Test + void shouldThrowNullPointerExceptionWhenDateIsNull() { + assertThrows(NullPointerException.class, () -> DateUtils.toLocalDateTime(null)); + assertThrows(NullPointerException.class, () -> DateUtils.toLocalDateTime(null, TimeZone.getDefault())); + } + + @Test + void shouldHandleDaylightSavingTimeCorrectly() { + Instant instant = LocalDateTime.of(2023, 3, 12, 7, 0) + .atOffset(ZoneOffset.UTC) + .toInstant(); + Date date = Date.from(instant); + + TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York"); + LocalDateTime expected = LocalDateTime.of(2023, 3, 12, 3, 0); + + assertEquals(expected, DateUtils.toLocalDateTime(date, newYorkTimeZone)); + } + + @Test + void shouldHandleExtremeTimeZoneCorrectly() { + Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) + .atOffset(ZoneOffset.UTC) + .toInstant(); + Date date = Date.from(instant); + + TimeZone extremeTimeZone = TimeZone.getTimeZone("Pacific/Kiritimati"); + LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 14, 0); + + assertEquals(expected, DateUtils.toLocalDateTime(date, extremeTimeZone)); + } + + + /** * Tests various values with the trunc method * From 79b109966fe06a2d02d8071fc8b68aeb286dbed0 Mon Sep 17 00:00:00 2001 From: finger Date: Sat, 17 May 2025 23:10:49 +0800 Subject: [PATCH 02/12] Manually convert date to Instant to avoid exceptions caused by incompatible subclasses (such as java.sql.Date). --- src/main/java/org/apache/commons/lang3/time/DateUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java index 5afd96c8cc0..558dccd6145 100644 --- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java +++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java @@ -18,8 +18,8 @@ import java.text.ParseException; import java.text.ParsePosition; +import java.time.Instant; import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.Calendar; import java.util.Date; import java.util.Iterator; @@ -1647,7 +1647,8 @@ public static LocalDateTime toLocalDateTime(final Date date) { * @since 3.18 */ public static LocalDateTime toLocalDateTime(final Date date, final TimeZone tz) { - return Objects.requireNonNull(date, "date").toInstant().atZone(ZoneId.of(tz.getID())).toLocalDateTime(); + return LocalDateTime.ofInstant(Instant.ofEpochMilli(Objects.requireNonNull(date, "date").getTime()), + Objects.requireNonNull(tz,"tz").toZoneId()); } /** From 6ad245bec103ed52fca93dc8f702d3f17ec0096b Mon Sep 17 00:00:00 2001 From: finger Date: Sat, 17 May 2025 23:30:14 +0800 Subject: [PATCH 03/12] feat: Enhance unit tests and add nanosecond support for java.sql.Timestamp **What** 1. Added extensive unit tests covering all java.sql date/time subclasses (Date, Time, Timestamp) 2. Implemented nanosecond precision support for java.sql.Timestamp conversions 3. Enhanced timezone handling to ensure correct conversion across different time zones 4. Added daylight saving time (DST) edge case testing **Why** 1. Existing tests lacked coverage for sql subclasses and edge cases 2. Timestamp conversions were losing nanosecond precision 3. Timezone handling was inconsistent between Date and Timestamp types --- .../apache/commons/lang3/time/DateUtils.java | 9 +- .../commons/lang3/time/DateUtilsTest.java | 107 ++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java index 558dccd6145..d7d2e0ad599 100644 --- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java +++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java @@ -16,6 +16,7 @@ */ package org.apache.commons.lang3.time; +import java.sql.Timestamp; import java.text.ParseException; import java.text.ParsePosition; import java.time.Instant; @@ -1647,8 +1648,12 @@ public static LocalDateTime toLocalDateTime(final Date date) { * @since 3.18 */ public static LocalDateTime toLocalDateTime(final Date date, final TimeZone tz) { - return LocalDateTime.ofInstant(Instant.ofEpochMilli(Objects.requireNonNull(date, "date").getTime()), - Objects.requireNonNull(tz,"tz").toZoneId()); + LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Objects.requireNonNull(date, "date").getTime()), + Objects.requireNonNull(tz, "tz").toZoneId()); + if (date instanceof java.sql.Timestamp ) { + return localDateTime.withNano(((Timestamp) date).getNanos()); + } + return localDateTime; } /** diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java index 84c1bc6df5d..b6cff149fea 100644 --- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java @@ -30,6 +30,7 @@ import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneId; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; @@ -1288,6 +1289,112 @@ public void testToCalendarWithTimeZoneNull() { assertThrows(NullPointerException.class, () -> DateUtils.toCalendar(date1, null)); } + @Test + void testToLocalDateTimeWithSqlDate() { + java.sql.Date sqlDate = java.sql.Date.valueOf("2000-01-01"); + + LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); + + assertNotNull(result); + + LocalDateTime expected = LocalDateTime.of(2000, 1, 1, 0, 0, 0); + assertEquals(expected, result); + + Instant instant = Instant.ofEpochMilli(sqlDate.getTime()); + LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + assertEquals(expectedWithZone, result); + } + + @Test + public void testToLocalDateTimeWithSqlTime() { + java.sql.Time sqlTime = java.sql.Time.valueOf("12:30:45"); + + LocalDateTime result = DateUtils.toLocalDateTime(sqlTime); + + assertNotNull(result); + + LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 12, 30, 45); + assertEquals(expected, result); + + Instant instant = Instant.ofEpochMilli(sqlTime.getTime()); + LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + assertEquals(expectedWithZone, result); + } + + @Test + public void testToLocalDateTimeWithSqlTimestamp() { + java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45.123456789"); + + LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp); + + assertNotNull(result); + + LocalDateTime expected = LocalDateTime.of( + 2000, 1, 1, 12, 30, 45, 123_456_789 + ); + assertEquals(expected, result); + + Instant instant = sqlTimestamp.toInstant(); + LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + assertEquals(expectedWithZone, result); + } + + @Test + public void testToLocalDateTimeWithSqlTimestamp_NanoPrecision() { + java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45.987654321"); + + LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp); + + LocalDateTime expected = LocalDateTime.of( + 2000, 1, 1, 12, 30, 45, 987_654_321 + ); + assertEquals(expected, result); + } + + @Test + public void testToLocalDateTimeWithSqlTimestamp_WithTimeZone() { + java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45"); + TimeZone timeZone = TimeZone.getTimeZone("America/New_York"); + + LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp, timeZone); + + Instant instant = sqlTimestamp.toInstant(); + LocalDateTime expected = LocalDateTime.ofInstant(instant, timeZone.toZoneId()); + assertEquals(expected, result); + } + + @Test + public void testToLocalDateTimeWithSqlDate_Epoch() { + java.sql.Date sqlDate = java.sql.Date.valueOf("1970-01-01"); + + LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); + + LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 0, 0, 0); + assertEquals(expected, result); + } + + @Test + public void testToLocalDateTimeWithSqlTime_MaxValue() { + java.sql.Time sqlTime = java.sql.Time.valueOf("23:59:59"); + + LocalDateTime result = DateUtils.toLocalDateTime(sqlTime); + + LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 23, 59, 59); + assertEquals(expected, result); + } + + @Test + public void testToLocalDateTimeWithSqlTimestamp_DaylightSaving() { + java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2023-03-12 02:30:00"); + TimeZone newYork = TimeZone.getTimeZone("America/New_York"); + + LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp, newYork); + + Instant instant = sqlTimestamp.toInstant(); + LocalDateTime expected = LocalDateTime.ofInstant(instant, newYork.toZoneId()); + assertEquals(expected, result); + } + @Test void shouldConvertDateToLocalDateTimeUsingDefaultTimeZone() { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); From f7dcdb82deebe5bdeca3192b0309d45324fe4383 Mon Sep 17 00:00:00 2001 From: finger Date: Sun, 18 May 2025 00:33:38 +0800 Subject: [PATCH 04/12] style: Add the final modifier to the variable --- .../apache/commons/lang3/time/DateUtils.java | 2 +- .../commons/lang3/time/DateUtilsTest.java | 98 +++++++++---------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java index d7d2e0ad599..1099f3c75fe 100644 --- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java +++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java @@ -1648,7 +1648,7 @@ public static LocalDateTime toLocalDateTime(final Date date) { * @since 3.18 */ public static LocalDateTime toLocalDateTime(final Date date, final TimeZone tz) { - LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Objects.requireNonNull(date, "date").getTime()), + final LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Objects.requireNonNull(date, "date").getTime()), Objects.requireNonNull(tz, "tz").toZoneId()); if (date instanceof java.sql.Timestamp ) { return localDateTime.withNano(((Timestamp) date).getNanos()); diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java index b6cff149fea..a67ec64f022 100644 --- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java @@ -1291,61 +1291,61 @@ public void testToCalendarWithTimeZoneNull() { @Test void testToLocalDateTimeWithSqlDate() { - java.sql.Date sqlDate = java.sql.Date.valueOf("2000-01-01"); + final java.sql.Date sqlDate = java.sql.Date.valueOf("2000-01-01"); - LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); + final LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); assertNotNull(result); - LocalDateTime expected = LocalDateTime.of(2000, 1, 1, 0, 0, 0); + final LocalDateTime expected = LocalDateTime.of(2000, 1, 1, 0, 0, 0); assertEquals(expected, result); - Instant instant = Instant.ofEpochMilli(sqlDate.getTime()); - LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + final Instant instant = Instant.ofEpochMilli(sqlDate.getTime()); + final LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); assertEquals(expectedWithZone, result); } @Test public void testToLocalDateTimeWithSqlTime() { - java.sql.Time sqlTime = java.sql.Time.valueOf("12:30:45"); + final java.sql.Time sqlTime = java.sql.Time.valueOf("12:30:45"); - LocalDateTime result = DateUtils.toLocalDateTime(sqlTime); + final LocalDateTime result = DateUtils.toLocalDateTime(sqlTime); assertNotNull(result); - LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 12, 30, 45); + final LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 12, 30, 45); assertEquals(expected, result); - Instant instant = Instant.ofEpochMilli(sqlTime.getTime()); - LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + final Instant instant = Instant.ofEpochMilli(sqlTime.getTime()); + final LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); assertEquals(expectedWithZone, result); } @Test public void testToLocalDateTimeWithSqlTimestamp() { - java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45.123456789"); + final java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45.123456789"); - LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp); + final LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp); assertNotNull(result); - LocalDateTime expected = LocalDateTime.of( + final LocalDateTime expected = LocalDateTime.of( 2000, 1, 1, 12, 30, 45, 123_456_789 ); assertEquals(expected, result); - Instant instant = sqlTimestamp.toInstant(); - LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + final Instant instant = sqlTimestamp.toInstant(); + final LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); assertEquals(expectedWithZone, result); } @Test public void testToLocalDateTimeWithSqlTimestamp_NanoPrecision() { - java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45.987654321"); + final java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45.987654321"); - LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp); + final LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp); - LocalDateTime expected = LocalDateTime.of( + final LocalDateTime expected = LocalDateTime.of( 2000, 1, 1, 12, 30, 45, 987_654_321 ); assertEquals(expected, result); @@ -1353,45 +1353,45 @@ public void testToLocalDateTimeWithSqlTimestamp_NanoPrecision() { @Test public void testToLocalDateTimeWithSqlTimestamp_WithTimeZone() { - java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45"); - TimeZone timeZone = TimeZone.getTimeZone("America/New_York"); + final java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45"); + final TimeZone timeZone = TimeZone.getTimeZone("America/New_York"); - LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp, timeZone); + final LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp, timeZone); - Instant instant = sqlTimestamp.toInstant(); - LocalDateTime expected = LocalDateTime.ofInstant(instant, timeZone.toZoneId()); + final Instant instant = sqlTimestamp.toInstant(); + final LocalDateTime expected = LocalDateTime.ofInstant(instant, timeZone.toZoneId()); assertEquals(expected, result); } @Test public void testToLocalDateTimeWithSqlDate_Epoch() { - java.sql.Date sqlDate = java.sql.Date.valueOf("1970-01-01"); + final java.sql.Date sqlDate = java.sql.Date.valueOf("1970-01-01"); - LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); + final LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); - LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 0, 0, 0); + final LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 0, 0, 0); assertEquals(expected, result); } @Test public void testToLocalDateTimeWithSqlTime_MaxValue() { - java.sql.Time sqlTime = java.sql.Time.valueOf("23:59:59"); + final java.sql.Time sqlTime = java.sql.Time.valueOf("23:59:59"); - LocalDateTime result = DateUtils.toLocalDateTime(sqlTime); + final LocalDateTime result = DateUtils.toLocalDateTime(sqlTime); - LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 23, 59, 59); + final LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 23, 59, 59); assertEquals(expected, result); } @Test public void testToLocalDateTimeWithSqlTimestamp_DaylightSaving() { - java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2023-03-12 02:30:00"); - TimeZone newYork = TimeZone.getTimeZone("America/New_York"); + final java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2023-03-12 02:30:00"); + final TimeZone newYork = TimeZone.getTimeZone("America/New_York"); - LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp, newYork); + final LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp, newYork); - Instant instant = sqlTimestamp.toInstant(); - LocalDateTime expected = LocalDateTime.ofInstant(instant, newYork.toZoneId()); + final Instant instant = sqlTimestamp.toInstant(); + final LocalDateTime expected = LocalDateTime.ofInstant(instant, newYork.toZoneId()); assertEquals(expected, result); } @@ -1399,25 +1399,25 @@ public void testToLocalDateTimeWithSqlTimestamp_DaylightSaving() { void shouldConvertDateToLocalDateTimeUsingDefaultTimeZone() { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); - Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) + final Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) .atOffset(ZoneOffset.UTC) .toInstant(); - Date date = Date.from(instant); + final Date date = Date.from(instant); - LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 8, 0); + final LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 8, 0); assertEquals(expected, DateUtils.toLocalDateTime(date)); } @Test void shouldConvertDateToLocalDateTimeUsingSpecifiedTimeZone() { - Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) + final Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) .atOffset(ZoneOffset.UTC) .toInstant(); - Date date = Date.from(instant); + final Date date = Date.from(instant); - TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York"); - LocalDateTime expected = LocalDateTime.of(2022, 12, 31, 19, 0); + final TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York"); + final LocalDateTime expected = LocalDateTime.of(2022, 12, 31, 19, 0); assertEquals(expected, DateUtils.toLocalDateTime(date, newYorkTimeZone)); } @@ -1430,26 +1430,26 @@ void shouldThrowNullPointerExceptionWhenDateIsNull() { @Test void shouldHandleDaylightSavingTimeCorrectly() { - Instant instant = LocalDateTime.of(2023, 3, 12, 7, 0) + final Instant instant = LocalDateTime.of(2023, 3, 12, 7, 0) .atOffset(ZoneOffset.UTC) .toInstant(); - Date date = Date.from(instant); + final Date date = Date.from(instant); - TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York"); - LocalDateTime expected = LocalDateTime.of(2023, 3, 12, 3, 0); + final TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York"); + final LocalDateTime expected = LocalDateTime.of(2023, 3, 12, 3, 0); assertEquals(expected, DateUtils.toLocalDateTime(date, newYorkTimeZone)); } @Test void shouldHandleExtremeTimeZoneCorrectly() { - Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) + final Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) .atOffset(ZoneOffset.UTC) .toInstant(); - Date date = Date.from(instant); + final Date date = Date.from(instant); - TimeZone extremeTimeZone = TimeZone.getTimeZone("Pacific/Kiritimati"); - LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 14, 0); + final TimeZone extremeTimeZone = TimeZone.getTimeZone("Pacific/Kiritimati"); + final LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 14, 0); assertEquals(expected, DateUtils.toLocalDateTime(date, extremeTimeZone)); } From 45003ab684fa1cc03b7273a8215d515af1ba6802 Mon Sep 17 00:00:00 2001 From: finger Date: Sun, 18 May 2025 00:53:40 +0800 Subject: [PATCH 05/12] style: Remove an extra space --- src/main/java/org/apache/commons/lang3/time/DateUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java index 1099f3c75fe..29e2f28a666 100644 --- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java +++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java @@ -1650,7 +1650,7 @@ public static LocalDateTime toLocalDateTime(final Date date) { public static LocalDateTime toLocalDateTime(final Date date, final TimeZone tz) { final LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Objects.requireNonNull(date, "date").getTime()), Objects.requireNonNull(tz, "tz").toZoneId()); - if (date instanceof java.sql.Timestamp ) { + if (date instanceof java.sql.Timestamp) { return localDateTime.withNano(((Timestamp) date).getNanos()); } return localDateTime; From 0d479ab9f059a747ddecf7d9d26fba63e03487d3 Mon Sep 17 00:00:00 2001 From: Finger <53509788+yuyu1025@users.noreply.github.com> Date: Sun, 18 May 2025 08:53:04 +0800 Subject: [PATCH 06/12] style: Optimize readability Co-authored-by: Piotr P. Karwasz --- src/main/java/org/apache/commons/lang3/time/DateUtils.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java index 29e2f28a666..91901807b9d 100644 --- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java +++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java @@ -1648,8 +1648,9 @@ public static LocalDateTime toLocalDateTime(final Date date) { * @since 3.18 */ public static LocalDateTime toLocalDateTime(final Date date, final TimeZone tz) { - final LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(Objects.requireNonNull(date, "date").getTime()), - Objects.requireNonNull(tz, "tz").toZoneId()); + Objects.requireNonNull(date, "date"); + Objects.requireNonNull(tz, "tz"); + final LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), tz.toZoneId()); if (date instanceof java.sql.Timestamp) { return localDateTime.withNano(((Timestamp) date).getNanos()); } From f7928bee084c42bd6c3e1e0cc93cc10d31c369bb Mon Sep 17 00:00:00 2001 From: finger Date: Sun, 18 May 2025 09:25:56 +0800 Subject: [PATCH 07/12] feat: Refactor date to LocalDateTime conversion tests to use parameterized tests - Remove multiple individual @Test methods for SQL date/time/timestamp conversions - Replace with 2 parameterized tests using @ParameterizedTest and @MethodSource - Introduce test data providers: - dateConversionProvider() for basic conversions (SQL Date/Time/Timestamp) - dateWithTimeZoneProvider() for timezone-aware conversions - Maintain full test coverage for: - Epoch date (1970-01-01) - Max time values (23:59:59) - Nano precision timestamps - Default/system time zone handling - Specific time zones (America/New_York, Asia/Shanghai, Pacific/Kiritimati) - Daylight Saving Time transitions - Improve test maintainability by reducing code duplication - Add null argument validation test for time zone parameter Signed-off-by: finger --- .../commons/lang3/time/DateUtilsTest.java | 243 +++++++----------- 1 file changed, 93 insertions(+), 150 deletions(-) diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java index a67ec64f022..5c4355fb9a5 100644 --- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java @@ -30,7 +30,6 @@ import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDateTime; -import java.time.ZoneId; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; @@ -39,12 +38,16 @@ import java.util.Locale; import java.util.NoSuchElementException; import java.util.TimeZone; +import java.util.stream.Stream; import org.apache.commons.lang3.AbstractLangTest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.junitpioneer.jupiter.DefaultLocale; import org.junitpioneer.jupiter.ReadsDefaultLocale; import org.junitpioneer.jupiter.WritesDefaultLocale; @@ -1289,173 +1292,113 @@ public void testToCalendarWithTimeZoneNull() { assertThrows(NullPointerException.class, () -> DateUtils.toCalendar(date1, null)); } - @Test - void testToLocalDateTimeWithSqlDate() { - final java.sql.Date sqlDate = java.sql.Date.valueOf("2000-01-01"); - - final LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); - - assertNotNull(result); - - final LocalDateTime expected = LocalDateTime.of(2000, 1, 1, 0, 0, 0); - assertEquals(expected, result); - - final Instant instant = Instant.ofEpochMilli(sqlDate.getTime()); - final LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - assertEquals(expectedWithZone, result); - } - - @Test - public void testToLocalDateTimeWithSqlTime() { - final java.sql.Time sqlTime = java.sql.Time.valueOf("12:30:45"); - - final LocalDateTime result = DateUtils.toLocalDateTime(sqlTime); - - assertNotNull(result); - - final LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 12, 30, 45); - assertEquals(expected, result); - - final Instant instant = Instant.ofEpochMilli(sqlTime.getTime()); - final LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - assertEquals(expectedWithZone, result); - } - - @Test - public void testToLocalDateTimeWithSqlTimestamp() { - final java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45.123456789"); - - final LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp); - + @ParameterizedTest + @MethodSource("dateConversionProvider") + void testToLocalDateTimeWithDate(Date sqlDate, LocalDateTime expected) { + LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); assertNotNull(result); - - final LocalDateTime expected = LocalDateTime.of( - 2000, 1, 1, 12, 30, 45, 123_456_789 - ); assertEquals(expected, result); - - final Instant instant = sqlTimestamp.toInstant(); - final LocalDateTime expectedWithZone = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); - assertEquals(expectedWithZone, result); } - @Test - public void testToLocalDateTimeWithSqlTimestamp_NanoPrecision() { - final java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45.987654321"); - - final LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp); - - final LocalDateTime expected = LocalDateTime.of( - 2000, 1, 1, 12, 30, 45, 987_654_321 + private static Stream dateConversionProvider() { + return Stream.of( + Arguments.of( + java.sql.Date.valueOf("2000-01-01"), + LocalDateTime.of(2000, 1, 1, 0, 0, 0) + ), + Arguments.of( + java.sql.Date.valueOf("1970-01-01"), + LocalDateTime.of(1970, 1, 1, 0, 0, 0) + ), + Arguments.of( + java.sql.Time.valueOf("12:30:45"), + LocalDateTime.of(1970, 1, 1, 12, 30, 45) + ), + Arguments.of( + java.sql.Time.valueOf("23:59:59"), + LocalDateTime.of(1970, 1, 1, 23, 59, 59) + ), + Arguments.of( + java.sql.Timestamp.valueOf("2000-01-01 12:30:45.123456789"), + LocalDateTime.of(2000, 1, 1, 12, 30, 45, 123_456_789) + ), + Arguments.of( + java.sql.Timestamp.valueOf("2000-01-01 12:30:45.987654321"), + LocalDateTime.of(2000, 1, 1, 12, 30, 45, 987_654_321) + ) ); - assertEquals(expected, result); - } - - @Test - public void testToLocalDateTimeWithSqlTimestamp_WithTimeZone() { - final java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2000-01-01 12:30:45"); - final TimeZone timeZone = TimeZone.getTimeZone("America/New_York"); - - final LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp, timeZone); - - final Instant instant = sqlTimestamp.toInstant(); - final LocalDateTime expected = LocalDateTime.ofInstant(instant, timeZone.toZoneId()); - assertEquals(expected, result); - } - - @Test - public void testToLocalDateTimeWithSqlDate_Epoch() { - final java.sql.Date sqlDate = java.sql.Date.valueOf("1970-01-01"); - - final LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); - - final LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 0, 0, 0); - assertEquals(expected, result); - } - - @Test - public void testToLocalDateTimeWithSqlTime_MaxValue() { - final java.sql.Time sqlTime = java.sql.Time.valueOf("23:59:59"); - - final LocalDateTime result = DateUtils.toLocalDateTime(sqlTime); - - final LocalDateTime expected = LocalDateTime.of(1970, 1, 1, 23, 59, 59); - assertEquals(expected, result); } - @Test - public void testToLocalDateTimeWithSqlTimestamp_DaylightSaving() { - final java.sql.Timestamp sqlTimestamp = java.sql.Timestamp.valueOf("2023-03-12 02:30:00"); - final TimeZone newYork = TimeZone.getTimeZone("America/New_York"); - - final LocalDateTime result = DateUtils.toLocalDateTime(sqlTimestamp, newYork); - - final Instant instant = sqlTimestamp.toInstant(); - final LocalDateTime expected = LocalDateTime.ofInstant(instant, newYork.toZoneId()); + @ParameterizedTest + @MethodSource("dateWithTimeZoneProvider") + void testToLocalDateTimeWithDate( + Date date, + TimeZone timeZone, + LocalDateTime expected) { + LocalDateTime result ; + if (timeZone != null) { + result = DateUtils.toLocalDateTime(date, timeZone); + } else { + result = DateUtils.toLocalDateTime(date); + } assertEquals(expected, result); } - @Test - void shouldConvertDateToLocalDateTimeUsingDefaultTimeZone() { - TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); - - final Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) - .atOffset(ZoneOffset.UTC) - .toInstant(); - final Date date = Date.from(instant); - - final LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 8, 0); - - assertEquals(expected, DateUtils.toLocalDateTime(date)); - } - - @Test - void shouldConvertDateToLocalDateTimeUsingSpecifiedTimeZone() { - final Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) - .atOffset(ZoneOffset.UTC) - .toInstant(); - final Date date = Date.from(instant); - - final TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York"); - final LocalDateTime expected = LocalDateTime.of(2022, 12, 31, 19, 0); - - assertEquals(expected, DateUtils.toLocalDateTime(date, newYorkTimeZone)); + private static Stream dateWithTimeZoneProvider() { + return Stream.of( + Arguments.of( + java.sql.Timestamp.valueOf("2000-01-01 12:30:45"), + TimeZone.getTimeZone("America/New_York"), + LocalDateTime.ofInstant( + java.sql.Timestamp.valueOf("2000-01-01 12:30:45").toInstant(), + TimeZone.getTimeZone("America/New_York").toZoneId() + ) + ), + Arguments.of( + java.sql.Timestamp.valueOf("2023-03-12 02:30:00"), + TimeZone.getTimeZone("America/New_York"), + LocalDateTime.ofInstant( + java.sql.Timestamp.valueOf("2023-03-12 02:30:00").toInstant(), + TimeZone.getTimeZone("America/New_York").toZoneId() + ) + ), Arguments.of( + Date.from(LocalDateTime.of(2023, 1, 1, 0, 0) + .atOffset(ZoneOffset.UTC) + .toInstant()), + null, + LocalDateTime.of(2023, 1, 1, 8, 0) + ), + Arguments.of( + Date.from(LocalDateTime.of(2023, 1, 1, 0, 0) + .atOffset(ZoneOffset.UTC) + .toInstant()), + TimeZone.getTimeZone("America/New_York"), + LocalDateTime.of(2022, 12, 31, 19, 0) + ), + Arguments.of( + Date.from(LocalDateTime.of(2023, 3, 12, 7, 0) + .atOffset(ZoneOffset.UTC) + .toInstant()), + TimeZone.getTimeZone("America/New_York"), + LocalDateTime.of(2023, 3, 12, 3, 0) + ), + Arguments.of( + Date.from(LocalDateTime.of(2023, 1, 1, 0, 0) + .atOffset(ZoneOffset.UTC) + .toInstant()), + TimeZone.getTimeZone("Pacific/Kiritimati"), + LocalDateTime.of(2023, 1, 1, 14, 0) + ) + ); } @Test void shouldThrowNullPointerExceptionWhenDateIsNull() { assertThrows(NullPointerException.class, () -> DateUtils.toLocalDateTime(null)); assertThrows(NullPointerException.class, () -> DateUtils.toLocalDateTime(null, TimeZone.getDefault())); + assertThrows(NullPointerException.class, () -> DateUtils.toLocalDateTime(new Date(), null)); } - @Test - void shouldHandleDaylightSavingTimeCorrectly() { - final Instant instant = LocalDateTime.of(2023, 3, 12, 7, 0) - .atOffset(ZoneOffset.UTC) - .toInstant(); - final Date date = Date.from(instant); - - final TimeZone newYorkTimeZone = TimeZone.getTimeZone("America/New_York"); - final LocalDateTime expected = LocalDateTime.of(2023, 3, 12, 3, 0); - - assertEquals(expected, DateUtils.toLocalDateTime(date, newYorkTimeZone)); - } - - @Test - void shouldHandleExtremeTimeZoneCorrectly() { - final Instant instant = LocalDateTime.of(2023, 1, 1, 0, 0) - .atOffset(ZoneOffset.UTC) - .toInstant(); - final Date date = Date.from(instant); - - final TimeZone extremeTimeZone = TimeZone.getTimeZone("Pacific/Kiritimati"); - final LocalDateTime expected = LocalDateTime.of(2023, 1, 1, 14, 0); - - assertEquals(expected, DateUtils.toLocalDateTime(date, extremeTimeZone)); - } - - - /** * Tests various values with the trunc method * From 7d1692cf3c290d94a81267cdc6cd1d4938f1ac79 Mon Sep 17 00:00:00 2001 From: finger Date: Sun, 18 May 2025 09:54:21 +0800 Subject: [PATCH 08/12] feat:Add reflection-based handling for java.sql.Timestamp to avoid module dependency - Introduce static reflection initialization for Timestamp class/method - Use isTimestamp() to check instance type without direct dependency - Extract nanoseconds via reflection in extractNanosFromSqlTimestamp() - Gracefully handle absence of java.sql module with null checks and exception handling - Maintain existing functionality for converting Timestamps to LocalDateTime with nanosecond precision - Ensure compatibility with both modular (JPMS) and non-modular environments - Remove unused Instant import in test class for cleaner imports Signed-off-by: finger --- .../apache/commons/lang3/time/DateUtils.java | 55 ++++++++++++++++++- .../commons/lang3/time/DateUtilsTest.java | 1 - 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java index 91901807b9d..36e955ff628 100644 --- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java +++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java @@ -16,7 +16,8 @@ */ package org.apache.commons.lang3.time; -import java.sql.Timestamp; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.text.ParseException; import java.text.ParsePosition; import java.time.Instant; @@ -205,6 +206,28 @@ private enum ModifyType { * A month range, the week starting on Monday. */ public static final int RANGE_MONTH_MONDAY = 6; + /** + * The {@link Class} for {@link java.sql.Timestamp}. + */ + private static final Class timestampClass; + /** + * The {@link Method} for {@link java.sql.Timestamp#getNanos()}. + */ + private static final Method timestampGetNanosMethod; + + static { + Class clazz; + Method method; + try { + clazz = Class.forName("java.sql.Timestamp"); + method = clazz.getMethod("getNanos"); + } catch (ClassNotFoundException | NoSuchMethodException ex) { + clazz = null; + method = null; + } + timestampClass = clazz; + timestampGetNanosMethod = method; + } /** * Adds to a date returning a new object. @@ -1651,12 +1674,38 @@ public static LocalDateTime toLocalDateTime(final Date date, final TimeZone tz) Objects.requireNonNull(date, "date"); Objects.requireNonNull(tz, "tz"); final LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), tz.toZoneId()); - if (date instanceof java.sql.Timestamp) { - return localDateTime.withNano(((Timestamp) date).getNanos()); + if (isTimestamp(date)) { + return localDateTime.withNano(extractNanosFromSqlTimestamp(date)); } return localDateTime; } + /** + * Get the nanosecond part in the {@link java.sql.Timestamp} object. without requiring the java.sql module. + * + * @param date The date is a {@link java.sql.Timestamp} object. + * If it is not a {@link java.sql.Timestamp} object, + * it will return 0. + * @return The nanosecond part of the {@link java.sql.Timestamp} object. + */ + private static int extractNanosFromSqlTimestamp(Date date){ + if (timestampClass==null){ + return 0; + } + try { + return (int)timestampGetNanosMethod.invoke(date); + } catch (IllegalAccessException | InvocationTargetException e) { + return 0; + } + } + + /** + * Check to see if obj is an instance of {@link java.sql.Timestamp} without requiring the java.sql module. + */ + private static boolean isTimestamp(final Date date) { + return timestampClass != null && timestampClass.isAssignableFrom(date.getClass()); + } + /** * Truncates a date, leaving the field specified as the most * significant field. diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java index 5c4355fb9a5..0c36abc5931 100644 --- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java @@ -28,7 +28,6 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; -import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Calendar; From 88b71144bb617bb1881f15d0454173163ba8e945 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Sun, 18 May 2025 11:19:43 +0200 Subject: [PATCH 09/12] fix: add `java.sql` as optional dependency to Moditect --- pom.xml | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b45bb43b67f..52c9dccee24 100644 --- a/pom.xml +++ b/pom.xml @@ -452,8 +452,9 @@ + - java9+ + java-9-up [9,) @@ -462,6 +463,32 @@ -Xmx512m --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.time=ALL-UNNAMED --add-opens java.base/java.time.chrono=ALL-UNNAMED + + + + + org.moditect + moditect-maven-plugin + + + add-module-infos + + + + + static java.sql; + *; + + + + + + + + + java15 From 36eb042d0ba6721933a5ce005b89a77a39bf5e2c Mon Sep 17 00:00:00 2001 From: finger Date: Sun, 18 May 2025 17:50:15 +0800 Subject: [PATCH 10/12] fix: Fix unit test errors caused by different default time zones Signed-off-by: finger --- .../java/org/apache/commons/lang3/time/DateUtilsTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java index 0c36abc5931..48948cf9eb1 100644 --- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java @@ -1360,12 +1360,6 @@ private static Stream dateWithTimeZoneProvider() { java.sql.Timestamp.valueOf("2023-03-12 02:30:00").toInstant(), TimeZone.getTimeZone("America/New_York").toZoneId() ) - ), Arguments.of( - Date.from(LocalDateTime.of(2023, 1, 1, 0, 0) - .atOffset(ZoneOffset.UTC) - .toInstant()), - null, - LocalDateTime.of(2023, 1, 1, 8, 0) ), Arguments.of( Date.from(LocalDateTime.of(2023, 1, 1, 0, 0) From 5d20c0b678a6ff6ba8d1a3bc5223ab71c5977626 Mon Sep 17 00:00:00 2001 From: finger Date: Sun, 18 May 2025 18:20:33 +0800 Subject: [PATCH 11/12] style: Optimize the format by building verification Signed-off-by: finger --- src/main/java/org/apache/commons/lang3/time/DateUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/time/DateUtils.java b/src/main/java/org/apache/commons/lang3/time/DateUtils.java index 36e955ff628..5fcf9cde2ab 100644 --- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java +++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java @@ -1688,12 +1688,12 @@ public static LocalDateTime toLocalDateTime(final Date date, final TimeZone tz) * it will return 0. * @return The nanosecond part of the {@link java.sql.Timestamp} object. */ - private static int extractNanosFromSqlTimestamp(Date date){ - if (timestampClass==null){ + private static int extractNanosFromSqlTimestamp(Date date) { + if (timestampClass == null) { return 0; } try { - return (int)timestampGetNanosMethod.invoke(date); + return (int) timestampGetNanosMethod.invoke(date); } catch (IllegalAccessException | InvocationTargetException e) { return 0; } From eb906ba69a7aa1fd9d0f70b26207da3e588cba4a Mon Sep 17 00:00:00 2001 From: finger Date: Sun, 18 May 2025 18:36:29 +0800 Subject: [PATCH 12/12] style: add final var Signed-off-by: finger --- .../commons/lang3/time/DateUtilsTest.java | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java index 48948cf9eb1..129631a3453 100644 --- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java @@ -1291,14 +1291,6 @@ public void testToCalendarWithTimeZoneNull() { assertThrows(NullPointerException.class, () -> DateUtils.toCalendar(date1, null)); } - @ParameterizedTest - @MethodSource("dateConversionProvider") - void testToLocalDateTimeWithDate(Date sqlDate, LocalDateTime expected) { - LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); - assertNotNull(result); - assertEquals(expected, result); - } - private static Stream dateConversionProvider() { return Stream.of( Arguments.of( @@ -1328,21 +1320,6 @@ private static Stream dateConversionProvider() { ); } - @ParameterizedTest - @MethodSource("dateWithTimeZoneProvider") - void testToLocalDateTimeWithDate( - Date date, - TimeZone timeZone, - LocalDateTime expected) { - LocalDateTime result ; - if (timeZone != null) { - result = DateUtils.toLocalDateTime(date, timeZone); - } else { - result = DateUtils.toLocalDateTime(date); - } - assertEquals(expected, result); - } - private static Stream dateWithTimeZoneProvider() { return Stream.of( Arguments.of( @@ -1385,6 +1362,29 @@ private static Stream dateWithTimeZoneProvider() { ); } + @ParameterizedTest + @MethodSource("dateConversionProvider") + void testToLocalDateTimeWithDate(final Date sqlDate, final LocalDateTime expected) { + final LocalDateTime result = DateUtils.toLocalDateTime(sqlDate); + assertNotNull(result); + assertEquals(expected, result); + } + + @ParameterizedTest + @MethodSource("dateWithTimeZoneProvider") + void testToLocalDateTimeWithDate( + final Date date, + final TimeZone timeZone, + final LocalDateTime expected) { + final LocalDateTime result; + if (timeZone != null) { + result = DateUtils.toLocalDateTime(date, timeZone); + } else { + result = DateUtils.toLocalDateTime(date); + } + assertEquals(expected, result); + } + @Test void shouldThrowNullPointerExceptionWhenDateIsNull() { assertThrows(NullPointerException.class, () -> DateUtils.toLocalDateTime(null));