Skip to content

fix: Return UTC-aware datetimes from unmarshalling#809

Open
abrookins wants to merge 5 commits intoredis:mainfrom
abrookins:fix/datetime-timezone-aware
Open

fix: Return UTC-aware datetimes from unmarshalling#809
abrookins wants to merge 5 commits intoredis:mainfrom
abrookins:fix/datetime-timezone-aware

Conversation

@abrookins
Copy link
Collaborator

@abrookins abrookins commented Feb 24, 2026

Summary

Fixes #807 - Unmarshalled datetime fields are now UTC-aware instead of naive local time.

Problem

Previously, datetime fields were unmarshalled using datetime.fromtimestamp(value) which returns a naive datetime in the server's local timezone. This caused:

  • Non-deterministic behavior depending on server timezone
  • Inability to compare retrieved datetimes with timezone-aware datetimes
  • Time jumps around daylight savings transitions

Solution

Changed unmarshalling to use datetime.fromtimestamp(value, datetime.timezone.utc) which returns a UTC-aware datetime. This follows the standard ORM pattern of storing UTC and returning UTC-aware datetimes.

Breaking Change

Retrieved datetime fields are now UTC-aware instead of naive local time. Code that compared retrieved datetimes with naive datetimes will need to either:

  1. Make the comparison datetime UTC-aware, or
  2. Use .timestamp() for comparison

Tests

Added new tests for timezone-aware datetime handling and updated existing tests to work with the new behavior.


Co-authored by Augment Code


Note

Medium Risk
Changes the public behavior of datetime fields to return UTC-aware values instead of naive local-time datetimes, which can break callers comparing/serializing datetimes. Scope is limited to timestamp unmarshalling and associated tests.

Overview
Datetime unmarshalling now returns UTC-aware values. convert_timestamp_to_datetime() switches from datetime.fromtimestamp(value) to datetime.fromtimestamp(value, datetime.timezone.utc), making retrieved datetime fields consistently timezone-aware.

Tests are updated to assert UTC tzinfo and compare instants via .timestamp(), plus new coverage for round-tripping non-UTC timezone-aware datetimes in both HashModel and JsonModel. Minor string/formatting cleanups are included in query/schema helpers.

Written by Cursor Bugbot for commit 1bff3fd. This will update automatically on new commits. Configure here.

Previously, datetime fields were unmarshalled using datetime.fromtimestamp(value)
which returns a naive datetime in the server's local timezone. This caused:
- Non-deterministic behavior depending on server timezone
- Inability to compare retrieved datetimes with timezone-aware datetimes
- Time jumps around daylight savings transitions

This fix changes unmarshalling to use datetime.fromtimestamp(value, timezone.utc)
which returns a UTC-aware datetime. This follows the standard ORM pattern of
storing UTC and returning UTC-aware datetimes.

BREAKING CHANGE: Retrieved datetime fields are now UTC-aware instead of naive
local time. Code that compared retrieved datetimes with naive datetimes will
need to either:
1. Make the comparison datetime UTC-aware, or
2. Use .timestamp() for comparison

Fixes redis#807
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

# preferred timezone with dt.astimezone(tz).
dt = datetime.datetime.fromtimestamp(
value, datetime.timezone.utc
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Date fields shift by one day in non-UTC timezones

High Severity

The retrieval path now uses datetime.timezone.utc to interpret timestamps, but convert_datetime_to_timestamp still stores datetime.date values by combining with midnight in local time (naive datetime.datetime.combine(obj, datetime.time.min).timestamp()). When the server is east of UTC, local midnight is the previous day in UTC, so fromtimestamp(value, utc).date() returns the wrong date. For example, a stored date(2023, 1, 1) in UTC+5 round-trips as date(2022, 12, 31).

Additional Locations (1)

Fix in Cursor Fix in Web

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.

Unmarshalled Datetime fields are naive

1 participant