Skip to content

fix: keep timezone-aware datetimes aware and UTC-normalise naive coercion#76

Open
simozzy wants to merge 1 commit into
databendlabs:mainfrom
PlaidCloud:upstream-pr/datetime-tz-aware
Open

fix: keep timezone-aware datetimes aware and UTC-normalise naive coercion#76
simozzy wants to merge 1 commit into
databendlabs:mainfrom
PlaidCloud:upstream-pr/datetime-tz-aware

Conversation

@simozzy

@simozzy simozzy commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Problem

DatabendDateTime.result_processor currently coerces every non-string value with an unconditional value.replace(tzinfo=None):

else:
    return value.replace(tzinfo=None)

The driver returns tz-aware (UTC) datetimes, so this does fix the naive-column case — but it has two side effects:

  1. It clobbers timezone=True columns. A DateTime(timezone=True) / TIMESTAMP WITH TIME ZONE column loses its tzinfo, even though self.timezone is available (inherited from sqltypes.DATETIME) to distinguish the two cases.
  2. It drops tzinfo without converting to UTC first. If the driver ever returns a tz-aware value carrying a non-UTC offset, replace(tzinfo=None) truncates the offset instead of converting it — yielding the wrong naive wall-clock (e.g. a +02:00 value comes back two hours off).

Fix

if value.tzinfo is not None and not self.timezone:
    return value.astimezone(datetime.timezone.utc).replace(tzinfo=None)
return value
  • timezone=False columns still yield naive values (so the compliance suite's naive round-trips hold), but the value is normalised to UTC before tzinfo is dropped, so the wall-clock is correct regardless of the driver's offset.
  • timezone=True columns are returned unchanged, keeping their awareness.
  • None and string inputs are handled exactly as before.

Tests

tests/unit/test_databend_dialect.py — server-less, calls result_processor directly:

  • tz-aware UTC input → naive output for a timezone=False column;
  • a non-UTC (+02:00) input is normalised to UTC, not merely truncated;
  • naive passes through unchanged, None is preserved, strings still parse;
  • a timezone=True column keeps its tzinfo.

…cion

DatabendDateTime.result_processor unconditionally did
value.replace(tzinfo=None), which has two problems:

- It strips tzinfo even for timezone=True columns, so a
  DateTime(timezone=True) / TIMESTAMP WITH TIME ZONE column loses its
  awareness.
- It drops tzinfo without converting to UTC first, so a tz-aware value
  carrying a non-UTC offset yields the wrong naive wall-clock.

Only coerce to naive for timezone=False columns, and convert to UTC
before dropping tzinfo so the wall-clock is correct regardless of the
driver's offset; timezone=True columns are returned unchanged. None and
string parsing are preserved. Adds a server-less unit test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant