Skip to content

Conversation

@zzstoatzz
Copy link
Collaborator

@zzstoatzz zzstoatzz commented Oct 28, 2025

Summary

We discovered a regression in interval schedules after the pendulum → datetime/zoneinfo refactor. When the schedule JSON contains a fixed-offset anchor such as "2024-07-01T01:15:00+09:00" alongside a non-UTC timezone like "Asia/Tokyo", the scheduler drifts runs by the offset—so 01:15 JST became 10:15 JST.

Changes

The culprit is the new tzinfo "normalization." For tzinfos that aren't ZoneInfo, the code called .tzname() to get a timezone name; offset tzinfos return strings like "UTC+09:00", which fails when trying to create a ZoneInfo from that invalid key.

The fix uses .astimezone(ZoneInfo(target_timezone)) for offset-based tzinfos, which preserves both the absolute instant and microsecond precision. Naive datetimes still assume the schedule's timezone, and true ZoneInfo objects continue down the existing .to_tz() path.

Testing

Added regression test test_offset_anchor_preserves_local_hour which:

  • Only runs on Python 3.13+ (where the bug exists)
  • Creates an interval schedule with offset-based anchor and named timezone
  • Verifies scheduled times preserve the local hour instead of drifting

Test confirmed:

  • ❌ Fails on main (ZoneInfoNotFoundError: 'No time zone found with key UTC+09:00')
  • ✅ Passes with fix

All existing interval schedule tests and scheduler service tests pass on Python 3.13.

🤖 Generated with Claude Code

zzstoatzz and others added 2 commits October 28, 2025 18:14
We discovered a regression in interval schedules after the pendulum → datetime/zoneinfo refactor. When the schedule JSON contains a fixed-offset anchor such as "2024-07-01T01:15:00+09:00" alongside a non-UTC timezone like "Asia/Tokyo", the scheduler drifts runs by the offset—so 01:15 JST became 10:15 JST.

The culprit is the new tzinfo "normalization." For tzinfos that aren't ZoneInfo, we called `.tzname()` to get a timezone name; offset tzinfos return strings like "UTC+09:00", so we tried to create a ZoneInfo from that invalid key, which fails.

The fix keeps offset tzinfos anchored to their original instant by projecting through dt.timestamp() into the target zone. Naive datetimes still assume the schedule's timezone, and true ZoneInfo objects continue down the existing path.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@github-actions github-actions bot added the docs label Oct 28, 2025
@zzstoatzz zzstoatzz added the fix A fix for a bug in an existing feature label Oct 28, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Oct 28, 2025

CodSpeed Performance Report

Merging #19301 will not alter performance

Comparing fix-interval-schedule-offset-anchor-timezone (bc18be9) with main (1083cf4)

Summary

✅ 2 untouched

@zzstoatzz zzstoatzz marked this pull request as draft October 28, 2025 23:27
@zzstoatzz zzstoatzz force-pushed the fix-interval-schedule-offset-anchor-timezone branch from e656b91 to 87c28f1 Compare October 28, 2025 23:31
@zzstoatzz zzstoatzz marked this pull request as ready for review October 29, 2025 00:15
@zzstoatzz zzstoatzz merged commit 1eabc20 into main Oct 29, 2025
57 checks passed
@zzstoatzz zzstoatzz deleted the fix-interval-schedule-offset-anchor-timezone branch October 29, 2025 14:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs fix A fix for a bug in an existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants