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

shahar1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new ad3ecb8c1ab Fix serialization of datetimes with a fixed UTC offset 
(#74922)
ad3ecb8c1ab is described below

commit ad3ecb8c1ab40b4acfee920fd7e6b2743544bdb5
Author: Justin Pakzad <[email protected]>
AuthorDate: Sat Oct 24 16:13:44 2026 -0400

    Fix serialization of datetimes with a fixed UTC offset (#74922)
---
 task-sdk/src/airflow/sdk/serde/serializers/timezone.py |  8 ++++++--
 task-sdk/tests/task_sdk/serde/test_serializers.py      | 17 ++++++++++++++---
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/task-sdk/src/airflow/sdk/serde/serializers/timezone.py 
b/task-sdk/src/airflow/sdk/serde/serializers/timezone.py
index ae35280b674..84d5c5f717c 100644
--- a/task-sdk/src/airflow/sdk/serde/serializers/timezone.py
+++ b/task-sdk/src/airflow/sdk/serde/serializers/timezone.py
@@ -61,8 +61,12 @@ def serialize(o: object) -> tuple[U, str, int, bool]:
     if tz_name is not None:
         return tz_name, name, __version__, True
 
-    if cast("datetime.tzinfo", o).utcoffset(None) == datetime.timedelta(0):
-        return "UTC", qualname(FixedTimezone), __version__, True
+    offset = cast("datetime.tzinfo", o).utcoffset(None)
+    if offset is not None:
+        seconds = int(offset.total_seconds())
+        if seconds == 0:
+            return "UTC", qualname(FixedTimezone), __version__, True
+        return seconds, qualname(FixedTimezone), __version__, True
 
     return "", "", 0, False
 
diff --git a/task-sdk/tests/task_sdk/serde/test_serializers.py 
b/task-sdk/tests/task_sdk/serde/test_serializers.py
index 202184321e3..71180f59e49 100644
--- a/task-sdk/tests/task_sdk/serde/test_serializers.py
+++ b/task-sdk/tests/task_sdk/serde/test_serializers.py
@@ -64,7 +64,7 @@ class CustomTZ(datetime.tzinfo):
 
 class NoNameTZ(datetime.tzinfo):
     def utcoffset(self, dt):
-        return datetime.timedelta(hours=2)
+        return None
 
 
 class FooBarModel(BaseModel):
@@ -107,16 +107,21 @@ class TestSerializers:
             pytest.param(DateTime(2022, 7, 10, tzinfo=tzutc()), 
id="datetime_tzutc"),
             pytest.param(DateTime(2022, 7, 10, 
tzinfo=ZoneInfo("Europe/Paris")), id="datetime_zoneinfo"),
             pytest.param(datetime.datetime.now(), id="datetime_now"),
+            pytest.param(
+                datetime.datetime(2022, 7, 10, 
tzinfo=datetime.timezone(datetime.timedelta(hours=2))),
+                id="datetime_stdlib_fixed_offset",
+            ),
         ],
     )
     def test_datetime(self, input_obj):
         """Test serialization and deserialization of various datetime-related 
objects."""
         serialized_obj = serialize(input_obj)
         deserialized_obj = deserialize(serialized_obj)
-        if isinstance(input_obj, (datetime.date, datetime.timedelta, Date)):
+        if isinstance(input_obj, datetime.datetime):
             assert input_obj == deserialized_obj
+            assert input_obj.utcoffset() == deserialized_obj.utcoffset()
         else:
-            assert input_obj.timestamp() == deserialized_obj.timestamp()
+            assert input_obj == deserialized_obj
 
     @pytest.mark.parametrize(
         ("tz_input", "expected_tz_name"),
@@ -625,6 +630,12 @@ class TestSerializers:
 
         assert serialize(FixedTimezone(0)) == ("UTC", 
"pendulum.tz.timezone.FixedTimezone", 1, True)
 
+    def test_timezone_serialize_fixed_offset(self):
+        from airflow.sdk.serde.serializers.timezone import serialize
+
+        tz = datetime.timezone(datetime.timedelta(hours=-5))
+        assert serialize(tz) == (-18000, "pendulum.tz.timezone.FixedTimezone", 
1, True)
+
     def test_timezone_serialize_no_name(self):
         from airflow.sdk.serde.serializers.timezone import serialize
 

Reply via email to