Skip to content

[fields] the readonly datetime WIDGET faces are the same omission one type over — a bare toLocaleDateString + toLocaleTimeString pair, not formatDateTime #8209

Description

@os-justin

Found while implementing objectui#8194 (converging the four readonly date widget faces onto formatDate). Render-measured on origin/main 158d75bb3. Filing unassigned — recording, not claiming.

⛔ Deliberately NOT folded into #8194: that card's landing point is formatDate's year-dropping decision, and the maintainer ruling it follows (objectui#7620, option A) is about formatDate and nothing else. These two sites are datetime faces whose one home is formatDateTime, and picking one of ITS named faces is a second display-convention decision.

The claim

#8194's population is defined by OMISSION — a date rendered with no options bag — so it was re-enumerated by mechanism rather than by spelling. Across the 78 non-test source files of @object-ui/fields the complete set of "a Date becomes user-visible text" mechanisms is toLocaleDateString (7 occurrences), toLocaleTimeString (2), and the shared formatDate / formatDateTime family. There is no Intl.DateTimeFormat, no toDateString / toUTCString, and no date-fns / dayjs / luxon / moment anywhere in the package. (toLocaleString, 4 occurrences, is number formatting.)

That enumeration returns six bare no-bag sites, not the four #8194 names. The two extra ones are these:

site spelling
packages/fields/src/widgets/DateTimeField.tsx (readonly branch) date.toLocaleDateString(locale) and date.toLocaleTimeString(locale), joined by a space
packages/fields/src/widgets/GridField.tsx (temporalText, the datetime branch) the same pair, same join

Neither ever calls formatDateTime, which since objectui#7443 is the declared ONE home for the datetime display convention.

Measured, five locales, origin/main, TZ=UTC

Value 2026-07-04T07:00:00.000Z, rendered in the readonly DateTimeField and in the sub-grid datetime cell (both sites produce the identical string):

locale these two sites render formatDateTime 'compact' (what the datetime CELL renders)
en 7/4/2026 7:00:00 AM 7/4/2026 7:00 am
de 4.7.2026 07:00:00 4.7.2026 7:00 am
zh 2026/7/4 07:00:00 2026/7/4 上午7:00
ja 2026/7/4 7:00:00 2026/7/4 午前7:00
ar 4/7/2026 7:00:00 ص 4/7/2026 7:00 ص

(The ar forms carry U+200F marks around the date separators; they are omitted here so the table stays readable.)

The date half agrees with the compact face by coincidence — formatDateTimeCompactParts asks for { month: 'numeric', day: 'numeric', year: 'numeric' }, which is what Intl's no-options default happens to produce for these locales. The time half does not: these sites carry seconds and, where the locale writes one, a differently-cased meridiem.

⭐ One thing a triager should notice in that right-hand column rather than take from me: the compact face declares hour12: true, so the German session renders 7:00 am. date-display.ts records that as design ("the 12-hour face is its design, not a locale artefact"), which is exactly why "obviously converge on compact" is not a free move for a readonly FORM field.

Why it is worth a card

Same mechanism as objectui#7443, objectui#7620, objectui#8194 and objectui#4576 — more than one option bag for one concept (here: one bag and one absence of a bag), kept in step by nothing. #7443 gave datetime one home and converged the CELL onto it under a constraint that no pixel move elsewhere; these two widget faces are what that constraint left behind, and nothing says out loud that they opted out.

What the answer is NOT, yet

Converging them requires choosing WHICH formatDateTime face, and that is the decision:

Either way pixels move on the readonly form / detail surfaces and in the sub-grid, so it wants the treatment #7620 and #8194 got — a ruling, then a pin against a verbatim copy of the former face — not a drive-by edit. ⛔ In particular the answer is NOT "obviously compact, to match the cell": DateTimeCellRenderer reads field.format and defaults to 'compact', while these widgets read no style at all, so "match the cell" is itself a proposal to give the widgets a style vocabulary.

Grading

Observation-class. Every face is locale-correct, no data is lost or misread. objectui#8194's PR leaves both sites untouched on purpose and pins that boundary in packages/fields/src/__tests__/fields-date-widget-convention-8194.test.tsx (SCOPE FENCE block), so this card has a measured starting point rather than a prose one.

Related: objectui#8194, objectui#7443, objectui#7620, objectui#4576.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seati18npackage: fieldspriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions