fix: localization dates not being localized correctly#91569
Conversation
|
Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers! |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
…ation-dates-not-being-localized-correctly # Conflicts: # src/pages/Travel/FlightTripDetails.tsx
PR ReviewWell-executed migration from date-fns format strings to Issues to fix1. Mock type mismatch — 2. 3. Suggestions (non-blocking)4. 5. CalendarPicker missing 6. What's done well
|
|
@TaduJR you're missing native platform tests. Please test various timezones (US timezone UTC-X, Asia timezone UTC+X) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e56f6b81c3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…ation-dates-not-being-localized-correctly # Conflicts: # src/components/FrozenCardHeader.tsx # src/pages/Travel/TrainTripDetails.tsx
|
Address all the valid ones on latest commit |
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep them coming! ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 97e34fc0da
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
heyjennahay
left a comment
There was a problem hiding this comment.
Product review not required
…ation-dates-not-being-localized-correctly # Conflicts: # tests/unit/ViolationUtilsTest.ts
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
…ation-dates-not-being-localized-correctly # Conflicts: # src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx
|
@codex review |
|
Codex Review: Didn't find any major issues. 🎉 ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
…ation-dates-not-being-localized-correctly # Conflicts: # src/components/DatePicker/index.tsx # src/libs/ReportActionsUtils.ts
|
@codex review |
|
Codex Review: Didn't find any major issues. Breezy! ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Regression reviewBottom line: I reviewed the four highest-risk areas of this PR in depth and found no correctness regressions. CI is fully green. Below is what I verified, plus a couple of items worth a manual confirmation before merge. I focused where this change has historically broken (the reverted R3/R4/R7 cases): timezone day-shift, date-picker grid math, the locale store, and the new shared timer. Verified safeTimezone formatting — no double-conversion (I checked this carefully because it looks wrong at a glance). DOB / date-only values — no day-shift. Calendar grid math.
Worth a manual confirmation (not regressions)
Scope & methodReviewed via four parallel deep-dives over the post-change files at Next Steps: Reply with |
Explanation of Change
Intl.DateTimeFormat(dateStyle/timeStyle) across CalendarPicker, DateIcon, DOB step, receipts, travel, search, per-diem, status, schedule-call, and chat travel notifications.getWeekStartsOn/getWeekEndsOnare locale-aware viaIntl.Locale.getWeekInfo(); calendar shows the correct first day per locale.MM/DD/YYYY, de →DD.MM.YYYY, ja →YYYY/MM/DD).Enero, fr →Janvier, de →Januar).weekday: 'short'(fr →lun. mar. mer.) — narrow labels were ambiguous in fr/it/pt-BR.formatTravelDate(chat travel notifications) now respects locale; per-diem same-day "Time" row keeps the date.Architectural improvements:
Localeend-to-end —IntlStoreeager-seeds EN at module load sogetCurrentLocale(): Locale(neverundefined); cascades throughuseLocalize().preferredLocaleto every formatter signature. No?? CONST.LOCALES.DEFAULTfallbacks left.LocaleContextProviderreadsIntlStoreviauseSyncExternalStore— one re-render per locale change instead of the prioruseState+useEffect-on-Onyx-flag two-tick dance.useNowhook (new) —AutoUpdateTimeandParticipantLocalTimepreviously ran their own per-secondsetIntervals; both now share one ref-counted minute-tick viauseSyncExternalStore.Fixed Issues
$ #69562
PROPOSAL: #69562 (comment)
Tests
Setup
A. Language
B. Device timezone (OS-level — outside the app)
Set the device's timezone in the OS settings before each pass:
After changing OS timezone, fully close and reopen the App so it picks up the new zone.
C. In-app timezone (separate from device timezone)
In-app timezone is configured at: Settings → Profile → Timezone
America/Los_Angeles,Asia/Tokyo,Europe/Berlin).Tests 10, 11, 9, and R7-A explicitly require manipulating the in-app timezone.
Test 1 — Date Picker placeholder + selected value
Where: Settings → Profile → Personal Details → Date of birth.
Expected:
MM/DD/YYYY, saved value01/05/2026.DD/MM/AAAA(orDD/MM/YYYY), saved value05/01/2026.Test 2 — Calendar weekday headers and month names
Where: Any date picker.
Expected:
S M T W T F SorM T W T F S S; months read "January…".Test 3 — Calendar first day of week
Expected:
Test 4 — Travel reservations (Hotel / Flight / Car / Train)
Where: A chat with a travel booking → trip → individual reservation.
Expected:
2026-04-02T19:00-05:00must show consistent date+time, not "Apr 3 at 9:00 AM" vs "Apr 2 at 9:00 AM" split across the two halves of the row.Test 5 — Travel chat preview card
Where: Chat list showing a travel-room conversation.
Expected: Range uses the active language's month abbreviations. Single-day trips show one date.
Test 6 — Travel update messages in chat
Where: A travel chat with system notifications (ticket issued, flight changed, cancelled, etc.).
Expected: Both render in active language and describe the same moment regardless of device timezone.
Test 7 — Receipt full view (EReceipt)
Where: A transaction with a receipt → tap thumbnail → view receipt.
Expected: Date in active language (English: "January 5, 2026"; Spanish: "5 de enero de 2026"). Date does not shift across UTC−8 vs UTC+9.
Test 8 — Per-diem expense Time row
Where: Workspace with per-diem enabled.
Expected:
9:00 AM – 5:00 PM, Jan 5, 2026.9:00 AM, Jan 5, 2026 – 5:00 PM, Jan 6, 2026.Test 9 — Custom status (Clear after) — in-app timezone sensitive
Where: Settings → Profile → Status.
Run in two sub-passes:
9a. Device TZ = UTC−8, In-app TZ = Automatic (so in-app = UTC−8)
9b. Device TZ = UTC−8, In-app TZ = Manual →
Asia/Tokyo(UTC+9)Expected (both sub-passes):
Test 10 — Profile "Local time" row — in-app timezone sensitive
Where: Open your own profile (avatar → details) → find Local time row.
Run in two sub-passes:
10a. In-app Timezone = Automatic
Expected: Time matches the device clock. Auto-updates within ~60 seconds (e.g. "2:34 PM" → "2:35 PM"). 12-hour AM/PM in English, 24-hour in Spanish.
10b. In-app Timezone = Manual
Asia/TokyoExpected: Time now shows Tokyo wall-clock time (≈17 hours ahead of US Pacific), formatted in the active language. The displayed timezone abbreviation (e.g.
JST) reflects Tokyo. Auto-update at the minute boundary still works.Test 11 — Participant local time (chat header) — in-app timezone sensitive
Where: A 1:1 chat with a participant whose in-app timezone differs from yours.
Setup: You should be in (say)
America/Los_Angeles. The other participant should be inAsia/Tokyo. Both timezones set via Settings → Profile → Timezone → Manual in each account.Expected:
Test 12 — Search results date column
Where: Search → any search with dated results.
Expected: Dates use active language's month abbreviations. Past-year rows include year, current-year typically omit it. No row shows the wrong calendar day after changing device timezone.
Test 13 — Wallet statements
Where: Settings → Wallet → View statement → pick a month.
Expected: Month name fully localized.
Test 14 — Workspace distance rates start/end date
Where: Workspace with Date-bound mileage rate beta enabled. Workspace → Distance rates → tap a rate.
Expected: Rows in active language. Editor calendar shows correct localized placeholder. Saving a date and re-opening shows the same date even after switching device timezone.
Test 15 — Frozen card header
Where: Settings → Wallet → Expensify Card → A frozen card.
Expected: Frozen date follows the active language.
Test 16 — Schedule a call
Where: Any chat with the Schedule a call action.
Expected: Calendar and confirmation line both in active language. Selected date+time consistent across device timezones.
Test 17 — Out-of-office (Chronos) summary
Where: A chat with Chronos / OOO bot messages.
Expected: Both in active language, consistent date/time formatting.
Test 18 — Split expense date column
Where: An expense that has been split.
Expected: Dates in active language. No day-shift across device timezones.
Regression Test Cases (must pass before merge)
Each below reproduces a real bug from prior reverted attempts. They must all pass.
R1 — Merge expense: no crash on confirmation page (#80013)
Expected: Merge confirmation page opens normally. No crash, no white screen.
R2 — Merge expense: opening merged expense does not crash (PR #69945)
Expected: Detail page opens. Date row renders. No crash.
R3 — Date picker: picked date matches saved date (#76646)
Run on iOS App, Windows Chrome, and macOS Chrome/Safari.
Expected: Saved date is exactly Jan 15, 2000. Not Jan 14, not Jan 16.
Run this also under UTC+9 device timezone. The result must still be Jan 15.
R4 — Date picker: same-day select on Monday-start calendar (PR #69945 deploy-blocker)
Active language: English or Spanish.
Expected: Saved date is today. Every column in the grid saves the date its header letter says (M-cell → Monday, T-cell → Tuesday, etc.). Repeat for several weekdays.
R5 — Bank account DOB placeholder shows locale format (#80011)
Active language: English, Windows Chrome.
Expected: Placeholder reads
MM/DD/YYYY. NeverYYYY-MM-DD. Switch to Spanish and re-check.R6 — Split expense: opening a split does not crash on Android (#76643)
Run on Android (original repro: Redmi Note 10s, Android 13).
Expected: Detail page opens. Dates render in active language. No crash.
R7 — No day-shift across the app (#80024) — timezone-critical
Setup: Device language English. Run all three parts twice: once with device timezone US Pacific (UTC−08:00), then again with device timezone Asia/Tokyo (UTC+09:00). Test on iOS App and Windows Chrome.
R7-A — Status:
This also tests in-app timezone interaction.
Expected (both Pacific and Tokyo passes): Shows the date and time you picked. Never Jan 24 in one pass and Jan 25 in the other.
Then repeat R7-A with in-app Timezone set manually to
Europe/Berlin(UTC+01:00) — the saved value should now reflect Berlin wall-clock, not device wall-clock.R7-B — Date of birth:
Expected (both Pacific and Tokyo passes): Shows March 10, 1995 (or
03/10/1995). Not March 9, not March 11.R7-C — Company cards transaction start date:
Expected (both passes): Saved date is Feb 1, 2026.
Offline tests
Same as tests
QA Steps
// TODO: These must be filled out, or the issue title must include "[No QA]."
Same as tests
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectioncanBeMissingparam foruseOnyxtoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Mac-Chrome.mp4