Skip to content

fix(timezone): half-hour DST shift for Australia/Lord_Howe - #464

Merged
spencermountain merged 1 commit into
spencermountain:masterfrom
spokodev:fix/lord-howe-half-hour-dst
Jul 2, 2026
Merged

fix(timezone): half-hour DST shift for Australia/Lord_Howe#464
spencermountain merged 1 commit into
spencermountain:masterfrom
spokodev:fix/lord-howe-half-hour-dst

Conversation

@spokodev

@spokodev spokodev commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Australia/Lord_Howe uses a 30-minute DST shift, but spacetime reports it as a full hour, so every instant in the DST half of the year is 30 minutes off.

Repro

const spacetime = require('spacetime')
const tz = 'Australia/Lord_Howe'

// January = southern summer = DST on
spacetime(new Date('2026-01-15T00:00:00Z').getTime(), tz).offset() // 690  (+11:30)

// native Intl oracle
new Intl.DateTimeFormat('en-US', { timeZone: tz, timeZoneName: 'longOffset' })
  .formatToParts(new Date('2026-01-15T00:00:00Z'))
  .find(p => p.type === 'timeZoneName').value // 'GMT+11:00'

ACTUAL: +11:30 (690 min). EXPECTED: +11:00 (660 min).

Root cause

In src/timezone/index.js, the "other-season" offset is derived by hardcoding a 1-hour DST shift (winter = summer - 1 north, found.offset + 1 south). Lord Howe is the only IANA zone with a 30-minute DST shift (standard +10:30, DST +11:00). Its zonefile stores the standard offset 10.5; adding a full hour yields +11:30 instead of +11:00 for the summer (DST) side. This affects offset(), formatting, and UTC-to-wall-clock conversion for the whole DST half of the year.

The July / standard side was already correct (+10:30), because it reads the stored offset directly.

Fix

Derive the shift per zone and special-case the single half-hour zone:

const dstShift = tz === 'australia/lord_howe' ? 0.5 : 1

Then use dstShift in place of the hardcoded 1 for both hemispheres.

Tests

Added a case to test/dst-south.test.js that asserts offset() against the native Intl longOffset oracle for both seasons: January +11:00 and July +10:30.

Red baseline: with the source change reverted, the January assertion fails (690 vs 660); July already passes. With the fix, both pass.

Full suite (tape): 6080 tests. Before the fix 12 failed (11 pre-existing America/Denver minute-boundary cases in test/dst-sneak.test.js, plus the new Lord Howe case). After the fix 11 fail, all of them the same pre-existing Denver cases, which this change does not touch.

Lord Howe observes a 30-minute DST shift (standard +10:30, DST +11:00),
but the offset derivation hardcoded a 1-hour shift for every zone. During
southern summer this returned +11:30 instead of +11:00, so offset(),
formatting, and UTC-to-wall-clock were 30 minutes ahead for the DST half
of the year. Derive the shift per zone, special-casing the only IANA
half-hour DST zone.
@spencermountain

Copy link
Copy Markdown
Owner

thank you!!

@spencermountain
spencermountain merged commit 63e8c43 into spencermountain:master Jul 2, 2026
2 checks passed
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.

2 participants