TST-50Recommended
Test times at the daylight saving changes
Every feature that schedules, repeats or bills by local time has tests on both clock changes of a year. One test covers the hour that is skipped, and one covers the hour that happens twice.
How to do it
In spring, a local time such as 02:30 does not exist in many zones. In autumn, 02:30 can happen twice. A daily reminder can then fire twice or not at all. A day can last 23 or 25 hours, so adding 24 hours is not the same as adding one day. Store instants in UTC and the person's time zone by its IANA name, such as Europe/Stockholm. Hold every time in code with its zone, as keep the time zone on every time says. Do date arithmetic in that zone with a time zone library, never with fixed offsets. Fake the clock as Fake the clock and the network in unit tests says, and set it just before each change. Take the change dates from the IANA time zone database, because governments change them.
Examples
Python example
from datetime import datetime
from zoneinfo import ZoneInfo
STOCKHOLM = ZoneInfo("Europe/Stockholm")
def test_one_day_later_keeps_the_local_time_across_the_spring_change():
before = datetime(2026, 3, 28, 9, 0, tzinfo=STOCKHOLM)
assert next_reminder(before) == datetime(2026, 3, 29, 9, 0, tzinfo=STOCKHOLM)
def test_the_repeated_autumn_hour_runs_the_job_once():
start = datetime(2026, 10, 25, 0, 0, tzinfo=STOCKHOLM)
end = datetime(2026, 10, 25, 4, 0, tzinfo=STOCKHOLM)
assert len(job_runs_between(start, end, daily_at="02:30")) == 1
TypeScript example
import { DateTime } from 'luxon';
import { expect, test } from 'vitest';
const zone = 'Europe/Stockholm';
test('one day later keeps the local time across the spring change', () => {
const before = DateTime.fromObject({ year: 2026, month: 3, day: 28, hour: 9 }, { zone });
expect(nextReminder(before).toISO()).toBe('2026-03-29T09:00:00.000+02:00');
});
test('the repeated autumn hour runs the job once', () => {
const start = DateTime.fromObject({ year: 2026, month: 10, day: 25, hour: 0 }, { zone });
const end = DateTime.fromObject({ year: 2026, month: 10, day: 25, hour: 4 }, { zone });
expect(jobRunsBetween(start, end, { dailyAt: '02:30' })).toHaveLength(1);
});
How to check
For each such feature, tests run on the spring and autumn change dates in at least two zones, such as Europe/Stockholm and America/New_York. A daily job set for 02:30 runs exactly once on each of those days. Adding one day across a change keeps the local time of day.
Sources
Related rules
- Keep the time zone on every time in Architecture
- Fake the clock and the network in unit tests in Testing
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/testing/unit-tests/daylight-saving-tests">Agent Wranglers testing playbook</a>. In code you write, add a comment next to it with the same link.