Skip to content

Fix cancellation policy date timezone conversion for hotel and car trips#87755

Open
MelvinBot wants to merge 8 commits intomainfrom
claude-fixCancellationDateTimezone
Open

Fix cancellation policy date timezone conversion for hotel and car trips#87755
MelvinBot wants to merge 8 commits intomainfrom
claude-fixCancellationDateTimezone

Conversation

@MelvinBot
Copy link
Copy Markdown
Contributor

Explanation of Change

The cancellation deadline ISO string from Spotnana contains the venue's local date/time with a timezone offset (e.g., 2026-04-19T15:00:00+07:00). Previously, new Date() converted this to UTC internally, and format() from date-fns rendered it in the device's local timezone — shifting the displayed date/time away from what Spotnana shows (potentially crossing a day boundary).

This PR strips the timezone offset from the ISO string before parsing, so the date/time components are treated as-is (venue-local). This matches what Spotnana displays. The fix is applied to both hotel and car trip cancellation deadlines.

Fixed Issues

$ #87076
PROPOSAL: #87076 (comment)

Tests

  1. Book a hotel that has a free cancellation policy (in a timezone different from your device)
  2. Go to the hotel trip details in Expensify
  3. Verify the cancellation policy date/time matches what Spotnana shows
  4. Repeat for a car rental booking with a cancellation deadline
  • Verify that no errors appear in the JS console

Offline tests

N/A — cancellation deadline formatting is a display-only change using data already available locally.

QA Steps

  1. Book a hotel with a free cancellation policy where the hotel is in a different timezone than your device
  2. Open the hotel trip details in NewDot
  3. Compare the cancellation deadline date/time with what Spotnana shows
  4. Verify they match
  5. Repeat with a car rental booking that has a cancellation deadline
  • 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
  • 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 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

N/A — display-only formatting change, no UI layout changes

Android: mWeb Chrome

N/A — display-only formatting change, no UI layout changes

iOS: Native

N/A — display-only formatting change, no UI layout changes

iOS: mWeb Safari

N/A — display-only formatting change, no UI layout changes

MacOS: Chrome / Safari

N/A — display-only formatting change, no UI layout changes

…imezone

The cancellation deadline ISO string from Spotnana contains the venue's
local time with a timezone offset. Stripping the offset before parsing
ensures the date/time is displayed as-is (venue-local), matching Spotnana.
Applied to both hotel and car trip details.

Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 13, 2026

Codecov Report

❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.

Files with missing lines Coverage Δ
src/libs/DateUtils.ts 69.57% <100.00%> (+0.32%) ⬆️
src/pages/Travel/HotelTripDetails.tsx 96.42% <ø> (ø)
src/libs/TripReservationUtils.ts 80.00% <84.61%> (-0.52%) ⬇️
src/pages/Travel/CarTripDetails.tsx 0.00% <0.00%> (ø)
... and 201 files with indirect coverage changes

Comment thread src/libs/DateUtils.ts Outdated
Comment on lines +843 to +845
// Strip timezone offset (e.g., +07:00, -04:00, Z) to treat the date/time as venue-local
const naiveDateString = isoDateString.replace(/([+-]\d{2}:\d{2}|Z)$/, '');
const date = new Date(naiveDateString);
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.

@MelvinBot no, you should use method parseISO, according to #87076 (comment). Please update and add necessary unit tests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated to use parseISO from date-fns instead of new Date(), and added unit tests covering:

  • Positive timezone offset stripping (+07:00)
  • Negative timezone offset stripping (-05:00)
  • UTC indicator stripping (Z)
  • Non-current-year formatting (includes year)
  • ISO string without timezone offset

…e parsing

Replace new Date(naiveDateString) with parseISO(naiveDateString) in
getFormattedCancellationDate as recommended in review. Add unit tests
covering timezone offset stripping, current year vs other year formatting,
and ISO strings without timezone offsets.

Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
Comment thread src/libs/DateUtils.ts Outdated
Comment on lines +844 to +846
// Strip timezone offset (e.g., +07:00, -04:00, Z) to treat the date/time as venue-local
const naiveDateString = isoDateString.replace(/([+-]\d{2}:\d{2}|Z)$/, '');
const date = parseISO(naiveDateString);
Copy link
Copy Markdown
Contributor

@eh2077 eh2077 Apr 13, 2026

Choose a reason for hiding this comment

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

@MelvinBot Parse isoDateString directly with method parseISO

Suggested change
// Strip timezone offset (e.g., +07:00, -04:00, Z) to treat the date/time as venue-local
const naiveDateString = isoDateString.replace(/([+-]\d{2}:\d{2}|Z)$/, '');
const date = parseISO(naiveDateString);
const date = parseISO(isoDateString);

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.

@MelvinBot check the above comment!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — removed the timezone offset stripping and now parsing isoDateString directly with parseISO. Updated unit tests accordingly.

Parse isoDateString directly with parseISO as requested in review,
removing the manual timezone offset stripping. Updated unit tests
to reflect the new behavior.

Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
@MelvinBot
Copy link
Copy Markdown
Contributor Author

The only failing check is PR Reviewer Checklist / checklist, which is unrelated to the code changes — it tracks whether reviewers have completed their checklist. All code-related checks (ESLint, Prettier, builds, typecheck, etc.) are passing or still pending. No fix needed.

@eh2077
Copy link
Copy Markdown
Contributor

eh2077 commented Apr 14, 2026

@MelvinBot Mark it ready for review

@MelvinBot
Copy link
Copy Markdown
Contributor Author

🤖 I attempted to mark this PR as ready for review, but the command was not authorized in my current environment.

eh2077 — you can mark it ready yourself by clicking the "Ready for review" button on the PR page, or by running:

gh pr ready 87755 --repo Expensify/App

@eh2077
Copy link
Copy Markdown
Contributor

eh2077 commented Apr 14, 2026

@MelvinBot C+ doesn't have write access to mark PR ready for review. Can you try again?

@MelvinBot MelvinBot marked this pull request as ready for review April 14, 2026 14:25
@MelvinBot MelvinBot requested a review from a team as a code owner April 14, 2026 14:25
@MelvinBot
Copy link
Copy Markdown
Contributor Author

🤖 Done — PR is now marked as ready for review.

@melvin-bot melvin-bot Bot requested review from eh2077 and trjExpensify and removed request for a team April 14, 2026 14:25
@melvin-bot
Copy link
Copy Markdown

melvin-bot Bot commented Apr 14, 2026

@eh2077 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]

Copy link
Copy Markdown

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

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: 00171f9115

ℹ️ 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/libs/DateUtils.ts Outdated
Comment on lines 842 to 844
const date = parseISO(isoDateString);
if (isThisYear(date)) {
return format(date, 'EEEE, MMM d h:mm a');
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve venue-local cancellation time when parsing deadlines

Parsing cancellationDeadline with parseISO() still applies the embedded offset and then format() renders in the device timezone, so users outside the venue timezone will continue to see shifted times (and potentially the wrong day) for cancellation cutoffs. For example, 2026-04-19T15:00:00+07:00 will display as early morning in US timezones instead of 3:00 PM, which defeats the intended Spotnana-aligned behavior and can mislead users about the true cancellation window.

Useful? React with 👍 / 👎.

@eh2077
Copy link
Copy Markdown
Contributor

eh2077 commented Apr 14, 2026

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible 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 checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (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
    • 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 verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • 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
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately and each prop has a /** comment above it */
    • The file is named correctly
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • For Class Components, any internal methods passed to components event handlers are bound to this properly so there are no scoping issues (i.e. for onClick={this.submit} the method this.submit should be bound to this in the constructor)
    • Any internal methods bound to this are necessary to be bound (i.e. avoid this.submit = this.submit.bind(this); if this.submit is never passed to a component event handler like onClick)
    • All JSX used for rendering exists in the render method
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • 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 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.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this 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.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
Android: mWeb Chrome
iOS: HybridApp
iOS: mWeb Safari
MacOS: Chrome / Safari

@eh2077
Copy link
Copy Markdown
Contributor

eh2077 commented Apr 14, 2026

@MelvinBot I think we should use a different approach.

  1. use the duration before arrival date time, eg PT2D, define prop in TripData.ts
            /** Deadline in duration before the check-in date time. */
            durationBeforeArrivalDeadline: {
                /** ISO 8601 format. */
                iso8601: string;
            };
  1. in src/libs/TripReservationUtils.ts, make method parseDurationToSeconds support parse PT2D
  2. in src/libs/TripReservationUtils.ts, let
    cancellationDeadline = pnrData.checkInDateTime?.iso8601 subtract durationBeforeArrivalDeadline

Can you try this?

- Add durationBeforeArrivalDeadline prop to HotelPnr cancellationPolicy type
- Update parseDurationToSeconds to support day durations (e.g., P2D, P2DT3H)
- Compute hotel cancellationDeadline by subtracting duration from checkInDateTime
- Fall back to existing deadline.iso8601 if no duration is available

Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
@MelvinBot MelvinBot requested a review from a team as a code owner April 14, 2026 17:12
@melvin-bot melvin-bot Bot requested review from marcochavezf and removed request for a team April 14, 2026 17:12
@melvin-bot
Copy link
Copy Markdown

melvin-bot Bot commented Apr 14, 2026

@marcochavezf 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]

@MelvinBot
Copy link
Copy Markdown
Contributor Author

Done — updated to use the duration-based approach:

  1. Added durationBeforeArrivalDeadline (optional) to HotelPnr.room.cancellationPolicy in TripData.ts
  2. Updated parseDurationToSeconds to support day durations (P2D, P2DT3H, etc.)
  3. Added getCancellationDeadline helper in TripReservationUtils.ts that computes checkInDateTime - durationBeforeArrivalDeadline (falls back to deadline.iso8601 if no duration is available)

Copy link
Copy Markdown
Contributor

@trjExpensify trjExpensify left a comment

Choose a reason for hiding this comment

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

@stitesExpensify do you have eyes on this? A couple of thoughts:

  • It sounds like the cancellation date and time in Spotnana is the Hotel's local timezone, but it doesn't actually show that. Spotnana would do better here to include the timezone for clarity. Maybe we can suggest it?
Image
  • I agree the date/time we add to the "Cancellation policy" row in the product here should be accurate:
Image
  1. show it "converted" to the local time of the user
  2. or show it in the "venue" time but include the timezone in the string so it's clear

As Spotnana doesn't include the timezone on their side, our "conversion" to local does make it a bit confusing - appearing as though there's a mismatch. So maybe the second option is the one to go for, for now?

@MelvinBot
Copy link
Copy Markdown
Contributor Author

The build (Debug, true) failure is unrelated to this PR — it's a transient GitHub Actions infrastructure error. The runner failed to download the aws-actions/configure-aws-credentials action after 3 retries due to network issues:

Error while copying content to a stream.
Failed to download archive '...aws-actions/configure-aws-credentials...' after 3 attempts.

No code fix is needed. Please re-run the failed job to resolve this.

The cancellationDeadline and cancellationPolicy fields in the
ReservationItem type are typed as optional (string | undefined),
but the code was assigning null values, causing a TypeScript error.

Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
@MelvinBot
Copy link
Copy Markdown
Contributor Author

Fixed the typecheck failure: getCancellationDeadline returned string | null but the cancellationDeadline field is typed as string | undefined. Changed to use undefined instead of null for both cancellationPolicy and cancellationDeadline fields, and updated the === null check in CarTripDetails.tsx accordingly.

The build (Debug, true) failure is unrelated — GitHub Actions failed to download the aws-actions/configure-aws-credentials action (infra issue). It should pass on re-run.

@MelvinBot
Copy link
Copy Markdown
Contributor Author

The TypeScript check failure was caused by cancellationPolicy and cancellationDeadline being set to null (via ?? null) while the Reservation type expects string | undefined. The latest commit (4f1e972) fixed this by replacing ?? null with optional chaining, which correctly produces undefined instead of null. I've verified the typecheck passes locally — the new CI run should be green.

@MelvinBot
Copy link
Copy Markdown
Contributor Author

⚠️ MelvinBot CI Fix Loop Detected

MelvinBot has made 5 consecutive commits trying to fix CI failures. Automatic CI fix dispatches are now paused for this PR.

Next steps: A human should review the recent commits and resolve the conflicting CI errors manually.

@eh2077
Copy link
Copy Markdown
Contributor

eh2077 commented Apr 16, 2026

The fix effect looks like below, let me know if it makes sense cc @trjExpensify @stitesExpensify

Screenshot 2026-04-16 at 11 50 10 PM Screenshot 2026-04-16 at 11 51 09 PM

@trjExpensify
Copy link
Copy Markdown
Contributor

Right, I'm saying:

  • Spotnana is bad for not specifying the timezone of "3pm"
  • We should tell them to improve that.
  • Regardless of that, let's include the timezone on our side.

@stitesExpensify
Copy link
Copy Markdown
Contributor

Regardless of that, let's include the timezone on our side.

Agreed! We should definitely make it automatically show up in the user's timezone (and show the timezone to be super clear)

@trjExpensify
Copy link
Copy Markdown
Contributor

Yeah, so I'm fine with the local time of check-in.. and that time matches Spotnana... but I think we should clarify the timezone.

@stitesExpensify
Copy link
Copy Markdown
Contributor

but I think we should clarify the timezone.

100%

@eh2077
Copy link
Copy Markdown
Contributor

eh2077 commented Apr 23, 2026

@MelvinBot rework the PR to use solution from this commit f166d76

Switch from parseISO + format to formatInTimeZone from date-fns-tz,
which renders the date/time in the venue's timezone (embedded as the
UTC offset in the ISO string) instead of converting to device-local time.

Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
@MelvinBot
Copy link
Copy Markdown
Contributor Author

Reworked getFormattedCancellationDate to use formatInTimeZone from date-fns-tz (matching the approach from f166d76), so the cancellation date/time is rendered in the venue's timezone instead of being converted to device-local time. Updated tests to expect venue-local times accordingly.

@eh2077
Copy link
Copy Markdown
Contributor

eh2077 commented Apr 23, 2026

@MelvinBot revert change of file src/libs/TripReservationUtils.ts and src/types/onyx/TripData.ts

Reverts getCancellationDeadline helper, parseDurationToSeconds day support,
durationBeforeArrivalDeadline type, and null-to-undefined changes per review.

Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
@MelvinBot
Copy link
Copy Markdown
Contributor Author

Reverted all changes to src/libs/TripReservationUtils.ts and src/types/onyx/TripData.ts back to their main versions. Also restored the === null checks in CarTripDetails.tsx since those were dependent on the TripReservationUtils changes (?? null vs ?).

Comment thread src/libs/DateUtils.ts Outdated
const offsetMatch = isoDateString.match(/([+-]\d{2}:\d{2})$/);
const venueTimezone = offsetMatch ? offsetMatch[1] : 'UTC';
const date = new Date(isoDateString);
const pattern = isThisYear(date) ? 'EEEE, MMM d h:mm a' : 'EEEE, MMM d, yyyy h:mm a';
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.

@MelvinBot include timezone in pattern and update unit test

Suggested change
const pattern = isThisYear(date) ? 'EEEE, MMM d h:mm a' : 'EEEE, MMM d, yyyy h:mm a';
const pattern = isThisYear(date) ? 'EEEE, MMM d h:mm a, zzz' : 'EEEE, MMM d, yyyy h:mm a, zzz';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — added zzz to both format patterns and updated unit tests to expect the timezone abbreviation (e.g. GMT+7, UTC).

Add zzz to the format pattern so the venue timezone is displayed
alongside the date/time (e.g. "Sunday, Apr 19, 2026 3:00 PM, GMT+7").
Updated unit tests to match.

Co-authored-by: Eric Han <eh2077@users.noreply.github.com>
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