Repository navigation
feat(Timestamp, PowerSearch, Schedule): write dates in the provider's calendar - #9639
nynexman4464 wants to merge 10 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
There was a problem hiding this comment.
Approval waits on an engineering owner's decision. Built-in date formats that follow the provider's calendar (Timestamp's date formats, PowerSearch values, Schedule labels and the new calendar helper arguments) rest only on the draft spec:AST-111 FR18, while the current internationalization architecture (INV10) and Timestamp FR2 still pin Gregorian. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so if they are an engineering owner. Whatever the decision, these need fixing:
- Relative Timestamps now fail hydration when the server lacks
Temporalbut the browser has it (Timestamp.tsx:367, Timestamp FR3); keep the pre-hydration placeholder Gregorian. - Schedule's
isSameCalendarMonthworks out calendar months a second way; re-export Core'scalendarMonthOfthrough the internal entry and call it. - The title says charts follow the provider calendar, and the body says Astryx call sites pass the calendar to
formatSharedDate; neither is true yet, so narrow the wording.
[Automated review]
| isClockPending | ||
| ? {timeZone: 'UTC', isTimezoneShown: true} | ||
| : {isTimezoneShown}, | ||
| ? {timeZone: 'UTC', isTimezoneShown: true, calendar} |
There was a problem hiding this comment.
With a calendar that needs Temporal, the server and the browser can resolve different calendars here. On a server without Temporal (Node 24) the placeholder is Gregorian; a browser with native Temporal writes it in Persian, and React reports "Hydration failed because the server rendered text didn't match the client". Timestamp FR3 says the server markup hydrates without a mismatch. The client replaces this placeholder after hydration, so keeping it Gregorian ({timeZone: 'UTC', isTimezoneShown: true}) avoids the mismatch.
|
|
||
| // A Gregorian month or year can span two months or years of another calendar, | ||
| // so ranges compare the calendar's own fields. | ||
| function isSameCalendarMonth( |
There was a problem hiding this comment.
Core's calendarMonthOf (packages/core/src/utils/calendarSystem.ts) already decides which calendar month a Gregorian day falls in, and Calendar uses it. This string comparison is a second path, and the two already disagree: under japanese, Gregorian January 1989 is one month to Core but two here ("January 64 Shōwa - January 1 Heisei"). Re-export calendarMonthOf from @astryxdesign/core/internal/calendar-system and compare calendarMonthOf(calendar, a).start for both dates.
| calendar: 'gregory', | ||
| }), | ||
| ); | ||
| /** One date formatter per locale and calendar. */ |
There was a problem hiding this comment.
Non-blocking: this comment restates byLocaleAndCalendar. Delete it.
e5a8d85 to
accd0ed
Compare
There was a problem hiding this comment.
Approval waits on an engineering owner's decision. Built-in date formats that follow the provider's calendar (Timestamp's date formats, its card and accessible name, PowerSearch values, Schedule labels, and the new calendar arguments on formatSharedDate and formatFilterValue) rest only on the draft spec:AST-111 FR18, while the current internationalization architecture (INV10) and Timestamp FR2 still pin Gregorian. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so if they are an engineering owner. Whatever the decision, these need fixing:
- Relative Timestamps fail hydration when the server lacks
Temporalbut the browser has it (Timestamp.tsx:367, Timestamp FR3), so keep the pre-hydration placeholder Gregorian and state the both-sides-Temporalcondition next to FR3's zone gap. - Under
chineseanddangi, Timestamp, PowerSearch and Schedule write dates with rawIntlwhileCalendaruses Core'sformatCalendarDate, so Gregorian 2027-02-06 is day 1 of First Month inCalendarbut the 30th of Twelfth Month beside it; route these labels, and Schedule'sisSameCalendarMonth, through Core's calendar helpers. InternationalizationProvider's docs and record at this head still say the provider calendar reaches onlyCalendar, so update them to name Timestamp, PowerSearch and Schedule.- The title (charts), the body ("Astryx call sites pass the resolved calendar", "always agree") and the changeset's opening sentence claim more than the change does, so narrow them to the components that pass the calendar.
[Automated review]
| isClockPending | ||
| ? {timeZone: 'UTC', isTimezoneShown: true} | ||
| : {isTimezoneShown}, | ||
| ? {timeZone: 'UTC', isTimezoneShown: true, calendar} |
There was a problem hiding this comment.
With a calendar that needs Temporal, a server without it (Node 24) writes this placeholder in Gregorian and a browser with native Temporal writes it in that calendar, so React reports "Hydration failed because the server rendered text didn't match the client" (Timestamp FR3). The client replaces the placeholder after hydration, so keeping it Gregorian ({timeZone: 'UTC', isTimezoneShown: true}) removes the mismatch.
|
|
||
| // A Gregorian month or year can span two months or years of another calendar, | ||
| // so ranges compare the calendar's own fields. | ||
| function isSameCalendarMonth( |
There was a problem hiding this comment.
Core's calendarMonthOf and formatCalendarDate (packages/core/src/utils/calendarSystem.ts) already decide calendar months and write a day the way Calendar shows it. This string comparison and the raw-Intl labels are a second path that disagrees: under japanese, Gregorian January 1989 is one month to Calendar and two here, and under chinese, 2027-02-06 is day 1 of First Month in Calendar but day 30 here. Re-export those helpers from @astryxdesign/core/internal/calendar-system and call them.
| calendar: 'gregory', | ||
| }), | ||
| ); | ||
| /** One date formatter per locale and calendar. */ |
There was a problem hiding this comment.
Non-blocking: this comment restates byLocaleAndCalendar; delete it.
There was a problem hiding this comment.
Approval waits on an engineering owner's decision. Built-in date formats that follow the provider's calendar (Timestamp's date formats, its hover card and accessible name, PowerSearch values, Schedule labels, and the new calendar arguments on formatSharedDate and formatFilterValue) rest only on the draft spec:AST-111 FR18, which is not on main, while the current internationalization architecture (INV10) and Timestamp FR2 still pin the Gregorian calendar. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. Whatever the decision, these need fixing:
- A server without
Temporalfails React hydration in a browser that has it (Timestamp.tsx:367, Timestamp FR3), so keep the pre-hydration placeholder Gregorian and state in the rewritten record that date formats hydrate cleanly only when both sides haveTemporalor both lack it. - Under
chineseanddangi, Timestamp, PowerSearch and Schedule write dates with rawIntlwhileCalendaruses Core'sformatCalendarDate, so with nativeTemporalGregorian 2027-02-06 is day 1 of First Month inCalendarbut the 30th of Twelfth Month beside it; route these labels, and Schedule'sisSameCalendarMonth, through Core's calendar helpers. - The
InternationalizationProviderdocs and prop description still say the calendar is whatCalendarshows, so name Timestamp, PowerSearch and Schedule there too. - The title includes charts, which never read the provider, and the body says Astryx call sites pass the calendar and that a Timestamp and a Calendar always agree, which
formatSharedDate's callers and the Chinese case contradict, so narrow that wording.
[Automated review]
| isClockPending | ||
| ? {timeZone: 'UTC', isTimezoneShown: true} | ||
| : {isTimezoneShown}, | ||
| ? {timeZone: 'UTC', isTimezoneShown: true, calendar} |
There was a problem hiding this comment.
With a calendar that needs Temporal, a server without it (Node 24) writes this placeholder in Gregorian and a browser with native Temporal writes it in that calendar, so React reports "Hydration failed because the server rendered text didn't match the client" (Timestamp FR3). Keep the placeholder Gregorian; the client replaces it after hydration:
isClockPending
? {timeZone: 'UTC', isTimezoneShown: true}
: {isTimezoneShown, calendar},| timeZoneName: timeZoneNameStyle, | ||
| ...zone, | ||
| calendar: 'gregory', | ||
| calendar, |
There was a problem hiding this comment.
Under chinese and dangi, raw Intl and the runtime's Temporal disagree on some month starts, and Calendar names days through Core's formatCalendarDate (utils/calendarSystem.ts), which follows Temporal. With native Temporal, Gregorian 2027-02-06 is day 1 of First Month in Calendar but "Twelfth Month 30, 2026" here. Format the date part through the same helper so the two agree.
|
|
||
| // A Gregorian month or year can span two months or years of another calendar, | ||
| // so ranges compare the calendar's own fields. | ||
| function isSameCalendarMonth( |
There was a problem hiding this comment.
Core's calendarMonthOf (packages/core/src/utils/calendarSystem.ts) already decides which calendar month a Gregorian day falls in, and Calendar uses it. This string comparison is a second path that disagrees with it: under japanese, January 1989 is one month to Calendar and two here. Export calendarMonthOf and formatCalendarMonthLabel through @astryxdesign/core/internal/calendar-system and call them instead.
| type: "'gregory' | 'iso8601' | 'buddhist' | 'roc' | 'japanese' | 'persian' | 'indian' | 'hebrew' | 'chinese' | 'dangi' | 'coptic' | 'ethiopic' | 'ethioaa' | 'islamic-civil' | 'islamic-tbla' | 'islamic-umalqura'", | ||
| required: false, | ||
| description: | ||
| "Calendar system that `Calendar` in the subtree shows, such as 'persian', 'hebrew', or 'buddhist'. `DateInput`, `DateRangeInput`, and `DateTimeInput` show Gregorian dates. When omitted, a `-u-ca-` keyword in `locale` selects the calendar, else Gregorian; a locale's default calendar never applies. A component's own `calendar` prop takes precedence. Values stay Gregorian ISO dates. Calendars other than gregory, iso8601, buddhist, roc, and japanese need `Temporal`; where the runtime lacks it, load a polyfill such as `temporal-polyfill/full/global` on the server and in the browser, or those calendars show Gregorian dates and warn in development.", |
There was a problem hiding this comment.
With this pull request the provider calendar also rewrites Timestamp, PowerSearch and Schedule dates, but this description (and the prop JSDoc in InternationalizationProvider.tsx) still says it is the calendar that Calendar shows. Name those components here so builders know what setting calendar changes.
| calendar: 'gregory', | ||
| }), | ||
| ); | ||
| /** One date formatter per locale and calendar. */ |
There was a problem hiding this comment.
Non-blocking: this comment restates byLocaleAndCalendar; delete it.
c13e940 to
53da574
Compare
f6e7414 to
40aa346
Compare
There was a problem hiding this comment.
Approval waits on an engineering owner's decision. Built-in date formats that follow the provider's calendar (Timestamp's date formats, its hover card and accessible name, PowerSearch values, Schedule labels, the switch for existing -u-ca- locales, and the new calendar arguments on formatSharedDate and formatFilterValue) rest only on the draft spec:AST-111 FR18, which is not on main, while the current internationalization architecture (INV10) and Timestamp FR2 still pin the Gregorian calendar. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. Whatever the decision, these need fixing:
- A server without
Temporalfails React hydration in a browser that has it (Timestamp.tsx:367, Timestamp FR3), so keep the pre-hydration placeholder Gregorian and state in FR3 that date formats hydrate cleanly only when both sides haveTemporalor both lack it. - Under
chineseanddangi, Timestamp, PowerSearch and Schedule write dates with rawIntlwhileCalendaruses Core'sformatCalendarDate, so with nativeTemporalGregorian 2027-02-06 is day 1 of First Month inCalendarbut the 30th of Twelfth Month beside it; route these labels, and Schedule'sisSameCalendarMonth, through Core's calendar helpers. - The
InternationalizationProviderdocs and prop description still say the calendar is whatCalendarshows, so name Timestamp, PowerSearch and Schedule there too. - The title includes charts, whose formatters never read the provider, and the body says Astryx call sites pass the calendar to
formatSharedDateand that a Timestamp and a Calendar always agree, so narrow that wording.
[Automated review]
|
Heads-up: #9790 removed |
There was a problem hiding this comment.
Approval waits on an engineering owner's decision: built-in date formats that follow the provider's calendar (Timestamp's date formats, its hover card and accessible name, PowerSearch values, Schedule labels, the switch for existing -u-ca- locales, and the new calendar arguments on formatSharedDate and formatFilterValue), together with the new Timestamp FR2/FR3 text and the browser-support exceptions in the Timestamp and PowerSearch records, rest only on the draft spec:AST-111 FR18, which is not on main, while the current internationalization architecture (INV10) and Timestamp FR2 still pin the Gregorian calendar. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner. Whatever the decision, these need fixing:
- Under
chineseanddangi, Timestamp, PowerSearch and Schedule write dates with rawIntlwhileCalendaruses Core'sformatCalendarDate, so in Chromium with nativeTemporalGregorian 2027-02-06 is day 1 of First Month inCalendarbut the 30th of Twelfth Month beside it, and Schedule'sisSameCalendarMonthsplits Gregorian January 1989 underjapanesewhere Core'scalendarMonthOfdoes not; route these labels through Core's calendar helpers. - The
InternationalizationProviderdocs and prop description still say the calendar is whatCalendarshows, so name Timestamp, PowerSearch and Schedule there too. - The title includes charts, whose formatters never read the provider, and the body says Astryx call sites pass the calendar to
formatSharedDateand that a Timestamp and a Calendar always agree, so narrow that wording.
[Automated review]
| ...SHARED_DATE_FORMAT_OPTIONS.date_long, | ||
| ...zone, | ||
| calendar: 'gregory', | ||
| calendar, |
There was a problem hiding this comment.
Under chinese and dangi, raw Intl and the runtime's Temporal disagree on some month starts, and Calendar names days through Core's formatCalendarDate (utils/calendarSystem.ts), which follows Temporal. In Chromium 149 with native Temporal, Gregorian 2027-02-06 is day 1 of First Month in Calendar but "Twelfth Month 30, 2026" here (and in formatFilterValue.ts). Format the date part through the same helper so the two agree.
|
|
||
| // A Gregorian month or year can span two months or years of another calendar, | ||
| // so ranges compare the calendar's own fields. | ||
| function isSameCalendarMonth( |
There was a problem hiding this comment.
Core's calendarMonthOf (packages/core/src/utils/calendarSystem.ts) already decides which calendar month a Gregorian day falls in, and Calendar uses it. This string comparison is a second path that disagrees with it: under japanese, Gregorian January 1989 is one month to Calendar ("January 1 Heisei") and two here ("January 64 Shōwa - January 1 Heisei"). Export calendarMonthOf and formatCalendarMonthLabel through @astryxdesign/core/internal/calendar-system and call them here.
| calendar: 'gregory', | ||
| }), | ||
| ); | ||
| /** One date formatter per locale and calendar. */ |
There was a problem hiding this comment.
Non-blocking: this comment restates byLocaleAndCalendar; delete it.
…calendar Timestamp's date-bearing formats, its accessible name and hover card rows, and PowerSearch's date values, including the bottom-sheet presentation's token, Filters rows, and removal announcement, now use the calendar that the InternationalizationProvider resolves. The pure formatters take an explicit calendar that defaults to Gregorian. Time-only, relative, system_* and unix_seconds output does not change.
Lab components reach Core's calendar resolution through @astryxdesign/core/internal/calendar-system, so their date labels follow the same calendar as Core's date components.
Schedule's titles, day numbers, weekday and full-date labels, week ranges, and long event ranges use the calendar that Core's date components resolve. A Gregorian month that spans two calendar months is titled with both. Schedule still lays out and steps Gregorian days, weeks, and months.
The chart date formatters keep a Gregorian default and accept a calendar, so an application can write axis and tooltip dates in the calendar its date components use.
The Timestamp record states which formats follow the calendar that the provider resolves and which stay Gregorian or calendar-neutral. The Timestamp, PowerSearch, and Schedule docs say the same for builders, the API report records formatFilterValue's calendar argument, and a Changeset describes the change.
formatSharedDate keeps its Gregorian default and takes an optional trailing calendar, so a caller can write the shared date formats in the calendar its date components use. system_date stays the Gregorian ISO date.
A Calendar systems story shows Timestamp's date formats, a PowerSearch date value, and Calendar in the Persian calendar, in English and Farsi, with system_date_time staying Gregorian ISO.
…r calendars that need Temporal
…ep the hydration placeholder Gregorian Date-only Timestamp formats, PowerSearch's token value, and Schedule's labels name the month and day through Core's calendar helpers, so a Chinese or Korean month start matches Calendar, and Schedule compares calendar months with calendarMonthOf and names a month by its middle day. The clock placeholder is Gregorian, so a server without Temporal hydrates in a browser with it. The provider docs name the components that follow the calendar, and the records state that an explicit -u-ca- locale is the application's request.
53da574 to
fe3c951
Compare
48a3e9e to
6c500c9
Compare
There was a problem hiding this comment.
Approval waits on an engineering owner's decision: built-in date formats that follow the provider's calendar (Timestamp's date formats, its hover card and accessible name, PowerSearch values, Schedule labels, the switch for existing -u-ca- locales, and the new calendar arguments on formatSharedDate and formatFilterValue), together with the rewritten Timestamp FR2/FR3 and the new browser-support exceptions in the Timestamp and PowerSearch records, rest only on the draft spec:AST-111 FR18, which is not on main, while the current internationalization architecture (INV10) and Timestamp FR2 still pin the Gregorian calendar. An engineering owner records that decision by commenting /approve-spec <full head SHA> on this pull request; the author may do so as an engineering owner.
Whatever the decision, these need fixing:
- Timestamp's
date_timeand full date (formatInstant.ts:195,:230),formatDateAbsolute,formatSharedDate, Schedule's month title, week and event ranges, and the chart helpers pass the resolved calendar straight toIntlinstead of through Core's calendar labels, so undercalendar="iso8601"date_timereads "2026 Oct 30, 12:00 PM" in Chromium and loses its month in Node 24 whiledate_longandCalendarread "October 30, 2026", and Chinese and Korean dates informatSharedDateand Schedule's ranges can name another day thanCalendar; route them through one Core helper that applieslabelCalendarandTemporal's day, and addiso8601to the tests. - The title names charts, whose formatters never read the provider, and the body says Astryx call sites pass the calendar to
formatSharedDateand that a Timestamp and a Calendar always agree, so narrow that wording.
Two new comments run past the comment style's sentence limit; suggested rewrites are inline.
[Automated review]
| ...zoneName, | ||
| ...zone, | ||
| calendar: 'gregory', | ||
| calendar, |
There was a problem hiding this comment.
Under calendar="iso8601" this writes "2026 Oct 30, 12:00 PM" in Chromium 149 and "2026 30, 12:00 PM", with no month, in Node 24, while date_long above goes through formatInstantDate and Core's labelCalendar and reads "October 30, 2026". full at line 195 has the same problem, and it feeds the hover card and the relative accessible name. Route the date part through the shared calendar formatter (extended for a time of day), or at least apply labelCalendar, and add iso8601 to the calendar test matrix.
| return plainDateFormat(pd, SHARED_DATE_FORMAT_OPTIONS[format], locale); | ||
| return plainDateFormat( | ||
| pd, | ||
| {...SHARED_DATE_FORMAT_OPTIONS[format], calendar}, |
There was a problem hiding this comment.
formatSharedDate(day, 'date', 'en', 'iso8601') returns "2026 30" in Node 24, and under chinese this raw Intl path can name another day than Timestamp's date and Calendar, which go through formatCalendarDate. Use the same helper for non-Gregorian calendars.
| year: 'numeric', | ||
| timeZone: timezoneID, | ||
| calendar: 'gregory', | ||
| calendar, |
There was a problem hiding this comment.
Under iso8601 this range reads "2026 25–31" in Node 24, and the month title from LocalizedMonthTitle (line 170) reads "2026 October" in Chromium while the table name reads "October 2026". Under chinese the range can disagree with the day numbers, which now go through formatCalendarDate. Apply Core's label calendar here, in LocalizedMonthTitle and in formatEventDateTimeRange.
| // The pre-hydration placeholder is written in UTC, labelled, so | ||
| // the server and the hydrating client produce the same text in | ||
| // every time zone. | ||
| // The pre-hydration placeholder is Gregorian and in UTC, labelled, |
There was a problem hiding this comment.
Non-blocking (comment style): this is one 28-word sentence. Suggested: "The placeholder before hydration is Gregorian and in UTC, with its zone. The server and the client then write the same text, with or without Temporal."
| timeZone: timezoneID, | ||
| calendar: 'gregory', | ||
| }).format(new Date(plainDateToInstant(date, timezoneID, 12))); | ||
| // Core's helper names the month and day that its Calendar shows; raw Intl |
There was a problem hiding this comment.
Non-blocking (comment style): 26 words with two topics. Suggested: "Core's helper names the month and day that Calendar shows. Raw Intl can start a Chinese or Korean month on another day."
This implements
spec:AST-111(draft #9605) FR18.User need
An application that sets
calendar="persian"(orhebrew,buddhist, …) onInternationalizationProvidergets Persian dates inCalendar. But a Timestamp, a PowerSearch date token, a Schedule label, or a chart axis beside it still reads Gregorian. One screen then shows the same day in two calendars, so people misread dates.Problem and solution fit
date,date_long,date_weekdayanddate_timeformats, the hover card's full date, and the relative accessible name use the provider's resolved calendar.time, relative,system_*andunix_secondsdon't change.calendar:formatSharedDate,formatFilterValue, andshortDate/monthYearin@astryxdesign/chartsand LabChart. Astryx's own Timestamp, PowerSearch and Schedule calls pass the resolved calendar.@astryxdesign/core/internal/calendar-system. It applies the same rules and the same no-Temporalfallback asCalendar.Current specification
spec:AST-111FR18, together with FR2 and FR5.architecture:internationalizationINV8–INV10.spec:AST-017FR21 for the internal entry.spec:AST-013FR12: Timestamp and PowerSearch record the browser-support exception for calendars that needTemporal(AST-111 FR5), as Calendar and the date inputs do.Public delta and caller burden
'gregory':formatSharedDate(pd, format, locale?, calendar?),formatFilterValue(…, timezoneID?, calendar?),shortDate(value, locale, calendar?)andmonthYear(value, locale, calendar?).Design and alternatives
Formatting reads the same calendar each date component already resolves, including the fallback to Gregorian without
Temporal, so a Timestamp and a Calendar in one subtree show the same calendar. Underchineseanddangi, Timestamp formats that include a time, PowerSearch's date-and-time value and Schedule's week ranges still take the date fromIntl, so they can differ by a day whereIntlandTemporaldisagree; the Timestamp record lists this as an implementation gap.Rejected: each formatter picking up its locale's default calendar. FR2 excludes that.
Known limit: Schedule's month grid and paging stay Gregorian. Only its labels follow the calendar.
Evidence
Real Chromium against Storybook, in English and
fa-IR:Temporal: Timestamp, the PowerSearch token and Calendar all show Aban 1405, andsystem_date_timestays2026-10-30 12:00:00.Temporalremoved: Gregorian dates, plus the development warning.Temporalremoved, polyfill loaded: the same as native.Screenshots
calendar="persian", Englishcalendar="persian",fa-IRScope
Testing
pnpm exec vitest run --project uiover Timestamp, PowerSearch, Calendar, i18n, utils, internal, Lab Schedule, and Chart, and charts: 123 files and 2114 tests pass. DateInput and plainDate: 490 tests pass.persian,hebrewandbuddhist, each underenand the native locale;system_*andtimeunchanged;Temporalfallback, withbuddhiststill working;-u-ca-locale, and a barefa-IR.pnpm check:knowledgepasses.tsc --noEmitis clean for core, lab and charts, apart from pre-existingpseudo.jsonimport errors that also fail on the base.Writing rubric results
component:Timestamp:component:PowerSearch:Temporal).