Testland
Browse all skills & agents

dst-transition-reference

Pure-reference catalog of Daylight Saving Time (DST) transition patterns and their canonical bug classes. Covers the spring-forward (skipped hour: 02:00 → 03:00 local) and fall-back (repeated hour: 02:00 → 01:00 local) transitions, the historical irregularity of DST (different jurisdictions, transitions on different dates, some regions abolish DST or never adopted it), the IANA timezone database (tz / Olson DB) as the canonical source, and the testable behaviors DST creates (duplicate / missing local timestamps, cron jobs that fire 0 or 2 times, billing periods that miss / double-count, recurring meetings on transition days). Use when designing or auditing time-handling code or test cases. Composes leap-second-reference + iso-8601-vs-rfc-3339-reference.

dst-transition-reference

Overview

DST transitions are responsible for a large share of production time-bugs. They happen twice yearly in many jurisdictions, on dates that vary by region, and create non-existent local times (spring-forward) and duplicate local times (fall-back).

The IANA Time Zone Database (iana.org/time-zones) - also known as the tz database or the Olson database - is the canonical source of historical and current DST rules.

When to use

  • Designing time-handling code that crosses DST boundaries.
  • Auditing existing code for DST-safety.
  • Writing test cases that exercise DST behaviour.
  • Investigating "scheduled job ran twice / didn't run" reports.

DST mechanics

Spring-forward (skipped hour)

Per en.wikipedia.org/wiki/Daylight_saving_time, in US Eastern: on the 2nd Sunday of March, at 02:00 local time the clock jumps to 03:00. The 02:00-02:59 hour does not exist in local time.

Tests against 2026-03-08 02:30 America/New_York produce ambiguous or invalid results depending on library:

LibraryBehaviour at non-existent local time
Python pytz (legacy)pytz.exceptions.NonExistentTimeError
Python zoneinfo (3.9+)Returns the "would-be" time + 1h (=03:30 EDT)
Java ZonedDateTimeConstructor takes a resolver: STRICT / SMART_BACKWARD / SMART_FORWARD
JS IntlBrowsers vary; often returns the post-transition time

Fall-back (repeated hour)

In US Eastern: 1st Sunday of November at 02:00 local time, the clock falls back to 01:00. The 01:00-01:59 hour occurs twice - once as EDT (UTC-4), once as EST (UTC-5).

2026-11-01 01:30 America/New_York is ambiguous. Libraries either:

  • Pick one (typically the first occurrence in pytz/zoneinfo)
  • Raise an error
  • Take an is_dst / fold flag (Python 3.6+ has fold=0|1)

Per-jurisdiction differences

RegionDST behaviour
US (most)Spring-forward 2nd Sun March; fall-back 1st Sun November
EULast Sun March; last Sun October (one hour earlier)
Australia (most of NSW/VIC)First Sun October; first Sun April (Southern hemisphere - reversed)
Australia (QLD, NT, WA, NT)No DST
Japan, China, IndiaNo DST
RussiaAbolished DST in 2011
IranAbolished DST in 2022
MexicoAbolished mainland DST in 2022
BrazilAbolished DST in 2019

Per IANA: rules change frequently. Test against current zoneinfo, not assumptions.

Common bug classes

Cron jobs

A "daily at 02:30" cron in America/New_York:

  • Spring-forward day: doesn't fire (02:30 doesn't exist).
  • Fall-back day: fires twice (02:30 EDT, then 02:30 EST).

Mitigation:

  • Use UTC cron expressions when possible.
  • For local-time business hours, accept the irregularity or schedule outside transition hours (04:00 is safe everywhere).
  • Per qa-async-jobs/cron-job-test-author: always test DST + leap-day edge cases.

Billing periods

"Bill on the 1st of each month at 00:00 local time":

  • Fine in jurisdictions without DST.
  • Risk in DST-observing: 00:00 local on Nov 1 (US) might overlap with the fall-back hour if billing involves more than one event.

Mitigation: bill at UTC, or at a local hour known to be safe (e.g., 06:00).

Duration arithmetic

tomorrow_same_time = today_same_time + Duration("24 hours"):

  • Spring-forward: result is 23 hours later in local time.
  • Fall-back: result is 25 hours later in local time.

Mitigation: distinguish "24 hours from now" (Duration) from "this time tomorrow" (calendar addition).

Recurring meeting

"Every Monday at 09:00 local time":

  • Crosses DST boundary → still 09:00 local, but 2 minutes before or after the UTC equivalent of the previous week.
  • Calendar systems handle this; custom scheduling code often doesn't.

Storage

Storing wall-clock-local strings ("2026-03-08 02:30") is unsafe across DST. Always store UTC + zone identifier.

Testable behaviours

BehaviourTest
Code handles non-existent local timeConstruct 2026-03-08 02:30 America/New_York; library raises or normalises; assert expected
Code handles ambiguous local timeConstruct 2026-11-01 01:30 America/New_York; library raises or picks; assert
Cron-equivalent fires 0 / 1 / 2 timesSimulate clock across the transition; count invocations
Duration vs calendar addition consistentAssert difference on transition day
Storage uses UTC + zoneParse stored value; expect ISO format with offset or Z

Per timezone-test-matrix-builder, the test matrix combines (zone, transition-type, library-version).

Test data fixtures

Useful canonical timestamps per region (refresh against IANA):

RegionSpring-forward 2026Fall-back 2026
America/New_York2026-03-08 02:00 → 03:00 EDT2026-11-01 02:00 → 01:00 EST
Europe/London2026-03-29 01:00 → 02:00 BST2026-10-25 02:00 → 01:00 GMT
Australia/Sydney2026-10-04 02:00 → 03:00 AEDT2026-04-05 03:00 → 02:00 AEST

These dates change year to year (some); commit a current fixture and refresh annually.

Anti-patterns

Anti-patternWhy it failsFix
Storing local times as stringsAmbiguous on fall-back; nonexistent on spring-forwardUTC + zone, or RFC 3339 with explicit offset
Assuming all jurisdictions observe DSTHalf the world doesn'tPer-zone testing
Using "24 hours" for "tomorrow"Off by 1 hour on transition daysCalendar arithmetic primitives
Pinning to a specific year's transition dateRules change annuallyUse IANA zoneinfo dynamically
Crossing DST with naive datetimeBehaviour undefinedAlways tz-aware
Cron in local time without DST testingMisses / duplicates jobsTest transition days
Hardcoded UTC offset (-5:00)Wrong when DST is in effectUse zone identifier

Limitations

  • IANA zoneinfo changes throughout the year. Sept 2026 may add or remove DST observance for some jurisdictions; test data goes stale.
  • OS / runtime zoneinfo versions differ. Java's tzdata ships with the JDK; system tzdata is separate; Python's zoneinfo reads system tzdata. Mismatches cause subtle bugs.
  • Polar regions, antimeridian, and historic timezones. Special cases not covered here.
  • Doesn't address leap seconds. See leap-second-reference.

References