Skip to content

fix: localization dates not being localized correctly#91569

Open
TaduJR wants to merge 22 commits into
Expensify:mainfrom
TaduJR:fix-localization-dates-not-being-localized-correctly
Open

fix: localization dates not being localized correctly#91569
TaduJR wants to merge 22 commits into
Expensify:mainfrom
TaduJR:fix-localization-dates-not-being-localized-correctly

Conversation

@TaduJR

@TaduJR TaduJR commented May 25, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

  • Display dates use Intl.DateTimeFormat (dateStyle / timeStyle) across CalendarPicker, DateIcon, DOB step, receipts, travel, search, per-diem, status, schedule-call, and chat travel notifications.
  • getWeekStartsOn / getWeekEndsOn are locale-aware via Intl.Locale.getWeekInfo(); calendar shows the correct first day per locale.
  • DOB field placeholder + selected value both localized (en → MM/DD/YYYY, de → DD.MM.YYYY, ja → YYYY/MM/DD).
  • Month names render capitalized across locales (es → Enero, fr → Janvier, de → Januar).
  • CalendarPicker weekday headers use 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:

  • Strict Locale end-to-endIntlStore eager-seeds EN at module load so getCurrentLocale(): Locale (never undefined); cascades through useLocalize().preferredLocale to every formatter signature. No ?? CONST.LOCALES.DEFAULT fallbacks left.
  • LocaleContextProvider reads IntlStore via useSyncExternalStore — one re-render per locale change instead of the prior useState + useEffect-on-Onyx-flag two-tick dance.
  • useNow hook (new) — AutoUpdateTime and ParticipantLocalTime previously ran their own per-second setIntervals; both now share one ref-counted minute-tick via useSyncExternalStore.

Fixed Issues

$ #69562
PROPOSAL: #69562 (comment)

Tests

Setup

A. Language

  • Settings → Profile → Preferences → Language. Switch between English and Español between full passes.

B. Device timezone (OS-level — outside the app)

Set the device's timezone in the OS settings before each pass:

OS How
iOS Settings → General → Date & Time → turn Set Automatically off → tap Time Zone → search "Los Angeles" or "Tokyo"
Android Settings → System → Date & time → turn off automatic → set timezone manually
macOS System Settings → General → Date & Time → turn off "Set time zone automatically" → set city
Windows Settings → Time & language → Date & time → turn off "Set time zone automatically" → set zone

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

  • Automatic: ON → in-app timezone mirrors the device's OS timezone.
  • Automatic: OFF → you pick a timezone manually from the list (e.g. 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.

  1. Look at the grey placeholder inside the empty field.
  2. Tap to open the calendar. Pick January 5, 2026 (or any past date).
  3. Tap Save.
  4. Reopen the row and read the saved value.

Expected:

  • English: placeholder MM/DD/YYYY, saved value 01/05/2026.
  • Spanish: placeholder DD/MM/AAAA (or DD/MM/YYYY), saved value 05/01/2026.
  • Placeholder format and saved value format match (both day-first or both month-first).
  • Result is identical across all device timezones (UTC−8 and UTC+9 should both show the same value).

Test 2 — Calendar weekday headers and month names

Where: Any date picker.

  1. Open the calendar.
  2. Look at the row of weekday letters above the day grid.
  3. Tap the month name at the top → look at the month list.

Expected:

  • English: weekday letters S M T W T F S or M T W T F S S; months read "January…".
  • Spanish: localized weekday initials; months read "Enero, Febrero, Marzo…" with capitalized first letter.
  • No empty cells or two columns showing the same letter ambiguously.

Test 3 — Calendar first day of week

  1. Open any date picker.
  2. Look at the leftmost weekday column header.

Expected:

  • English: Monday is leftmost.
  • Spanish: Monday is leftmost.
  • If a Japanese build is available: Sunday is leftmost.

Test 4 — Travel reservations (Hotel / Flight / Car / Train)

Where: A chat with a travel booking → trip → individual reservation.

  1. Open a Hotel reservation. Check Check-in and Check-out rows.
  2. Open a Flight reservation. Check Departs and Arrives rows.
  3. Open a Car reservation. Check Pick-up, Drop-off, Cancellation lines.
  4. Open a Train reservation. Check dates + route description.

Expected:

  • Dates and times in the active language.
  • Date and time on the same line describe the same moment — never off-by-one-day between them. Re-verify on UTC+9 (Tokyo device): a flight stored as e.g. 2026-04-02T19:00-05:00 must 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.

  1. Find a chat with a travel preview card.
  2. Look at the date range shown.

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.).

  1. Find any travel notification (grey/italic system message).
  2. Look at the embedded date and time.

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.

  1. Look at the date printed on the receipt page.

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.

  1. Create a per-diem with same start and end date (today 2pm–6pm).
  2. Look at the "Time" row.
  3. Repeat with different start and end dates (today 9am to tomorrow 5pm).

Expected:

  • Same-day case: 9:00 AM – 5:00 PM, Jan 5, 2026.
  • Multi-day case: 9:00 AM, Jan 5, 2026 – 5:00 PM, Jan 6, 2026.
  • Active language reflected.
  • On UTC+9 device: same per-diem still shows the time you picked, in the same date you picked it on.

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)

  1. Set a status. Set Clear after → Custom, pick tomorrow at 5:00 PM. Save.
  2. Read the "Clear after" row.

9b. Device TZ = UTC−8, In-app TZ = Manual → Asia/Tokyo (UTC+9)

  1. Settings → Profile → Timezone → toggle Automatic OFF → pick Asia/Tokyo. Save and go back.
  2. Settings → Profile → Status → Clear after → Custom. Pick tomorrow at 5:00 PM. Save.
  3. Read the "Clear after" row.

Expected (both sub-passes):

  • The displayed time and date match what you picked in the picker, interpreted in the in-app timezone you have selected (not the device timezone).
  • Open a chat with another person and verify the small status hint on the chat list row reads consistently.

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

  1. Note the displayed time.
  2. Wait ~1–2 minutes without interacting.

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/Tokyo

  1. Settings → Profile → Timezone → turn Automatic OFF → pick Asia/Tokyo. Save.
  2. Return to your profile → Local time row.

Expected: 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 in Asia/Tokyo. Both timezones set via Settings → Profile → Timezone → Manual in each account.

  1. Open the 1:1 chat.
  2. Look at the chat header / participants section for "X's local time is …".
  3. Wait ~1–2 minutes.

Expected:

  • The displayed time reflects the other person's in-app timezone (e.g. Tokyo wall-clock).
  • If the two timezones land you on different weekdays, the hint includes the participant's day name (e.g. "9:00 AM Tue").
  • Time format follows your own active language.
  • The time auto-updates at the minute boundary.

Test 12 — Search results date column

Where: Search → any search with dated results.

  1. Observe the date column on each row.

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.

  1. Open any monthly statement. Observe page title / month label.

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.

  1. Look at Start date and End date rows.
  2. Tap either row → opens the date editor.

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.

  1. Look at the "Frozen on …" subtitle/banner.

Expected: Frozen date follows the active language.


Test 16 — Schedule a call

Where: Any chat with the Schedule a call action.

  1. Start scheduling. Pick a date in the calendar.
  2. Continue to the confirmation page. Observe the date/time line.

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.

  1. Find a full-day OOO summary ("X is out for 2 days, back on Mon, Jan 12").
  2. Find a partial-day OOO message ("X is out 9:00 AM – 5:00 PM on Mon, Jan 5, 2026").

Expected: Both in active language, consistent date/time formatting.


Test 18 — Split expense date column

Where: An expense that has been split.

  1. Open the expense → tap Split. Observe each row's date.

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)

  1. In a workspace chat, create two expenses.
  2. Open the expense report → select both via checkboxes.
  3. Tap dropdown → Merge.
  4. Pick a field from either expense → Next.

Expected: Merge confirmation page opens normally. No crash, no white screen.


R2 — Merge expense: opening merged expense does not crash (PR #69945)

  1. Complete the merge from R1.
  2. Open the resulting merged expense from the report.

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.

  1. Settings → Profile → Personal Details → Date of birth.
  2. Pick January 15, 2000Save.
  3. Reopen and read.

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.

  1. Open any date picker.
  2. Note today's day-name.
  3. Tap today (highlighted cell). Tap Save. Reopen.

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.

  1. Workspaces → (any workspace) → Workflows → Add bank account → Connect manually.
  2. Step through to Personal info → Date of birth.
  3. Look at the placeholder before tapping.

Expected: Placeholder reads MM/DD/YYYY. Never YYYY-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).

  1. Create a split expense.
  2. Tap the split in chat to open it.

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.

  1. Settings → Profile → Timezone: keep Automatic ON (so in-app TZ = device TZ for this part).
  2. Settings → Profile → Status → Clear after → Custom.
  3. Pick January 25, 2026 at a specific time → Save.
  4. Read the "Clear after" row.

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:

  1. Settings → Profile → Personal Details → Date of birth.
  2. Pick March 10, 1995 → Save → reopen.

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:

  1. Workspaces → (any with company cards) → Company cards.
  2. Tap an unassigned card → Assign to a user.
  3. In the wizard, pick Transaction start date = February 1, 2026.
  4. Continue, then return and re-read.

Expected (both passes): Saved date is Feb 1, 2026.

  • Verify that no errors appear in the JS console

Offline tests

Same as tests

QA Steps

// TODO: These must be filled out, or the issue title must include "[No QA]."
Same as tests

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
    • MacOS: Desktop
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I verified there are no new alerts related to the canBeMissing param for useOnyx
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari
Mac-Chrome.mp4

@TaduJR TaduJR requested review from a team as code owners May 25, 2026 06:40
@melvin-bot melvin-bot Bot requested a review from situchan May 25, 2026 06:40
@melvin-bot

melvin-bot Bot commented May 25, 2026

Copy link
Copy Markdown

Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers!

@melvin-bot melvin-bot Bot requested review from heyjennahay and removed request for a team May 25, 2026 06:40
@melvin-bot

melvin-bot Bot commented May 25, 2026

Copy link
Copy Markdown

@situchan Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@melvin-bot melvin-bot Bot removed the request for review from a team May 25, 2026 06:40
Comment thread src/hooks/useNow.ts Outdated
@codecov

codecov Bot commented May 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ Changes either increased or maintained existing code coverage, great job!

Files with missing lines Coverage Δ
src/CONST/index.ts 93.84% <ø> (ø)
src/components/DateIcon.tsx 100.00% <100.00%> (ø)
...s/DatePicker/CalendarPicker/generateMonthMatrix.ts 100.00% <100.00%> (ø)
src/components/DatePicker/CalendarPicker/index.tsx 96.59% <100.00%> (+0.02%) ⬆️
src/components/DatePicker/index.tsx 56.04% <100.00%> (+0.48%) ⬆️
src/components/LocaleContextProvider.tsx 89.79% <100.00%> (+0.32%) ⬆️
...ponents/ReportActionItem/ChronosOOOListActions.tsx 43.75% <100.00%> (ø)
...rc/components/ReportActionItem/TripRoomPreview.tsx 56.75% <100.00%> (ø)
...components/Search/SearchList/ListItem/DateCell.tsx 100.00% <100.00%> (ø)
src/components/SubStepForms/DateOfBirthStep.tsx 0.00% <ø> (ø)
... and 35 more
... and 29 files with indirect coverage changes

…ation-dates-not-being-localized-correctly

# Conflicts:
#	src/pages/Travel/FlightTripDetails.tsx
@MelvinBot

Copy link
Copy Markdown
Contributor

PR Review

Well-executed migration from date-fns format strings to Intl.DateTimeFormat. The IntlStore / useSyncExternalStore architecture is clean, the useNow hook is correctly implemented, and locale threading is consistent across callers. A few items worth addressing:

Issues to fix

1. Mock type mismatch — src/languages/__mocks__/IntlStore.ts:7
The mock declares currentLocale: Locale | undefined while production now uses Locale (non-optional). This means getCurrentLocale() in the mock has an inferred return type of Locale | undefined, which doesn't match production. Tests could pass while incorrectly handling undefined. Change to Locale (no | undefined).

2. getStablePerDiemMerchantDateRange byte-stability concern — src/libs/DateUtils.ts
The comment says output "must stay byte-stable across user-locale changes." The locale is correctly pinned, but switching from format(date, 'MMM d, yyyy') (deterministic template) to Intl.DateTimeFormat('en', {dateStyle: 'medium'}) means output now depends on the platform's ICU version. iOS, Android, and web can produce subtly different strings (e.g., "Jun 5, 2025" vs "Jun. 5, 2025"). If this string is matched/compared on the backend, that's a problem.

3. datetimeToRelative still produces English-only output — src/libs/DateUtils.ts:256-260
This function accepts a locale parameter but never passes a date-fns locale to formatDistance. Relative strings like "12 minutes ago" are always English regardless of the user's locale. Pre-existing issue, but a notable omission given the PR's goal.

Suggestions (non-blocking)

4. SHORT_DATE vs SHORT_DATE_PADDED separator consistency
getLocalizedDatePlaceholder derives field ORDER from SHORT_DATE (dateStyle: 'short') but the actual display uses SHORT_DATE_PADDED ({year:'numeric', month:'2-digit', day:'2-digit'}). These can theoretically produce different separators in some locales (e.g., / vs .). Worth cross-locale testing.

5. CalendarPicker missing useMemo
monthNames, daysOfWeekLong, daysOfWeek at src/components/DatePicker/CalendarPicker/index.tsx are recalculated every render. Wrapping in useMemo with preferredLocale as dependency would avoid unnecessary array allocations.

6. getWeekStartsOn tests are ICU-dependent
The parameterized tests for ja and pt-BR expect Sunday-start via Intl.Locale.getWeekInfo(). If CI has small-icu Node builds, these fall back to Monday and fail.

What's done well

  • UTC anchoring via toUTCDate/toLocalDate prevents the classic date-only-string timezone shift bug
  • NNBSP stripping regex is precise — only targets space before AM/PM, preserving French/Slavic typography
  • useNow hook is textbook useSyncExternalStore: stable function references, ref-counted subscriptions, monotonic minute indices, correct cleanup
  • IntlStore race guard (monotonic load token) correctly handles rapid locale switches
  • zh-hans narrow weekday test guards against CJK narrow-name collision — excellent edge case coverage
  • Type narrowing from Locale | undefinedLocale throughout is safe now that IntlStore pre-seeds EN

@situchan

Copy link
Copy Markdown
Contributor

@TaduJR you're missing native platform tests.

Please test various timezones (US timezone UTC-X, Asia timezone UTC+X)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/components/LocaleContextProvider.tsx Outdated
@TaduJR

TaduJR commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

#91569 (comment)

Address all the valid ones on latest commit

@TaduJR

TaduJR commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

ℹ️ 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".

@TaduJR

TaduJR commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/components/DatePicker/CalendarPicker/generateMonthMatrix.ts
@TaduJR

TaduJR commented May 30, 2026

Copy link
Copy Markdown
Contributor Author

@TaduJR you're missing native platform tests.

Added for Android Native now. I wanted to get some feedbacks before.

@situchan

Do you think we should test on adhoc build first and ask QA to go through the tests cause I don't have Travel transactions.

@heyjennahay heyjennahay left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Product review not required

…ation-dates-not-being-localized-correctly

# Conflicts:
#	tests/unit/ViolationUtilsTest.ts
@TaduJR

TaduJR commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

ℹ️ 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".

TaduJR added 2 commits June 10, 2026 09:21
…ation-dates-not-being-localized-correctly

# Conflicts:
#	src/pages/workspace/distanceRates/PolicyDistanceRateDetailsPage.tsx
@TaduJR

TaduJR commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ 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/DatePicker/index.tsx
#	src/libs/ReportActionsUtils.ts
@TaduJR

TaduJR commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ 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".

@MelvinBot

Copy link
Copy Markdown
Contributor

Regression review

Bottom 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 safe

Timezone formatting — no double-conversion (I checked this carefully because it looks wrong at a glance). datetimeToCalendarTime feeds a toZonedTime-shifted Date into formatIntl(...) with no timeZone option. That is intentionally correct: toZonedTime pre-shifts the epoch so the runtime-local fields read as the selected-TZ wall clock, and Intl.format with no timeZone reads in runtime-local — exactly what the old date-fns format(date, …) did. Old and new produce identical output. The same pattern holds for formatToLocalTime / formatToLongDateWithWeekday (incl. the Chronos OOO path). A real double-conversion would require passing the zoned date and a timeZone option — that doesn't happen here.

DOB / date-only values — no day-shift. formatToLocalizedShortDate routes through toUTCDate + a UTC-pinned formatter, and the picker stores the raw yyyy-MM-dd string unmodified — symmetric, so picked == saved (the #76646 / R7-B fix is intact).

Calendar grid math. getWeekEndsOn is derived as (getWeekStartsOn + 6) % 7, and the day-grid + weekday headers both consume the same getWeekStartsOn, so a tapped cell always saves the day its header shows (the #69945 / R4 deploy-blocker). Intl.Locale.getWeekInfo() is guarded by try/catch with a Monday fallback — no throw risk on engines that lack it.

useNow / locale store. The shared per-minute tick is ref-counted correctly (interval cleared on last unsubscribe, restarted on remount) and useSyncExternalStore returns a stable snapshot — no leak, no infinite re-render. IntlStore eager-seeding EN + useSyncExternalStore produces one re-render per locale switch with no missed update.

Worth a manual confirmation (not regressions)

  • Native week-start is not locale-aware. Hermes ships Intl.Locale without getWeekInfo(), so on iOS/Android all non-en locales fall back to Monday-start. Headers and grid stay consistent (no day-shift), but the "locale-aware first day of week" claim only holds on web. This matches main's prior hardcoded behavior, so it's a feature-gap, not a regression — just calling it out since Test 3 implies native coverage.
  • Custom Status "Clear after" text changed format (e.g. 2023-05-16 11:10 PMMay 16, 2023, 11:10 PM). This is the intended localization change (Test 9), just confirm the new copy is expected.
Scope & method

Reviewed via four parallel deep-dives over the post-change files at 01192f6: DateUtils.ts (+400/-175), IntlStore.ts + LocaleContextProvider.tsx, useNow.ts + AutoUpdateTime + ParticipantLocalTime, and the DatePicker/CalendarPicker cluster. I did not exhaustively review all 60 files — lower-risk display-only call sites (receipts, travel detail views, search cells, wallet) were spot-checked but rely on the same verified formatter helpers. The "double-conversion" hypothesis was investigated and rejected with old-vs-new code comparison, so reviewers shouldn't re-raise it.


Next Steps: Reply with @MelvinBot <feedback> to dig into a specific area, or have QA run the R1–R7 cases (especially R7 on a UTC+9 device) for runtime confirmation.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants