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