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"));
+ }
+
}