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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new aedf2be112 ISIS-3105: adds support for parsing of incomplete temporal 
strings
aedf2be112 is described below

commit aedf2be11286b194d7989b4609623fd890546246
Author: Andi Huber <[email protected]>
AuthorDate: Tue Aug 2 13:44:44 2022 +0200

    ISIS-3105: adds support for parsing of incomplete temporal strings
    
    - eg. missing zone/offset info or less than 9 digits for fractional
    seconds
---
 .../isis/commons/internal/base/_Temporals.java     | 55 ++++++++++++++++++----
 .../isis/commons/internal/base/TemporalsTest.java  | 28 +++++++++++
 2 files changed, 74 insertions(+), 9 deletions(-)

diff --git 
a/commons/src/main/java/org/apache/isis/commons/internal/base/_Temporals.java 
b/commons/src/main/java/org/apache/isis/commons/internal/base/_Temporals.java
index b01eb36444..2466a5c8e2 100644
--- 
a/commons/src/main/java/org/apache/isis/commons/internal/base/_Temporals.java
+++ 
b/commons/src/main/java/org/apache/isis/commons/internal/base/_Temporals.java
@@ -31,11 +31,13 @@ import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Optional;
+import java.util.regex.Pattern;
 
 import org.springframework.lang.Nullable;
 
 import org.apache.isis.commons.collections.Can;
 
+import lombok.NonNull;
 import lombok.val;
 import lombok.experimental.UtilityClass;
 
@@ -86,23 +88,26 @@ public final class _Temporals {
 
     // -- TEMPORAL TO STRING CONVERTERS
 
+    private static final String FRACTIONAL_SECONDS_READ_PATTERN =
+            "[.SSSSSSSSS][.SSSSSS][.SSS][.S]";
+
     private static final DateTimeFormatter OFFSETTIME_DATASTORE_FORMATTER =
             DateTimeFormatter.ofPattern("HH:mm:ss.SSSSSSSSS XXX");
 
     private static final DateTimeFormatter OFFSETTIME_DATASTORE_PARSER =
-            DateTimeFormatter.ofPattern("HH:mm:ss[.SSSSSSSSS][ XXX]");
+            DateTimeFormatter.ofPattern("HH:mm:ss" + 
FRACTIONAL_SECONDS_READ_PATTERN + "[ XXX]");
 
     private static final DateTimeFormatter OFFSETDATETIME_DATASTORE_FORMATTER =
             DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS XXX");
 
     private static final DateTimeFormatter OFFSETDATETIME_DATASTORE_PARSER =
-            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSSSSSSSS][ 
XXX]");
+            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" + 
FRACTIONAL_SECONDS_READ_PATTERN + "[ XXX]");
 
     private static final DateTimeFormatter ZONEDDATETIME_DATASTORE_FORMATTER =
             DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSS VV");
 
     private static final DateTimeFormatter ZONEDDATETIME_DATASTORE_PARSER =
-            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSSSSSSSS][ 
VV]");
+            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" + 
FRACTIONAL_SECONDS_READ_PATTERN + "[ VV]");
 
 
     /**
@@ -121,7 +126,11 @@ public final class _Temporals {
     @Nullable
     public OffsetTime destringAsOffsetTime(final @Nullable String 
datastoreValue) {
         return _Strings.isNotEmpty(datastoreValue)
-                ? OffsetTime.parse(datastoreValue, OFFSETTIME_DATASTORE_PARSER)
+                ? hasZoneOrOffsetInfoWhenAssumingTimeOnly(datastoreValue)
+                        ? OffsetTime.parse(datastoreValue, 
OFFSETTIME_DATASTORE_PARSER)
+                        : OffsetTime.of(
+                                LocalTime.parse(datastoreValue,
+                                        OFFSETTIME_DATASTORE_PARSER), 
ZoneOffset.UTC)
                 : null;
     }
 
@@ -141,7 +150,11 @@ public final class _Temporals {
     @Nullable
     public OffsetDateTime destringAsOffsetDateTime(final @Nullable String 
datastoreValue) {
         return _Strings.isNotEmpty(datastoreValue)
-                ? OffsetDateTime.parse(datastoreValue, 
OFFSETDATETIME_DATASTORE_PARSER)
+                ? hasZoneOrOffsetInfoWhenAssumingDateAndTime(datastoreValue)
+                        ? OffsetDateTime.parse(datastoreValue, 
OFFSETDATETIME_DATASTORE_PARSER)
+                        : OffsetDateTime.of(
+                                LocalDateTime.parse(datastoreValue,
+                                        OFFSETDATETIME_DATASTORE_PARSER), 
ZoneOffset.UTC)
                 : null;
     }
 
@@ -161,25 +174,29 @@ public final class _Temporals {
     @Nullable
     public ZonedDateTime destringAsZonedDateTime(final @Nullable String 
datastoreValue) {
         return _Strings.isNotEmpty(datastoreValue)
-                ? ZonedDateTime.parse(datastoreValue, 
ZONEDDATETIME_DATASTORE_PARSER)
+                ? hasZoneOrOffsetInfoWhenAssumingDateAndTime(datastoreValue)
+                        ? ZonedDateTime.parse(datastoreValue, 
ZONEDDATETIME_DATASTORE_PARSER)
+                        : ZonedDateTime.of(
+                                LocalDateTime.parse(datastoreValue,
+                                        ZONEDDATETIME_DATASTORE_PARSER), 
ZoneOffset.UTC)
                 : null;
     }
 
     // -- TEMPORAL SAMPLERS
 
-    public static Can<LocalDateTime> sampleLocalDateTime() {
+    public Can<LocalDateTime> sampleLocalDateTime() {
         return Can.of(
                 LocalDateTime.now(),
                 LocalDateTime.now().plusDays(2).plusSeconds(15));
     }
 
-    public static Can<LocalDate> sampleLocalDate() {
+    public Can<LocalDate> sampleLocalDate() {
         return Can.of(
                 LocalDate.now(),
                 LocalDate.now().plusDays(2));
     }
 
-    public static Can<LocalTime> sampleLocalTime() {
+    public Can<LocalTime> sampleLocalTime() {
         return Can.of(
                 LocalTime.now(),
                 LocalTime.now().plusSeconds(15));
@@ -224,4 +241,24 @@ public final class _Temporals {
                 .setScale(3, RoundingMode.HALF_EVEN);
     }
 
+    /**
+     * Whether the number of delimiting white-spaces hints at presence of 
zone/offset info.
+     */
+    private boolean hasZoneOrOffsetInfoWhenAssumingTimeOnly(final @NonNull 
String datastoreValue) {
+        return delimitedChunksCount(datastoreValue)>1;
+    }
+
+    /**
+     * Whether the number of delimiting white-spaces hints at presence of 
zone/offset info.
+     */
+    private boolean hasZoneOrOffsetInfoWhenAssumingDateAndTime(final @NonNull 
String datastoreValue) {
+        return delimitedChunksCount(datastoreValue)>2;
+    }
+
+    private long delimitedChunksCount(final @NonNull String datastoreValue) {
+        return _Strings.splitThenStream(datastoreValue, 
Pattern.compile("\\s+"))
+                .filter(_Strings::isNotEmpty)
+                .count();
+    }
+
 }
diff --git 
a/commons/src/test/java/org/apache/isis/commons/internal/base/TemporalsTest.java
 
b/commons/src/test/java/org/apache/isis/commons/internal/base/TemporalsTest.java
index 7ba7add80a..e14eb41825 100644
--- 
a/commons/src/test/java/org/apache/isis/commons/internal/base/TemporalsTest.java
+++ 
b/commons/src/test/java/org/apache/isis/commons/internal/base/TemporalsTest.java
@@ -18,6 +18,13 @@
  */
 package org.apache.isis.commons.internal.base;
 
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -47,4 +54,25 @@ class TemporalsTest {
         }
     }
 
+    @Test
+    void incompleteOffsetTime() {
+        assertEquals(
+                OffsetTime.of(LocalTime.of(12, 31), ZoneOffset.UTC),
+                _Temporals.destringAsOffsetTime("12:31:00.0"));
+    }
+
+    @Test
+    void incompleteOffsetDateTime() {
+        assertEquals(
+                OffsetDateTime.of(LocalDate.of(2022, 1, 28), LocalTime.of(12, 
31), ZoneOffset.UTC),
+                _Temporals.destringAsOffsetDateTime("2022-01-28 12:31:00.0"));
+    }
+
+    @Test
+    void incompleteZonedDateTime() {
+        assertEquals(
+                ZonedDateTime.of(LocalDate.of(2022, 1, 28), LocalTime.of(12, 
31), ZoneOffset.UTC),
+                _Temporals.destringAsZonedDateTime("2022-01-28 12:31:00.0"));
+    }
+
 }

Reply via email to