Found while implementing #7178 (routing a date-valued dataset measure through the list-cell date path). Source-confirmed on main at 6411def25. Filing unassigned — recording, not claiming.
The claim
For the date field type there is one display convention. For datetime there are two, and the cell renderer uses the one that is not the module's own function.
date cells → DateCellRenderer → formatDate(value, field.format || 'relative', { dueLike, locale, t }). One path, shared, and field.format is read as a display style.
datetime cells → DateTimeCellRenderer → does not call formatDateTime. It inlines its own pair:
date.toLocaleDateString(locale, { month: 'numeric', day: 'numeric', year: 'numeric' })
date.toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit', hour12: true }).toLowerCase()
Meanwhile formatDateTime — exported from the same barrel, right beside formatDate — renders:
date.toLocaleDateString(locale, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' })
Measured for 2024-07-04T07:00:00.000Z in en-US:
| path |
renders |
DateTimeCellRenderer (the datetime cell) |
7/4/2024 7:00 am |
formatDateTime (the module's datetime function) |
Jul 4, 2024, 07:00 AM |
Both are locale-correct. They are simply two conventions for one field type. DateTimeCellRenderer also never reads field.format, so the style vocabulary a date field gets is unavailable to a datetime field.
Why this is worth a card
It is the same shape as objectui#4576, which is the precedent this repo already paid for once: sharing the scaling of percent was not enough while the convention lived in two places, and a German session read 1.234,5 % in a list cell beside 1.234,5% in a dashboard measure. Here the split is between two cells of adjacent field types rather than between a cell and a measure, but the mechanism is identical — two Intl option bags for one concept, kept in step by nothing.
There is a third spelling in the same family: formatCellValue in packages/components/src/renderers/complex/data-table.tsx sniffs ISO strings and builds its own Intl.DateTimeFormat with { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' } — close to formatDateTime but independently authored.
Consequence already visible
#7442 (the #7178 fix) routes a datetime-shaped measure through formatDateTime, following ObjectGantt.tsx's sniffed-ISO dispatch, which is the repo's existing answer for an ISO string arriving with no field type attached. That is the best available target, but it means a datetime measure on a dashboard tile and a datetime cell in a list render the same instant in two different conventions. The ruling for #7178 asked for "the same path a list cell uses for that field"; for date that is exact, and for datetime it cannot currently be exact, because the datetime cell does not use the shared function.
Suggested direction
Not obvious, and deliberately not chosen here — the compact 7/4/2024 7:00 am face may well be the intended design for a dense grid cell rather than an accident (the code says as much about hour12). Two candidate shapes:
DateTimeCellRenderer calls formatDateTime and the compact face becomes a named style on it, the way 'short' is a named style on formatDate. One convention, one home, and field.format becomes readable for datetime too.
- The compact face is declared to be the datetime cell convention and
formatDateTime is documented as the non-cell one, so the divergence is intentional and written down rather than silent.
Either closes it; leaving it undocumented is the part that reads as a latent #4576.
Grading
Observation-class. Nothing is wrong on screen today — both faces are locale-correct and legible — and no data is lost or misread. The reusable half is the undocumented second convention, not a user-facing defect.
Note that packages/plugin-list/src/ListView.tsx and packages/components/src/renderers/complex/data-table.tsx were held by other in-flight PRs when this was filed.
Found while implementing #7178 (routing a date-valued dataset measure through the list-cell date path). Source-confirmed on
mainat6411def25. Filing unassigned — recording, not claiming.The claim
For the
datefield type there is one display convention. Fordatetimethere are two, and the cell renderer uses the one that is not the module's own function.datecells →DateCellRenderer→formatDate(value, field.format || 'relative', { dueLike, locale, t }). One path, shared, andfield.formatis read as a display style.datetimecells →DateTimeCellRenderer→ does not callformatDateTime. It inlines its own pair:Meanwhile
formatDateTime— exported from the same barrel, right besideformatDate— renders:Measured for
2024-07-04T07:00:00.000Zinen-US:DateTimeCellRenderer(thedatetimecell)7/4/2024 7:00 amformatDateTime(the module's datetime function)Jul 4, 2024, 07:00 AMBoth are locale-correct. They are simply two conventions for one field type.
DateTimeCellRendereralso never readsfield.format, so the style vocabulary adatefield gets is unavailable to adatetimefield.Why this is worth a card
It is the same shape as objectui#4576, which is the precedent this repo already paid for once: sharing the scaling of percent was not enough while the convention lived in two places, and a German session read
1.234,5 %in a list cell beside1.234,5%in a dashboard measure. Here the split is between two cells of adjacent field types rather than between a cell and a measure, but the mechanism is identical — twoIntloption bags for one concept, kept in step by nothing.There is a third spelling in the same family:
formatCellValueinpackages/components/src/renderers/complex/data-table.tsxsniffs ISO strings and builds its ownIntl.DateTimeFormatwith{ year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }— close toformatDateTimebut independently authored.Consequence already visible
#7442 (the #7178 fix) routes a datetime-shaped measure through
formatDateTime, followingObjectGantt.tsx's sniffed-ISO dispatch, which is the repo's existing answer for an ISO string arriving with no field type attached. That is the best available target, but it means a datetime measure on a dashboard tile and a datetime cell in a list render the same instant in two different conventions. The ruling for #7178 asked for "the same path a list cell uses for that field"; fordatethat is exact, and fordatetimeit cannot currently be exact, because thedatetimecell does not use the shared function.Suggested direction
Not obvious, and deliberately not chosen here — the compact
7/4/2024 7:00 amface may well be the intended design for a dense grid cell rather than an accident (the code says as much abouthour12). Two candidate shapes:DateTimeCellRenderercallsformatDateTimeand the compact face becomes a named style on it, the way'short'is a named style onformatDate. One convention, one home, andfield.formatbecomes readable fordatetimetoo.formatDateTimeis documented as the non-cell one, so the divergence is intentional and written down rather than silent.Either closes it; leaving it undocumented is the part that reads as a latent #4576.
Grading
Observation-class. Nothing is wrong on screen today — both faces are locale-correct and legible — and no data is lost or misread. The reusable half is the undocumented second convention, not a user-facing defect.
Note that
packages/plugin-list/src/ListView.tsxandpackages/components/src/renderers/complex/data-table.tsxwere held by other in-flight PRs when this was filed.