You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2025/08/24 19:43:30 UTC

(commons-lang) 01/02: Add org.apache.commons.lang3.time.DateUtils.toOffsetDateTime(Date[, TimeZone]) #1385

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git

commit e917dd0df3cd38b8ee7e7bbf3ba75fa6f8f06e40
Author: Gary D. Gregory <ga...@gmail.com>
AuthorDate: Sat Aug 23 07:46:15 2025 -0400

    Add org.apache.commons.lang3.time.DateUtils.toOffsetDateTime(Date[,
    TimeZone]) #1385
---
 src/changes/changes.xml                            |   1 +
 .../org/apache/commons/lang3/time/DateUtils.java   |  31 +++-
 .../apache/commons/lang3/time/DateUtilsTest.java   | 163 ++++++++++++---------
 3 files changed, 125 insertions(+), 70 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6fac9d6d2..112a79604 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -69,6 +69,7 @@ The <action> type attribute can be add,update,fix,remove.
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.StringUtils.indexOfAny(CharSequence, int, char...).</action>
     <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.concurrent.ConcurrentException.ConcurrentException(String).</action>
     <action                   type="add" dev="ggregory" due-to="Finger, Gary Gregory, Piotr P. Karwasz">Add org.apache.commons.lang3.time.DateUtils.toLocalDateTime(Date[, TimeZone]) #1385.</action>
+    <action                   type="add" dev="ggregory" due-to="Gary Gregory">Add org.apache.commons.lang3.time.DateUtils.toOffsetDateTime(Date[, TimeZone]) #1385.</action>
     <!-- UPDATE -->
     <action type="update" dev="ggregory" due-to="Gary Gregory">[test] Bump org.apache.commons:commons-text from 1.13.1 to 1.14.0.</action>
   </release>
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 061ab2aa3..990189147 100644
--- a/src/main/java/org/apache/commons/lang3/time/DateUtils.java
+++ b/src/main/java/org/apache/commons/lang3/time/DateUtils.java
@@ -19,6 +19,8 @@
 import java.text.ParseException;
 import java.text.ParsePosition;
 import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Iterator;
@@ -1647,7 +1649,34 @@ public static LocalDateTime toLocalDateTime(final Date date) {
      * @since 3.19.0
      */
     public static LocalDateTime toLocalDateTime(final Date date, final TimeZone timeZone) {
-        return LocalDateTime.ofInstant(date.toInstant(), TimeZones.toTimeZone(timeZone).toZoneId());
+        return LocalDateTime.ofInstant(date.toInstant(), toZoneId(timeZone));
+    }
+
+    /**
+     * Converts a {@link Date} to a {@link OffsetDateTime}.
+     *
+     * @param date the Date to convert, not null.
+     * @return a new OffsetDateTime.
+     * @since 3.19.0
+     */
+    public static OffsetDateTime toOffsetDateTime(final Date date) {
+        return toOffsetDateTime(date, TimeZone.getDefault());
+    }
+
+    /**
+     * Converts a {@link Date} to a {@link OffsetDateTime}.
+     *
+     * @param date     the Date to convert to a OffsetDateTime, not null.
+     * @param timeZone the time zone, null maps to to the default time zone.
+     * @return a new OffsetDateTime.
+     * @since 3.19.0
+     */
+    public static OffsetDateTime toOffsetDateTime(final Date date, final TimeZone timeZone) {
+        return OffsetDateTime.ofInstant(date.toInstant(), toZoneId(timeZone));
+    }
+
+    private static ZoneId toZoneId(final TimeZone timeZone) {
+        return TimeZones.toTimeZone(timeZone).toZoneId();
     }
 
     /**
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 3fbfaadfc..f114f4eb5 100644
--- a/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/time/DateUtilsTest.java
@@ -30,9 +30,12 @@
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
 import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
@@ -65,70 +68,6 @@ class DateUtilsTest extends AbstractLangTest {
     private static final TimeZone TIME_ZONE_MET = TimeZone.getTimeZone("MET");
     private static Date BASE_DATE;
 
-    private static Stream<Arguments> testToLocalDateTimeTimeZone() {
-        // @formatter:off
-        return Stream.of(
-                Arguments.of(
-                        LocalDateTime.ofInstant(
-                                java.sql.Timestamp.valueOf("2000-01-01 12:30:45").toInstant(),
-                                TimeZone.getTimeZone("America/New_York").toZoneId()
-                        ),
-                        java.sql.Timestamp.valueOf("2000-01-01 12:30:45"),
-                        TimeZone.getTimeZone("America/New_York")
-                ),
-                Arguments.of(
-                        LocalDateTime.ofInstant(
-                                java.sql.Timestamp.valueOf("2023-03-12 02:30:00").toInstant(),
-                                TimeZone.getTimeZone("America/New_York").toZoneId()
-                        ),
-                        java.sql.Timestamp.valueOf("2023-03-12 02:30:00"),
-                        TimeZone.getTimeZone("America/New_York")
-                ),
-                Arguments.of(
-                        LocalDateTime.ofInstant(
-                                java.sql.Timestamp.valueOf("2023-03-12 02:30:00").toInstant(),
-                                TimeZone.getDefault().toZoneId()
-                        ),
-                        java.sql.Timestamp.valueOf("2023-03-12 02:30:00"),
-                        null
-                ),
-                Arguments.of(
-                        LocalDateTime.of(2022, 12, 31, 19, 0),
-                        Date.from(LocalDateTime.of(2023, 1, 1, 0, 0)
-                                .atOffset(ZoneOffset.UTC)
-                                .toInstant()),
-                        TimeZone.getTimeZone("America/New_York")
-                ),
-                Arguments.of(
-                        LocalDateTime.of(2023, 3, 12, 3, 0),
-                        Date.from(LocalDateTime.of(2023, 3, 12, 7, 0)
-                                .atOffset(ZoneOffset.UTC)
-                                .toInstant()),
-                        TimeZone.getTimeZone("America/New_York")
-                ),
-                Arguments.of(
-                        LocalDateTime.of(2023, 1, 1, 14, 0),
-                        Date.from(LocalDateTime.of(2023, 1, 1, 0, 0)
-                                .atOffset(ZoneOffset.UTC)
-                                .toInstant()),
-                        TimeZone.getTimeZone("Pacific/Kiritimati")
-                )
-        );
-        // @formatter:on
-    }
-
-    @ParameterizedTest
-    @MethodSource
-    void testToLocalDateTimeTimeZone(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
-        assertEquals(expected, DateUtils.toLocalDateTime(date, timeZone));
-    }
-
-    @Test
-    void testToLocalDateTime() {
-        final Date date = new Date();
-        assertEquals(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), DateUtils.toLocalDateTime(date));
-    }
-
     /**
      * Used to check that Calendar objects are close enough
      * delta is in milliseconds
@@ -195,9 +134,74 @@ public static void classSetup() {
         BASE_DATE = cal.getTime();
     }
 
+    private static Stream<Arguments> testToLocalDateTimeTimeZone() {
+        // @formatter:off
+        return Stream.of(
+                Arguments.of(
+                        LocalDateTime.ofInstant(Instant.EPOCH, TimeZone.getTimeZone("GMT").toZoneId()),
+                        java.sql.Timestamp.valueOf("1970-01-01 00:00:00"),
+                        TimeZone.getTimeZone("America/New_York")
+                ),
+                Arguments.of(
+                        LocalDateTime.ofInstant(Instant.EPOCH.minus(1, ChronoUnit.DAYS), TimeZone.getTimeZone("GMT").toZoneId()),
+                        java.sql.Timestamp.valueOf("1969-12-31 00:00:00"),
+                        TimeZone.getTimeZone("America/New_York")
+                ),
+                Arguments.of(
+                        LocalDateTime.ofInstant(
+                                java.sql.Timestamp.valueOf("2000-01-01 12:30:45").toInstant(),
+                                TimeZone.getTimeZone("America/New_York").toZoneId()
+                        ),
+                        java.sql.Timestamp.valueOf("2000-01-01 12:30:45"),
+                        TimeZone.getTimeZone("America/New_York")
+                ),
+                Arguments.of(
+                        LocalDateTime.ofInstant(
+                                java.sql.Timestamp.valueOf("2023-03-12 02:30:00").toInstant(),
+                                TimeZone.getTimeZone("America/New_York").toZoneId()
+                        ),
+                        java.sql.Timestamp.valueOf("2023-03-12 02:30:00"),
+                        TimeZone.getTimeZone("America/New_York")
+                ),
+                Arguments.of(
+                        LocalDateTime.ofInstant(
+                                java.sql.Timestamp.valueOf("2023-03-12 02:30:00").toInstant(),
+                                TimeZone.getDefault().toZoneId()
+                        ),
+                        java.sql.Timestamp.valueOf("2023-03-12 02:30:00"),
+                        null
+                ),
+                Arguments.of(
+                        LocalDateTime.of(2022, 12, 31, 19, 0),
+                        Date.from(LocalDateTime.of(2023, 1, 1, 0, 0)
+                                .atOffset(ZoneOffset.UTC)
+                                .toInstant()),
+                        TimeZone.getTimeZone("America/New_York")
+                ),
+                Arguments.of(
+                        LocalDateTime.of(2023, 3, 12, 3, 0),
+                        Date.from(LocalDateTime.of(2023, 3, 12, 7, 0)
+                                .atOffset(ZoneOffset.UTC)
+                                .toInstant()),
+                        TimeZone.getTimeZone("America/New_York")
+                ),
+                Arguments.of(
+                        LocalDateTime.of(2023, 1, 1, 14, 0),
+                        Date.from(LocalDateTime.of(2023, 1, 1, 0, 0)
+                                .atOffset(ZoneOffset.UTC)
+                                .toInstant()),
+                        TimeZone.getTimeZone("Pacific/Kiritimati")
+                )
+        );
+        // @formatter:on
+    }
+
     private DateFormat dateParser;
+
     private DateFormat dateTimeParser;
+
     private Date dateAmPm1;
+
     private Date dateAmPm2;
     private Date dateAmPm3;
     private Date dateAmPm4;
@@ -222,13 +226,11 @@ public static void classSetup() {
     private Calendar cal6;
     private Calendar cal7;
     private Calendar cal8;
-
     @AfterEach
     public void afterEachResetTimeZones() {
         TimeZone.setDefault(TIME_ZONE_DEFAULT);
         dateTimeParser.setTimeZone(TIME_ZONE_DEFAULT);
     }
-
     private void assertDate(final Date date, final int year, final int month, final int day, final int hour, final int min, final int sec, final int mil) {
         final GregorianCalendar cal = new GregorianCalendar();
         cal.setTime(date);
@@ -240,7 +242,6 @@ private void assertDate(final Date date, final int year, final int month, final
         assertEquals(sec, cal.get(Calendar.SECOND));
         assertEquals(mil, cal.get(Calendar.MILLISECOND));
     }
-
     @BeforeEach
     public void setUp() throws Exception {
         dateParser = new SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH);
@@ -1303,10 +1304,34 @@ void testToCalendarWithTimeZoneNull() {
         assertNullPointerException(() -> DateUtils.toCalendar(date1, null));
     }
 
+    @ParameterizedTest
+    @MethodSource("testToLocalDateTimeTimeZone")
+    void testToLocalDateTime(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
+        assertEquals(LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), DateUtils.toLocalDateTime(date));
+    }
+
+    @ParameterizedTest
+    @MethodSource
+    void testToLocalDateTimeTimeZone(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
+        assertEquals(expected, DateUtils.toLocalDateTime(date, timeZone));
+    }
+
+    @ParameterizedTest
+    @MethodSource("testToLocalDateTimeTimeZone")
+    void testToOffsetDateTime(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
+        assertEquals(OffsetDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()), DateUtils.toOffsetDateTime(date));
+    }
+
+    @ParameterizedTest
+    @MethodSource("testToLocalDateTimeTimeZone")
+    void testToOffsetDateTimeTimeZone(final LocalDateTime expected, final Date date, final TimeZone timeZone) {
+        assertEquals(expected, DateUtils.toOffsetDateTime(date, timeZone).toLocalDateTime());
+    }
+
     /**
-     * Tests various values with the trunc method
+     * Tests various values with the trunc method.
      *
-     * @throws Exception so we don't have to catch it
+     * @throws Exception so we don't have to catch it.
      */
     @Test
     void testTruncate() throws Exception {