2 Commits

Author SHA1 Message Date
baa4f7d5ba Merge pull request 'fix(events): midnight in an announcement is 12:00 am on the Node we ship' (#200) from fix/events-announce-midnight-hourcycle into edge
All checks were successful
PR Checks / client-build (pull_request) Successful in 28s
PR Checks / bot-tests (pull_request) Successful in 29s
PR Checks / server-tests (pull_request) Successful in 13m32s
Reviewed-on: #200
Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
2026-09-10 00:27:58 +00:00
7d7840eb6b fix(events): midnight in an announcement is 12:00 am on the Node we ship
All checks were successful
PR Checks / client-build (pull_request) Successful in 40s
PR Checks / bot-tests (pull_request) Successful in 41s
PR Checks / server-tests (pull_request) Successful in 5m56s
`startsAtLabel` asked for `hour12: true` on `en-GB`. That is not the same
request as a 12-hour clock, and it does not survive a Node upgrade: for a
locale whose default cycle is h23, Node 20 resolves `hour12: true` to h11,
whose hours run 0-11, so midnight renders "0:00 am". Node 22 and later
resolve it to h12 and it renders "12:00 am". Same ICU on both sides, so it
is V8's ECMA-402 behaviour rather than locale data.

The image ships node:20-alpine and CI runs Node 20, while a dev machine is
newer -- which is how this rendered correctly in front of everyone who wrote
it and wrongly for every real recipient. An event mail announcing a midnight
start said "0:00 am" while the schedule editor beside it said "12:00 AM":
one instant, two spellings, which is the exact contradiction the option was
added to prevent.

`hourCycle: 'h12'` is the request that means what was meant. `recurrence.js`
already states the mirror-image rule for `h23`, and every other formatter in
this repo and in module-uo uses `hourCycle`; there is no `hour12` left here.

This is the one test that has been red on every events PR since #192, and
the only one -- each of those runs reported `# fail 1`. Verified by running
the suite under node:20-alpine, where the test fails without this change and
2152 tests pass with it; on Node 22+ it passes either way, so the test's
comment now says that a green run on a dev machine is not evidence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-09 19:20:31 -05:00
2 changed files with 19 additions and 2 deletions

View File

@@ -67,7 +67,18 @@ function startsAtLabel(at, zone) {
// Explicit rather than left to the locale, because `en-GB` would otherwise
// render midnight as "00:00" while the schedule editor beside it writes
// "12:00 AM" — one event, two spellings of the same instant.
hour12: true,
//
// `hourCycle: 'h12'` and NOT `hour12: true`, which is not the same request
// and does not survive a Node upgrade. For a locale whose default cycle is
// h23 — `en-GB` is one — Node 20 resolves `hour12: true` to **h11**, whose
// hours run 011, so midnight comes out "0:00 am"; Node 22 and later
// resolve it to h12 and it comes out "12:00 am". Same ICU on both, so this
// is V8's ECMA-402 behaviour and not locale data, and the image ships
// node:20-alpine while a dev machine is newer — which is how this rendered
// correctly in front of everyone who wrote it and wrongly for every real
// recipient. `recurrence.js` states the mirror-image rule for `h23`; there
// is no `hour12` left in this repo and it should stay that way.
hourCycle: 'h12',
}).format(when)
return `${text} (${zone || 'UTC'})`
} catch {

View File

@@ -260,9 +260,15 @@ test('the start time is written out in the SHARD\'s zone, not the server\'s', ()
})
test('midnight reads as 12:00 am and never as 00:00', () => {
// `hour12` is set explicitly. Left to the en-GB locale this would render
// The hour cycle is set explicitly. Left to the en-GB locale this would render
// "00:00" while the schedule editor beside it writes "12:00 AM" — one event,
// two spellings of the same instant.
//
// This assertion only has teeth on the Node the image ships (20), where
// `hour12: true` resolves to h11 and midnight reads "0:00 am". On Node 22+ it
// passes either way — so a green run on a dev machine is not evidence, and CI
// is what actually holds this line. See the note beside `hourCycle` in
// events/announce.js.
assert.match(announce.startsAtLabel(new Date('2026-09-13T04:00:00Z'), 'America/New_York'), /12:00 am/)
})