aniketwaghh opened a new issue, #72635:
URL: https://github.com/apache/airflow/issues/72635

   ### Apache Airflow version
   
   main (3.4.0, `123b1be658`)
   
   ### What happened and how to reproduce it?
   
   A naive `datetime` pushed to XCom by one task comes back with a different 
value when the task that pulls it runs on a worker with a different OS 
timezone. Nothing errors; the value is just wrong.
   
   `serialize` in `task-sdk/src/airflow/sdk/serde/serializers/datetime.py` 
stores `o.timestamp()`, which interprets a naive datetime as the **writing** 
process's local time. `deserialize` calls `datetime.fromtimestamp(ts, 
tz=None)`, which renders it in the **reading** process's local time. The two 
are the same machine in a single-process test and different machines in most 
real deployments.
   
   ```python
   # task A, worker with TZ=UTC
   from airflow.sdk.bases.xcom import BaseXCom
   import datetime
   blob = BaseXCom.serialize_value(datetime.datetime(2026, 1, 1, 12, 0, 0))
   
   # task B, worker with TZ=America/New_York
   import types
   BaseXCom.deserialize_value(types.SimpleNamespace(value=blob))
   ```
   
   Running that split three ways on `main`:
   
   ```
   -- control: both workers UTC
     task A on TZ=UTC              pushed 2026-01-01T12:00:00
     task B on TZ=UTC              pulled 2026-01-01T12:00:00
     identical: True
   
   -- worker A UTC, worker B America/New_York
     task A on TZ=UTC              pushed 2026-01-01T12:00:00
     task B on TZ=America/New_York pulled 2026-01-01T07:00:00
     identical: False
   
   -- worker A UTC, worker B Asia/Kolkata
     task A on TZ=UTC              pushed 2026-01-01T12:00:00
     task B on TZ=Asia/Kolkata     pulled 2026-01-01T17:30:00
     identical: False
   ```
   
   A timezone-aware datetime survives the same split unchanged, because the tz 
is stored alongside the timestamp. `datetime.date` is also unaffected — it is 
serialized with `isoformat()` rather than an epoch. So this is specific to 
naive `datetime`.
   
   ### What you think should happen instead?
   
   A naive datetime should come back equal to what went in, and the 
interpretation should not depend on which process reads it.
   
   The timezone docs already define what a naive datetime means in Airflow:
   
   > In case a naive `start_date` or `end_date` is encountered the default time 
zone is applied. It is applied in such a way that it is assumed that the naive 
date time is already in the default time zone.
   
   So the intended anchor is `core.default_timezone`, not the OS local time of 
whichever worker happens to deserialize. The current behaviour is neither: it 
is the writer's OS zone on the way in and the reader's on the way out.
   
   I did look for a documented "naive datetimes are unsupported in XCom" rule 
before filing and could not find one; the paragraph above is the only statement 
of intent I found, and it points the other way.
   
   ### Anything else?
   
   The awkward part is the serialized form rather than the fix. `{"timestamp": 
..., "tz": null}` cannot express "naive" — an epoch is a fixed instant, and 
naive means the opposite. Any correction changes the payload, so it needs 
`__version__` bumped to 3 with the version-2 shape still readable, and XComs 
persist in the database across upgrades. That is a call for maintainers rather 
than something to pick unilaterally, but the options seem to be:
   
   1. Serialize naive datetimes as an ISO wall-clock string, the way `date` 
already is, and keep the `{timestamp, tz}` form for aware ones. Round-trips 
exactly and needs no timezone config at read time.
   2. Anchor naive datetimes to `core.default_timezone` at serialize time and 
store the resulting aware instant, matching the documented interpretation. 
Changes value if the setting changes between write and read.
   3. Store the epoch as now, plus an explicit `naive: true` flag and the wall 
clock, so old readers keep working.
   
   Happy to implement whichever direction you prefer.
   
   ### Are you willing to submit PR?
   
   Yes, once there is a view on which of the above you want.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to