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

Reply via email to