This is an automated email from the ASF dual-hosted git repository.
exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new edb81fe6602 NIFI-15308 Added support for Zone Offset in Time Field
Converters (#10617)
edb81fe6602 is described below
commit edb81fe660245b92e79aa80a3945801b8498d86d
Author: Peter Turcsanyi <[email protected]>
AuthorDate: Tue Dec 16 15:19:41 2025 +0100
NIFI-15308 Added support for Zone Offset in Time Field Converters (#10617)
Signed-off-by: David Handermann <[email protected]>
---
.../field/ObjectLocalTimeFieldConverter.java | 32 ++++++++++++-
.../record/field/DateTimeTestUtil.java | 36 ++++++++++++++
.../field/ObjectLocalTimeFieldConverterTest.java | 56 ++++++++++++++++++++++
.../record/field/ObjectTimeFieldConverterTest.java | 28 +++++++++++
4 files changed, 150 insertions(+), 2 deletions(-)
diff --git
a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/field/ObjectLocalTimeFieldConverter.java
b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/field/ObjectLocalTimeFieldConverter.java
index 178993ed931..6359f8d929a 100644
---
a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/field/ObjectLocalTimeFieldConverter.java
+++
b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/field/ObjectLocalTimeFieldConverter.java
@@ -20,11 +20,15 @@ import
org.apache.nifi.serialization.record.util.IllegalTypeConversionException;
import java.sql.Time;
import java.time.Instant;
+import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
+import java.time.temporal.TemporalAccessor;
+import java.time.temporal.TemporalQueries;
+import java.time.temporal.TemporalQuery;
import java.util.Date;
import java.util.Optional;
@@ -32,6 +36,9 @@ import java.util.Optional;
* Convert Object to java.time.LocalTime using instanceof evaluation and
optional format pattern for DateTimeFormatter
*/
class ObjectLocalTimeFieldConverter implements FieldConverter<Object,
LocalTime> {
+
+ private static final TemporalQuery<LocalTime> LOCAL_TIME_TEMPORAL_QUERY =
new LocalTimeQuery();
+
/**
* Convert Object field to java.time.LocalTime using optional format
supported in DateTimeFormatter
*
@@ -72,7 +79,7 @@ class ObjectLocalTimeFieldConverter implements
FieldConverter<Object, LocalTime>
if (pattern.isPresent()) {
final DateTimeFormatter formatter =
DateTimeFormatterRegistry.getDateTimeFormatter(pattern.get());
try {
- return LocalTime.parse(string, formatter);
+ return formatter.parse(string,
LOCAL_TIME_TEMPORAL_QUERY);
} catch (final DateTimeParseException e) {
throw new FieldConversionException(LocalTime.class,
field, name, e);
}
@@ -93,8 +100,29 @@ class ObjectLocalTimeFieldConverter implements
FieldConverter<Object, LocalTime>
throw new FieldConversionException(LocalTime.class, field, name);
}
- private LocalTime ofInstant(final Instant instant) {
+ private static LocalTime ofInstant(final Instant instant) {
final ZonedDateTime zonedDateTime =
instant.atZone(ZoneId.systemDefault());
return zonedDateTime.toLocalTime();
}
+
+ private static class LocalTimeQuery implements TemporalQuery<LocalTime> {
+
+ @Override
+ public LocalTime queryFrom(final TemporalAccessor temporal) {
+ final LocalTime localTime;
+
+ // Query for ZoneId or ZoneOffset to determine time zone handling
+ final ZoneId zoneId = temporal.query(TemporalQueries.zone());
+ if (zoneId == null) {
+ localTime = LocalTime.from(temporal);
+ } else {
+ final ZonedDateTime zonedDateTime =
LocalTime.from(temporal).atDate(LocalDate.ofEpochDay(0)).atZone(zoneId);
+ // Convert Instant to LocalTime using system default zone
offset to incorporate adjusted hours and minutes
+ final Instant instant = zonedDateTime.toInstant();
+ localTime = ofInstant(instant);
+ }
+
+ return localTime;
+ }
+ }
}
diff --git
a/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/DateTimeTestUtil.java
b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/DateTimeTestUtil.java
new file mode 100644
index 00000000000..c7fd0f5924e
--- /dev/null
+++
b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/DateTimeTestUtil.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.serialization.record.field;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.stream.Stream;
+
+class DateTimeTestUtil {
+
+ private DateTimeTestUtil() {
+ }
+
+ static Stream<String> offsetSource() {
+ final String defaultOffset = ZoneId.systemDefault()
+ .getRules()
+ .getOffset(LocalDate.EPOCH.atStartOfDay())
+ .getId();
+
+ return Stream.of("-12:00", "-03:00", "+00:00", "+03:00", "+12:00",
defaultOffset);
+ }
+}
diff --git
a/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/ObjectLocalTimeFieldConverterTest.java
b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/ObjectLocalTimeFieldConverterTest.java
new file mode 100644
index 00000000000..96416cc76b4
--- /dev/null
+++
b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/ObjectLocalTimeFieldConverterTest.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.serialization.record.field;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ObjectLocalTimeFieldConverterTest {
+
+ private static final ObjectLocalTimeFieldConverter CONVERTER = new
ObjectLocalTimeFieldConverter();
+
+ private static final String FIELD_NAME = LocalTime.class.getSimpleName();
+
+ private static final String TIME_DEFAULT = "12:30:45";
+
+ private static final String TIME_WITH_OFFSET_PATTERN = "HH:mm:ssXXX";
+
+ @ParameterizedTest
+
@MethodSource("org.apache.nifi.serialization.record.field.DateTimeTestUtil#offsetSource")
+ void testConvertFieldStringWithTimeOffset(final String offsetId) {
+ final ZoneOffset zoneOffset = ZoneOffset.of(offsetId);
+
+ final LocalTime localTime = LocalTime.parse(TIME_DEFAULT);
+ final ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDate.EPOCH,
localTime, zoneOffset);
+ final ZonedDateTime zonedDateTimeAdjusted =
zonedDateTime.withZoneSameInstant(ZoneId.systemDefault());
+ final LocalTime expectedLocalTime =
zonedDateTimeAdjusted.toLocalTime();
+
+ final String localTimeWithOffsetId = TIME_DEFAULT + offsetId;
+ final LocalTime convertedLocalTime =
CONVERTER.convertField(localTimeWithOffsetId,
Optional.of(TIME_WITH_OFFSET_PATTERN), FIELD_NAME);
+
+ assertEquals(expectedLocalTime, convertedLocalTime);
+ }
+}
diff --git
a/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/ObjectTimeFieldConverterTest.java
b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/ObjectTimeFieldConverterTest.java
index 0e88c454301..87ef302744c 100644
---
a/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/ObjectTimeFieldConverterTest.java
+++
b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/field/ObjectTimeFieldConverterTest.java
@@ -18,8 +18,16 @@ package org.apache.nifi.serialization.record.field;
import org.apache.nifi.serialization.record.RecordFieldType;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
import java.sql.Time;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
import java.util.Date;
import java.util.Optional;
@@ -42,6 +50,8 @@ public class ObjectTimeFieldConverterTest {
private static final String TIME_NANOSECONDS = "12:30:45.123456789";
+ private static final String TIME_WITH_OFFSET_PATTERN = "HH:mm:ssXXX";
+
@Test
public void testConvertFieldNull() {
final Time time = CONVERTER.convertField(null,
Optional.of(DEFAULT_PATTERN), FIELD_NAME);
@@ -80,4 +90,22 @@ public class ObjectTimeFieldConverterTest {
final Time time = CONVERTER.convertField(field,
Optional.of(DEFAULT_PATTERN), FIELD_NAME);
assertNotNull(time);
}
+
+ @ParameterizedTest
+
@MethodSource("org.apache.nifi.serialization.record.field.DateTimeTestUtil#offsetSource")
+ public void testConvertFieldTimeOffset(final String offsetId) {
+ final ZoneOffset zoneOffset = ZoneOffset.of(offsetId);
+
+ final LocalTime localTime = LocalTime.parse(TIME_DEFAULT);
+ final ZonedDateTime zonedDateTime = ZonedDateTime.of(LocalDate.EPOCH,
localTime, zoneOffset);
+ final ZonedDateTime zonedDateTimeAdjusted =
zonedDateTime.withZoneSameInstant(ZoneId.systemDefault());
+ final Instant instant = zonedDateTimeAdjusted.toInstant();
+ final long epochMilliseconds = instant.toEpochMilli();
+ final Time expectedTime = new Time(epochMilliseconds);
+
+ final String localTimeWithOffsetId = TIME_DEFAULT + offsetId;
+ final Time convertedTime =
CONVERTER.convertField(localTimeWithOffsetId,
Optional.of(TIME_WITH_OFFSET_PATTERN), FIELD_NAME);
+
+ assertEquals(expectedTime.toString(), convertedTime.toString());
+ }
}