From f97640abefe1c007cb40ee76805f295b9650a05f Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 7 May 2026 17:31:54 -0700 Subject: [PATCH 1/4] Add startOfDay()/hoursInDay invariants to test/thorough/startofday.mjs For all of our interesting ZonedDateTimes, test the startOfDay() and hoursInDay invariants. See discussion in https://github.com/tc39/proposal-temporal/issues/3312 --- polyfill/test/thorough/startofday.mjs | 37 +++- .../test/thorough/startofday.snapshot.json | 158 ++++++++++++++++++ 2 files changed, 193 insertions(+), 2 deletions(-) diff --git a/polyfill/test/thorough/startofday.mjs b/polyfill/test/thorough/startofday.mjs index 33070973c..970b5ef1b 100644 --- a/polyfill/test/thorough/startofday.mjs +++ b/polyfill/test/thorough/startofday.mjs @@ -1,4 +1,12 @@ -import { getProgressBar, makeZonedCases, time, withSnapshotsFromFile } from './support.mjs'; +import { + assertEqual, + assertTemporalEqual, + getProgressBar, + makeZonedCases, + roundingModes, + time, + withSnapshotsFromFile +} from './support.mjs'; const interestingCases = makeZonedCases().filter( ([datetime]) => @@ -13,7 +21,32 @@ await time(async (start) => { await withSnapshotsFromFile('./startofday.snapshot.json', (matchSnapshot) => { for (const [datetime, str] of interestingCases) { progress.tick(1, { test: str }); - matchSnapshot(datetime.startOfDay().toString(), str); + const startToday = datetime.startOfDay(); + matchSnapshot(startToday.toString(), str); + + // Can't get tomorrow's startOfDay() if this is the last representable day + if (datetime.epochMilliseconds > 86400_0000_0000 - 86400) continue; + + const hoursInDay = datetime.hoursInDay; + matchSnapshot(hoursInDay, str + 'h'); + + // Invariant: startOfDay + hoursInDay = following day's startOfDay + const startTomorrow = datetime.add({ days: 1 }).startOfDay(); + assertTemporalEqual( + startToday.add({ seconds: hoursInDay * 3600 }), + startTomorrow, + `${startToday} + ${hoursInDay}h = ${startTomorrow}` + ); + + // Invariant: round({ smallestUnit: 'day' }) is start of today or tomorrow + for (const roundingMode of roundingModes) { + const rounded = datetime.round({ smallestUnit: 'day', roundingMode }); + assertEqual( + rounded.equals(startToday) || rounded.equals(startTomorrow), + true, + `${rounded} = ${startToday} or ${startTomorrow}` + ); + } } }); diff --git a/polyfill/test/thorough/startofday.snapshot.json b/polyfill/test/thorough/startofday.snapshot.json index 03e4367cc..db9f77592 100644 --- a/polyfill/test/thorough/startofday.snapshot.json +++ b/polyfill/test/thorough/startofday.snapshot.json @@ -10,163 +10,321 @@ "+275760-09-13T01:23:00+01:23[+01:23]": "+275760-09-13T00:00:00+01:23[+01:23]", "+275760-09-13T02:00:00+02:00[Europe/Amsterdam]": "+275760-09-13T00:00:00+02:00[Europe/Amsterdam]", "-000001-01-01T00:00:00+00:00[UTC]": "-000001-01-01T00:00:00+00:00[UTC]", + "-000001-01-01T00:00:00+00:00[UTC]h": 24, "-000001-01-01T00:17:30+00:18[Europe/Amsterdam]": "-000001-01-01T00:00:00+00:18[Europe/Amsterdam]", + "-000001-01-01T00:17:30+00:18[Europe/Amsterdam]h": 24, "-000001-01-01T01:23:00+01:23[+01:23]": "-000001-01-01T00:00:00+01:23[+01:23]", + "-000001-01-01T01:23:00+01:23[+01:23]h": 24, "-000002-12-31T11:26:00-12:34[-12:34]": "-000002-12-31T00:00:00-12:34[-12:34]", + "-000002-12-31T11:26:00-12:34[-12:34]h": 24, "-000002-12-31T15:47:32-08:12[America/Vancouver]": "-000002-12-31T00:00:00-08:12[America/Vancouver]", + "-000002-12-31T15:47:32-08:12[America/Vancouver]h": 24, "-271821-04-20T00:00:00+00:00[UTC]": "-271821-04-20T00:00:00+00:00[UTC]", + "-271821-04-20T00:00:00+00:00[UTC]h": 24, "-271821-04-20T11:26:00-12:34[-12:34]": "-271821-04-20T00:00:00-12:34[-12:34]", + "-271821-04-20T11:26:00-12:34[-12:34]h": 24, "-271821-04-20T15:47:32-08:12[America/Vancouver]": "-271821-04-20T00:00:00-08:12[America/Vancouver]", + "-271821-04-20T15:47:32-08:12[America/Vancouver]h": 24, "-271821-04-21T00:00:00+00:00[UTC]": "-271821-04-21T00:00:00+00:00[UTC]", + "-271821-04-21T00:00:00+00:00[UTC]h": 24, "-271821-04-21T00:17:30+00:18[Europe/Amsterdam]": "-271821-04-21T00:00:00+00:18[Europe/Amsterdam]", + "-271821-04-21T00:17:30+00:18[Europe/Amsterdam]h": 24, "-271821-04-21T01:23:00+01:23[+01:23]": "-271821-04-21T00:00:00+01:23[+01:23]", + "-271821-04-21T01:23:00+01:23[+01:23]h": 24, "0000-12-31T11:26:00-12:34[-12:34]": "0000-12-31T00:00:00-12:34[-12:34]", + "0000-12-31T11:26:00-12:34[-12:34]h": 24, "0000-12-31T15:47:32-08:12[America/Vancouver]": "0000-12-31T00:00:00-08:12[America/Vancouver]", + "0000-12-31T15:47:32-08:12[America/Vancouver]h": 24, "0001-01-01T00:00:00+00:00[UTC]": "0001-01-01T00:00:00+00:00[UTC]", + "0001-01-01T00:00:00+00:00[UTC]h": 24, "0001-01-01T00:17:30+00:18[Europe/Amsterdam]": "0001-01-01T00:00:00+00:18[Europe/Amsterdam]", + "0001-01-01T00:17:30+00:18[Europe/Amsterdam]h": 24, "0001-01-01T01:23:00+01:23[+01:23]": "0001-01-01T00:00:00+01:23[+01:23]", + "0001-01-01T01:23:00+01:23[+01:23]h": 24, "1385-06-11T11:51:26.290448384-12:34[-12:34]": "1385-06-11T00:00:00-12:34[-12:34]", + "1385-06-11T11:51:26.290448384-12:34[-12:34]h": 24, "1385-06-11T11:51:26.290448385-12:34[-12:34]": "1385-06-11T00:00:00-12:34[-12:34]", + "1385-06-11T11:51:26.290448385-12:34[-12:34]h": 24, "1385-06-11T16:12:58.290448384-08:12[America/Vancouver]": "1385-06-11T00:00:00-08:12[America/Vancouver]", + "1385-06-11T16:12:58.290448384-08:12[America/Vancouver]h": 24, "1385-06-11T16:12:58.290448385-08:12[America/Vancouver]": "1385-06-11T00:00:00-08:12[America/Vancouver]", + "1385-06-11T16:12:58.290448385-08:12[America/Vancouver]h": 24, "1385-06-12T00:25:26.290448384+00:00[UTC]": "1385-06-12T00:00:00+00:00[UTC]", + "1385-06-12T00:25:26.290448384+00:00[UTC]h": 24, "1385-06-12T00:25:26.290448385+00:00[UTC]": "1385-06-12T00:00:00+00:00[UTC]", + "1385-06-12T00:25:26.290448385+00:00[UTC]h": 24, "1385-06-12T00:42:56.290448384+00:18[Europe/Amsterdam]": "1385-06-12T00:00:00+00:18[Europe/Amsterdam]", + "1385-06-12T00:42:56.290448384+00:18[Europe/Amsterdam]h": 24, "1385-06-12T00:42:56.290448385+00:18[Europe/Amsterdam]": "1385-06-12T00:00:00+00:18[Europe/Amsterdam]", + "1385-06-12T00:42:56.290448385+00:18[Europe/Amsterdam]h": 24, "1385-06-12T01:48:26.290448384+01:23[+01:23]": "1385-06-12T00:00:00+01:23[+01:23]", + "1385-06-12T01:48:26.290448384+01:23[+01:23]h": 24, "1385-06-12T01:48:26.290448385+01:23[+01:23]": "1385-06-12T00:00:00+01:23[+01:23]", + "1385-06-12T01:48:26.290448385+01:23[+01:23]h": 24, "1677-09-20T11:38:43.145224191-12:34[-12:34]": "1677-09-20T00:00:00-12:34[-12:34]", + "1677-09-20T11:38:43.145224191-12:34[-12:34]h": 24, "1677-09-20T11:38:43.145224192-12:34[-12:34]": "1677-09-20T00:00:00-12:34[-12:34]", + "1677-09-20T11:38:43.145224192-12:34[-12:34]h": 24, "1677-09-20T16:00:15.145224191-08:12[America/Vancouver]": "1677-09-20T00:00:00-08:12[America/Vancouver]", + "1677-09-20T16:00:15.145224191-08:12[America/Vancouver]h": 24, "1677-09-20T16:00:15.145224192-08:12[America/Vancouver]": "1677-09-20T00:00:00-08:12[America/Vancouver]", + "1677-09-20T16:00:15.145224192-08:12[America/Vancouver]h": 24, "1677-09-21T00:12:43.145224191+00:00[UTC]": "1677-09-21T00:00:00+00:00[UTC]", + "1677-09-21T00:12:43.145224191+00:00[UTC]h": 24, "1677-09-21T00:12:43.145224192+00:00[UTC]": "1677-09-21T00:00:00+00:00[UTC]", + "1677-09-21T00:12:43.145224192+00:00[UTC]h": 24, "1677-09-21T00:30:13.145224191+00:18[Europe/Amsterdam]": "1677-09-21T00:00:00+00:18[Europe/Amsterdam]", + "1677-09-21T00:30:13.145224191+00:18[Europe/Amsterdam]h": 24, "1677-09-21T00:30:13.145224192+00:18[Europe/Amsterdam]": "1677-09-21T00:00:00+00:18[Europe/Amsterdam]", + "1677-09-21T00:30:13.145224192+00:18[Europe/Amsterdam]h": 24, "1677-09-21T01:35:43.145224191+01:23[+01:23]": "1677-09-21T00:00:00+01:23[+01:23]", + "1677-09-21T01:35:43.145224191+01:23[+01:23]h": 24, "1677-09-21T01:35:43.145224192+01:23[+01:23]": "1677-09-21T00:00:00+01:23[+01:23]", + "1677-09-21T01:35:43.145224192+01:23[+01:23]h": 24, "1901-12-13T08:11:51-12:34[-12:34]": "1901-12-13T00:00:00-12:34[-12:34]", + "1901-12-13T08:11:51-12:34[-12:34]h": 24, "1901-12-13T08:11:52-12:34[-12:34]": "1901-12-13T00:00:00-12:34[-12:34]", + "1901-12-13T08:11:52-12:34[-12:34]h": 24, "1901-12-13T12:45:51-08:00[America/Vancouver]": "1901-12-13T00:00:00-08:00[America/Vancouver]", + "1901-12-13T12:45:51-08:00[America/Vancouver]h": 24, "1901-12-13T12:45:52-08:00[America/Vancouver]": "1901-12-13T00:00:00-08:00[America/Vancouver]", + "1901-12-13T12:45:52-08:00[America/Vancouver]h": 24, "1901-12-13T20:45:51+00:00[Europe/Amsterdam]": "1901-12-13T00:00:00+00:00[Europe/Amsterdam]", + "1901-12-13T20:45:51+00:00[Europe/Amsterdam]h": 24, "1901-12-13T20:45:51+00:00[UTC]": "1901-12-13T00:00:00+00:00[UTC]", + "1901-12-13T20:45:51+00:00[UTC]h": 24, "1901-12-13T20:45:52+00:00[Europe/Amsterdam]": "1901-12-13T00:00:00+00:00[Europe/Amsterdam]", + "1901-12-13T20:45:52+00:00[Europe/Amsterdam]h": 24, "1901-12-13T20:45:52+00:00[UTC]": "1901-12-13T00:00:00+00:00[UTC]", + "1901-12-13T20:45:52+00:00[UTC]h": 24, "1901-12-13T22:08:51+01:23[+01:23]": "1901-12-13T00:00:00+01:23[+01:23]", + "1901-12-13T22:08:51+01:23[+01:23]h": 24, "1901-12-13T22:08:52+01:23[+01:23]": "1901-12-13T00:00:00+01:23[+01:23]", + "1901-12-13T22:08:52+01:23[+01:23]h": 24, "1919-03-31T12:00:00-04:00[America/Toronto]": "1919-03-31T00:30:00-04:00[America/Toronto]", + "1919-03-31T12:00:00-04:00[America/Toronto]h": 23.5, "1938-04-24T09:39:20-12:34[-12:34]": "1938-04-24T00:00:00-12:34[-12:34]", + "1938-04-24T09:39:20-12:34[-12:34]h": 24, "1938-04-24T14:13:20-08:00[America/Vancouver]": "1938-04-24T00:00:00-08:00[America/Vancouver]", + "1938-04-24T14:13:20-08:00[America/Vancouver]h": 24, "1938-04-24T22:13:20+00:00[UTC]": "1938-04-24T00:00:00+00:00[UTC]", + "1938-04-24T22:13:20+00:00[UTC]h": 24, "1938-04-24T23:13:20+01:00[Europe/Amsterdam]": "1938-04-24T00:00:00+01:00[Europe/Amsterdam]", + "1938-04-24T23:13:20+01:00[Europe/Amsterdam]h": 24, "1938-04-24T23:36:20+01:23[+01:23]": "1938-04-24T00:00:00+01:23[+01:23]", + "1938-04-24T23:36:20+01:23[+01:23]h": 24, "1969-09-18T05:25:59.757604687-12:34[-12:34]": "1969-09-18T00:00:00-12:34[-12:34]", + "1969-09-18T05:25:59.757604687-12:34[-12:34]h": 24, "1969-09-18T10:59:59.757604687-07:00[America/Vancouver]": "1969-09-18T00:00:00-07:00[America/Vancouver]", + "1969-09-18T10:59:59.757604687-07:00[America/Vancouver]h": 24, "1969-09-18T17:59:59.757604687+00:00[UTC]": "1969-09-18T00:00:00+00:00[UTC]", + "1969-09-18T17:59:59.757604687+00:00[UTC]h": 24, "1969-09-18T18:59:59.757604687+01:00[Europe/Amsterdam]": "1969-09-18T00:00:00+01:00[Europe/Amsterdam]", + "1969-09-18T18:59:59.757604687+01:00[Europe/Amsterdam]h": 24, "1969-09-18T19:22:59.757604687+01:23[+01:23]": "1969-09-18T00:00:00+01:23[+01:23]", + "1969-09-18T19:22:59.757604687+01:23[+01:23]h": 24, "1969-12-31T11:25:55.705032704-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:25:55.705032704-12:34[-12:34]h": 24, "1969-12-31T11:25:55.705032705-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:25:55.705032705-12:34[-12:34]h": 24, "1969-12-31T11:25:57.852516351-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:25:57.852516351-12:34[-12:34]h": 24, "1969-12-31T11:25:57.852516352-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:25:57.852516352-12:34[-12:34]h": 24, "1969-12-31T11:25:59.999999999-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:25:59.999999999-12:34[-12:34]h": 24, "1969-12-31T11:26:00-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:26:00-12:34[-12:34]h": 24, "1969-12-31T11:26:00.000000001-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:26:00.000000001-12:34[-12:34]h": 24, "1969-12-31T11:26:02.147483647-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:26:02.147483647-12:34[-12:34]h": 24, "1969-12-31T11:26:02.147483648-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:26:02.147483648-12:34[-12:34]h": 24, "1969-12-31T11:26:04.294967295-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:26:04.294967295-12:34[-12:34]h": 24, "1969-12-31T11:26:04.294967296-12:34[-12:34]": "1969-12-31T00:00:00-12:34[-12:34]", + "1969-12-31T11:26:04.294967296-12:34[-12:34]h": 24, "1969-12-31T15:59:55.705032704-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T15:59:55.705032704-08:00[America/Vancouver]h": 24, "1969-12-31T15:59:55.705032705-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T15:59:55.705032705-08:00[America/Vancouver]h": 24, "1969-12-31T15:59:57.852516351-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T15:59:57.852516351-08:00[America/Vancouver]h": 24, "1969-12-31T15:59:57.852516352-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T15:59:57.852516352-08:00[America/Vancouver]h": 24, "1969-12-31T15:59:59.999999999-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T15:59:59.999999999-08:00[America/Vancouver]h": 24, "1969-12-31T16:00:00-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T16:00:00-08:00[America/Vancouver]h": 24, "1969-12-31T16:00:00.000000001-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T16:00:00.000000001-08:00[America/Vancouver]h": 24, "1969-12-31T16:00:02.147483647-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T16:00:02.147483647-08:00[America/Vancouver]h": 24, "1969-12-31T16:00:02.147483648-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T16:00:02.147483648-08:00[America/Vancouver]h": 24, "1969-12-31T16:00:04.294967295-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T16:00:04.294967295-08:00[America/Vancouver]h": 24, "1969-12-31T16:00:04.294967296-08:00[America/Vancouver]": "1969-12-31T00:00:00-08:00[America/Vancouver]", + "1969-12-31T16:00:04.294967296-08:00[America/Vancouver]h": 24, "1969-12-31T23:59:55.705032704+00:00[UTC]": "1969-12-31T00:00:00+00:00[UTC]", + "1969-12-31T23:59:55.705032704+00:00[UTC]h": 24, "1969-12-31T23:59:55.705032705+00:00[UTC]": "1969-12-31T00:00:00+00:00[UTC]", + "1969-12-31T23:59:55.705032705+00:00[UTC]h": 24, "1969-12-31T23:59:57.852516351+00:00[UTC]": "1969-12-31T00:00:00+00:00[UTC]", + "1969-12-31T23:59:57.852516351+00:00[UTC]h": 24, "1969-12-31T23:59:57.852516352+00:00[UTC]": "1969-12-31T00:00:00+00:00[UTC]", + "1969-12-31T23:59:57.852516352+00:00[UTC]h": 24, "1969-12-31T23:59:59.999999999+00:00[UTC]": "1969-12-31T00:00:00+00:00[UTC]", + "1969-12-31T23:59:59.999999999+00:00[UTC]h": 24, "1970-01-01T00:00:00+00:00[UTC]": "1970-01-01T00:00:00+00:00[UTC]", + "1970-01-01T00:00:00+00:00[UTC]h": 24, "1970-01-01T00:00:00.000000001+00:00[UTC]": "1970-01-01T00:00:00+00:00[UTC]", + "1970-01-01T00:00:00.000000001+00:00[UTC]h": 24, "1970-01-01T00:00:02.147483647+00:00[UTC]": "1970-01-01T00:00:00+00:00[UTC]", + "1970-01-01T00:00:02.147483647+00:00[UTC]h": 24, "1970-01-01T00:00:02.147483648+00:00[UTC]": "1970-01-01T00:00:00+00:00[UTC]", + "1970-01-01T00:00:02.147483648+00:00[UTC]h": 24, "1970-01-01T00:00:04.294967295+00:00[UTC]": "1970-01-01T00:00:00+00:00[UTC]", + "1970-01-01T00:00:04.294967295+00:00[UTC]h": 24, "1970-01-01T00:00:04.294967296+00:00[UTC]": "1970-01-01T00:00:00+00:00[UTC]", + "1970-01-01T00:00:04.294967296+00:00[UTC]h": 24, "1970-01-01T00:59:55.705032704+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T00:59:55.705032704+01:00[Europe/Amsterdam]h": 24, "1970-01-01T00:59:55.705032705+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T00:59:55.705032705+01:00[Europe/Amsterdam]h": 24, "1970-01-01T00:59:57.852516351+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T00:59:57.852516351+01:00[Europe/Amsterdam]h": 24, "1970-01-01T00:59:57.852516352+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T00:59:57.852516352+01:00[Europe/Amsterdam]h": 24, "1970-01-01T00:59:59.999999999+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T00:59:59.999999999+01:00[Europe/Amsterdam]h": 24, "1970-01-01T01:00:00+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T01:00:00+01:00[Europe/Amsterdam]h": 24, "1970-01-01T01:00:00.000000001+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T01:00:00.000000001+01:00[Europe/Amsterdam]h": 24, "1970-01-01T01:00:02.147483647+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T01:00:02.147483647+01:00[Europe/Amsterdam]h": 24, "1970-01-01T01:00:02.147483648+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T01:00:02.147483648+01:00[Europe/Amsterdam]h": 24, "1970-01-01T01:00:04.294967295+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T01:00:04.294967295+01:00[Europe/Amsterdam]h": 24, "1970-01-01T01:00:04.294967296+01:00[Europe/Amsterdam]": "1970-01-01T00:00:00+01:00[Europe/Amsterdam]", + "1970-01-01T01:00:04.294967296+01:00[Europe/Amsterdam]h": 24, "1970-01-01T01:22:55.705032704+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:22:55.705032704+01:23[+01:23]h": 24, "1970-01-01T01:22:55.705032705+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:22:55.705032705+01:23[+01:23]h": 24, "1970-01-01T01:22:57.852516351+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:22:57.852516351+01:23[+01:23]h": 24, "1970-01-01T01:22:57.852516352+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:22:57.852516352+01:23[+01:23]h": 24, "1970-01-01T01:22:59.999999999+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:22:59.999999999+01:23[+01:23]h": 24, "1970-01-01T01:23:00+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:23:00+01:23[+01:23]h": 24, "1970-01-01T01:23:00.000000001+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:23:00.000000001+01:23[+01:23]h": 24, "1970-01-01T01:23:02.147483647+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:23:02.147483647+01:23[+01:23]h": 24, "1970-01-01T01:23:02.147483648+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:23:02.147483648+01:23[+01:23]h": 24, "1970-01-01T01:23:04.294967295+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:23:04.294967295+01:23[+01:23]h": 24, "1970-01-01T01:23:04.294967296+01:23[+01:23]": "1970-01-01T00:00:00+01:23[+01:23]", + "1970-01-01T01:23:04.294967296+01:23[+01:23]h": 24, "1970-04-14T17:25:59.378197781-12:34[-12:34]": "1970-04-14T00:00:00-12:34[-12:34]", + "1970-04-14T17:25:59.378197781-12:34[-12:34]h": 24, "1970-04-14T21:59:59.378197781-08:00[America/Vancouver]": "1970-04-14T00:00:00-08:00[America/Vancouver]", + "1970-04-14T21:59:59.378197781-08:00[America/Vancouver]h": 24, "1970-04-15T05:59:59.378197781+00:00[UTC]": "1970-04-15T00:00:00+00:00[UTC]", + "1970-04-15T05:59:59.378197781+00:00[UTC]h": 24, "1970-04-15T06:59:59.378197781+01:00[Europe/Amsterdam]": "1970-04-15T00:00:00+01:00[Europe/Amsterdam]", + "1970-04-15T06:59:59.378197781+01:00[Europe/Amsterdam]h": 24, "1970-04-15T07:22:59.378197781+01:23[+01:23]": "1970-04-15T00:00:00+01:23[+01:23]", + "1970-04-15T07:22:59.378197781+01:23[+01:23]h": 24, "1987-12-31T23:59:59.999999999+00:00[Antarctica/Troll]": "1987-12-31T00:00:00+00:00[Antarctica/Troll]", + "1987-12-31T23:59:59.999999999+00:00[Antarctica/Troll]h": 24, "1988-01-01T00:00:00+08:00[Antarctica/Casey]": "1988-01-01T00:00:00+08:00[Antarctica/Casey]", + "1988-01-01T00:00:00+08:00[Antarctica/Casey]h": 24, "2001-09-08T13:12:40-12:34[-12:34]": "2001-09-08T00:00:00-12:34[-12:34]", + "2001-09-08T13:12:40-12:34[-12:34]h": 24, "2001-09-08T18:46:40-07:00[America/Vancouver]": "2001-09-08T00:00:00-07:00[America/Vancouver]", + "2001-09-08T18:46:40-07:00[America/Vancouver]h": 24, "2001-09-09T01:46:40+00:00[UTC]": "2001-09-09T00:00:00+00:00[UTC]", + "2001-09-09T01:46:40+00:00[UTC]h": 24, "2001-09-09T03:09:40+01:23[+01:23]": "2001-09-09T00:00:00+01:23[+01:23]", + "2001-09-09T03:09:40+01:23[+01:23]h": 24, "2001-09-09T03:46:40+02:00[Europe/Amsterdam]": "2001-09-09T00:00:00+02:00[Europe/Amsterdam]", + "2001-09-09T03:46:40+02:00[Europe/Amsterdam]h": 24, "2011-12-29T23:59:59.999999999-10:00[Pacific/Apia]": "2011-12-29T00:00:00-10:00[Pacific/Apia]", + "2011-12-29T23:59:59.999999999-10:00[Pacific/Apia]h": 24, "2011-12-31T00:00:00+14:00[Pacific/Apia]": "2011-12-31T00:00:00+14:00[Pacific/Apia]", + "2011-12-31T00:00:00+14:00[Pacific/Apia]h": 24, "2018-02-17T23:00:00-03:00[America/Sao_Paulo]": "2018-02-17T00:00:00-02:00[America/Sao_Paulo]", + "2018-02-17T23:00:00-03:00[America/Sao_Paulo]h": 25, "2018-02-17T23:00:00.000000001-03:00[America/Sao_Paulo]": "2018-02-17T00:00:00-02:00[America/Sao_Paulo]", + "2018-02-17T23:00:00.000000001-03:00[America/Sao_Paulo]h": 25, "2018-02-17T23:59:59.999999999-02:00[America/Sao_Paulo]": "2018-02-17T00:00:00-02:00[America/Sao_Paulo]", + "2018-02-17T23:59:59.999999999-02:00[America/Sao_Paulo]h": 25, "2018-11-03T23:59:59.999999999-03:00[America/Sao_Paulo]": "2018-11-03T00:00:00-03:00[America/Sao_Paulo]", + "2018-11-03T23:59:59.999999999-03:00[America/Sao_Paulo]h": 24, "2018-11-04T01:00:00-02:00[America/Sao_Paulo]": "2018-11-04T01:00:00-02:00[America/Sao_Paulo]", + "2018-11-04T01:00:00-02:00[America/Sao_Paulo]h": 23, "2018-11-04T01:00:00.000000001-02:00[America/Sao_Paulo]": "2018-11-04T01:00:00-02:00[America/Sao_Paulo]", + "2018-11-04T01:00:00.000000001-02:00[America/Sao_Paulo]h": 23, "2023-06-17T09:47:33.648291574+05:45[Asia/Kathmandu]": "2023-06-17T00:00:00+05:45[Asia/Kathmandu]", + "2023-06-17T09:47:33.648291574+05:45[Asia/Kathmandu]h": 24, "2023-07-15T15:16:23.011704192-11:00[Pacific/Niue]": "2023-07-15T00:00:00-11:00[Pacific/Niue]", + "2023-07-15T15:16:23.011704192-11:00[Pacific/Niue]h": 24, "2023-07-16T16:23:11.704192385+14:00[Pacific/Kiritimati]": "2023-07-16T00:00:00+14:00[Pacific/Kiritimati]", + "2023-07-16T16:23:11.704192385+14:00[Pacific/Kiritimati]h": 24, "2024-02-29T01:36:49.862917305+01:00[Africa/Algiers]": "2024-02-29T00:00:00+01:00[Africa/Algiers]", + "2024-02-29T01:36:49.862917305+01:00[Africa/Algiers]h": 24, "2024-10-27T02:00:00+01:00[Europe/Amsterdam]": "2024-10-27T00:00:00+02:00[Europe/Amsterdam]", + "2024-10-27T02:00:00+01:00[Europe/Amsterdam]h": 25, "2024-10-27T02:00:00.000000001+01:00[Europe/Amsterdam]": "2024-10-27T00:00:00+02:00[Europe/Amsterdam]", + "2024-10-27T02:00:00.000000001+01:00[Europe/Amsterdam]h": 25, "2024-10-27T02:59:59.999999999+02:00[Europe/Amsterdam]": "2024-10-27T00:00:00+02:00[Europe/Amsterdam]", + "2024-10-27T02:59:59.999999999+02:00[Europe/Amsterdam]h": 25, "2024-11-03T01:00:00-08:00[America/Vancouver]": "2024-11-03T00:00:00-07:00[America/Vancouver]", + "2024-11-03T01:00:00-08:00[America/Vancouver]h": 25, "2024-11-03T01:00:00.000000001-08:00[America/Vancouver]": "2024-11-03T00:00:00-07:00[America/Vancouver]", + "2024-11-03T01:00:00.000000001-08:00[America/Vancouver]h": 25, "2024-11-03T01:59:59.999999999-07:00[America/Vancouver]": "2024-11-03T00:00:00-07:00[America/Vancouver]", + "2024-11-03T01:59:59.999999999-07:00[America/Vancouver]h": 25, "2025-03-09T01:59:59.999999999-08:00[America/Vancouver]": "2025-03-09T00:00:00-08:00[America/Vancouver]", + "2025-03-09T01:59:59.999999999-08:00[America/Vancouver]h": 23, "2025-03-09T03:00:00-07:00[America/Vancouver]": "2025-03-09T00:00:00-08:00[America/Vancouver]", + "2025-03-09T03:00:00-07:00[America/Vancouver]h": 23, "2025-03-09T03:00:00.000000001-07:00[America/Vancouver]": "2025-03-09T00:00:00-08:00[America/Vancouver]", + "2025-03-09T03:00:00.000000001-07:00[America/Vancouver]h": 23, "2025-03-30T01:59:59.999999999+01:00[Europe/Amsterdam]": "2025-03-30T00:00:00+01:00[Europe/Amsterdam]", + "2025-03-30T01:59:59.999999999+01:00[Europe/Amsterdam]h": 23, "2025-03-30T03:00:00+02:00[Europe/Amsterdam]": "2025-03-30T00:00:00+01:00[Europe/Amsterdam]", + "2025-03-30T03:00:00+02:00[Europe/Amsterdam]h": 23, "2025-03-30T03:00:00.000000001+02:00[Europe/Amsterdam]": "2025-03-30T00:00:00+01:00[Europe/Amsterdam]", + "2025-03-30T03:00:00.000000001+02:00[Europe/Amsterdam]h": 23, "2038-01-18T14:40:08-12:34[-12:34]": "2038-01-18T00:00:00-12:34[-12:34]", + "2038-01-18T14:40:08-12:34[-12:34]h": 24, "2038-01-18T14:40:09-12:34[-12:34]": "2038-01-18T00:00:00-12:34[-12:34]", + "2038-01-18T14:40:09-12:34[-12:34]h": 24, "2038-01-18T19:14:08-08:00[America/Vancouver]": "2038-01-18T00:00:00-08:00[America/Vancouver]", + "2038-01-18T19:14:08-08:00[America/Vancouver]h": 24, "2038-01-18T19:14:09-08:00[America/Vancouver]": "2038-01-18T00:00:00-08:00[America/Vancouver]", + "2038-01-18T19:14:09-08:00[America/Vancouver]h": 24, "2038-01-19T03:14:08+00:00[UTC]": "2038-01-19T00:00:00+00:00[UTC]", + "2038-01-19T03:14:08+00:00[UTC]h": 24, "2038-01-19T03:14:09+00:00[UTC]": "2038-01-19T00:00:00+00:00[UTC]", + "2038-01-19T03:14:09+00:00[UTC]h": 24, "2038-01-19T04:14:08+01:00[Europe/Amsterdam]": "2038-01-19T00:00:00+01:00[Europe/Amsterdam]", + "2038-01-19T04:14:08+01:00[Europe/Amsterdam]h": 24, "2038-01-19T04:14:09+01:00[Europe/Amsterdam]": "2038-01-19T00:00:00+01:00[Europe/Amsterdam]", + "2038-01-19T04:14:09+01:00[Europe/Amsterdam]h": 24, "2038-01-19T04:37:08+01:23[+01:23]": "2038-01-19T00:00:00+01:23[+01:23]", + "2038-01-19T04:37:08+01:23[+01:23]h": 24, "2038-01-19T04:37:09+01:23[+01:23]": "2038-01-19T00:00:00+01:23[+01:23]", + "2038-01-19T04:37:09+01:23[+01:23]h": 24, "2262-04-11T11:13:16.854775807-12:34[-12:34]": "2262-04-11T00:00:00-12:34[-12:34]", "2262-04-11T11:13:16.854775808-12:34[-12:34]": "2262-04-11T00:00:00-12:34[-12:34]", "2262-04-11T16:47:16.854775807-07:00[America/Vancouver]": "2262-04-11T00:00:00-07:00[America/Vancouver]", From 3dd094c5b6d3efbfff84e157ca523b492cc9c4fa Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 7 May 2026 17:47:35 -0700 Subject: [PATCH 2/4] Polyfill: Cap day span at <100% of day length in ZonedDateTime.round() The assertion in the previous code was a problem in cases where a backwards UTC shift spanned across midnight (i.e. from a time after midnight to a time before midnight, not starting or ending at midnight.) It would produce a span of startNs...thisNs that was longer than 100% of the day length, which failed the assertion, but would also mess up rounding modes (would expand to 200% of the day length, for example.) See: https://github.com/tc39/proposal-temporal/issues/3312 --- polyfill/lib/zoneddatetime.mjs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/polyfill/lib/zoneddatetime.mjs b/polyfill/lib/zoneddatetime.mjs index 7b82870c0..dda981717 100644 --- a/polyfill/lib/zoneddatetime.mjs +++ b/polyfill/lib/zoneddatetime.mjs @@ -287,7 +287,7 @@ export class ZonedDateTime { // first, round the underlying DateTime fields const timeZone = GetSlot(this, TIME_ZONE); - const thisNs = GetSlot(this, EPOCHNANOSECONDS); + let thisNs = GetSlot(this, EPOCHNANOSECONDS); const iso = dateTime(this); let epochNanoseconds; @@ -301,7 +301,13 @@ export class ZonedDateTime { assert(thisNs.geq(startNs), 'cannot produce an instant during a day that occurs before start-of-day instant'); const endNs = ES.GetStartOfDay(timeZone, dateEnd); - assert(thisNs.lt(endNs), 'cannot produce an instant during a day that occurs on or after end-of-day instant'); + // Handle the case where a transition starts after midnight and falls back + // to before midnight, and pieces of two calendar days are interleaved. + // endNs is the start of the first piece of the second calendar day, so if + // thisNs is inside the second piece of the first calendar day, it can be + // outside of the box defined by start-of-day and end-of-day. + // https://github.com/tc39/proposal-temporal/issues/3312 + if (thisNs.geq(endNs)) thisNs = endNs.minus(bigInt.one); const dayLengthNs = endNs.subtract(startNs); const dayProgressNs = TimeDuration.fromEpochNsDiff(thisNs, startNs); From 31a367ba7c9d31b2dd0643d20e28fa2b41f9ecd4 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 21 May 2026 11:27:19 -0700 Subject: [PATCH 3/4] Polyfill: Mimic observable reads in DateTimeFormat constructor Recently new test262 tests were added that test the order of observable reads in the DateTimeFormat constructor more strictly. Property reads should be interleaved with the observable calls to toString/valueOf. Implement this in our polyfilled DateTimeFormat constructor so that it adheres to the spec and passes the test262 tests. --- polyfill/lib/intl.mjs | 79 +++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/polyfill/lib/intl.mjs b/polyfill/lib/intl.mjs index 0193050fa..a4bb2177c 100644 --- a/polyfill/lib/intl.mjs +++ b/polyfill/lib/intl.mjs @@ -8,6 +8,7 @@ import { TypeError as TypeErrorCtor, // class static functions and methods + ArrayPrototypeIncludes, ArrayPrototypeSlice, IntlDateTimeFormat, IntlDateTimeFormatPrototypeGetFormat, @@ -22,6 +23,7 @@ import { IntlNumberFormat, IntlNumberFormatPrototypeGetFormat, MathCeil, + NumberIsFinite, NumberIsNaN, ObjectAssign, ObjectCreate, @@ -131,37 +133,62 @@ function adjustRenderedWeekday(polyfilledFormatter, realFormatter, originalEpoch function internalCreateDateTimeFormat(dtf, locale, options, required) { const hasOptions = typeof options !== 'undefined'; if (hasOptions) { - // Read all the options in the expected order and copy them to a - // null-prototype object with which we can do further operations - // unobservably + // Read all the options in the expected order, perform expected conversions + // and validations, and copy them to a null-prototype object with which we + // can do further operations unobservably const props = [ - 'localeMatcher', - 'calendar', - 'numberingSystem', - 'hour12', - 'hourCycle', - 'timeZone', - 'weekday', - 'era', - 'year', - 'month', - 'day', - 'dayPeriod', - 'hour', - 'minute', - 'second', - 'fractionalSecondDigits', - 'timeZoneName', - 'formatMatcher', - 'dateStyle', - 'timeStyle' + ['localeMatcher', 'string', ['lookup', 'best fit']], + ['calendar', 'string', null], + ['numberingSystem', 'string', null], + ['hour12', 'boolean'], + ['hourCycle', 'string', ['h11', 'h12', 'h23', 'h24']], + ['timeZone', 'string', null], + ['weekday', 'string', ['narrow', 'short', 'long']], + ['era', 'string', ['narrow', 'short', 'long']], + ['year', 'string', ['2-digit', 'numeric']], + ['month', 'string', ['2-digit', 'numeric', 'narrow', 'short', 'long']], + ['day', 'string', ['2-digit', 'numeric']], + ['dayPeriod', 'string', ['narrow', 'short', 'long']], + ['hour', 'string', ['2-digit', 'numeric']], + ['minute', 'string', ['2-digit', 'numeric']], + ['second', 'string', ['2-digit', 'numeric']], + ['fractionalSecondDigits', 'number', 1, 3], + ['timeZoneName', 'string', ['short', 'long', 'shortOffset', 'longOffset', 'shortGeneric', 'longGeneric']], + ['formatMatcher', 'string', ['basic', 'best fit']], + ['dateStyle', 'string', ['full', 'long', 'medium', 'short']], + ['timeStyle', 'string', ['full', 'long', 'medium', 'short']] ]; options = ES.ToObject(options); const newOptions = ObjectCreate(null); for (let i = 0; i < props.length; i++) { - const prop = props[i]; - if (ES.HasOwnProperty(options, prop)) { - newOptions[prop] = options[prop]; + const entry = props[i]; + const prop = entry[0]; + let value = options[prop]; + if (value !== undefined) { + const conversionHint = entry[1]; + switch (conversionHint) { + case 'string': + { + value = ES.ToString(value); + const allowed = entry[2]; + if (allowed !== null && !ES.Call(ArrayPrototypeIncludes, allowed, [value])) { + throw new RangeError(`Value ${value} out of range for Intl.DateTimeFormat options property ${prop}`); + } + } + break; + case 'number': + { + value = ES.ToNumber(value); + const minimum = entry[2]; + const maximum = entry[3]; + if (!NumberIsFinite(value) || value < minimum || value > maximum) { + throw new RangeError(`${prop} value is out of range`); + } + } + break; + case 'boolean': // nothing observable, handle in real DTF constructor + } + newOptions[prop] = value; } } options = newOptions; From 81a688632bc0bfb0c9d478294eeb2fe385872364 Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Thu, 21 May 2026 10:54:56 -0700 Subject: [PATCH 4/4] Update test262 --- polyfill/test262 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polyfill/test262 b/polyfill/test262 index d0c1b4555..227d90551 160000 --- a/polyfill/test262 +++ b/polyfill/test262 @@ -1 +1 @@ -Subproject commit d0c1b4555b03dd404873fd6422a4b5da00136500 +Subproject commit 227d905513f790dec90858d04ddf8cf81326706f