Skip to content

Reset Hermes timezone cache on system timezone change - #55467

Open
Im-awesome-Aadi wants to merge 1 commit into
react:mainfrom
Im-awesome-Aadi:fix/my-awesome-fix
Open

Im-awesome-Aadi wants to merge 1 commit into
react:mainfrom
Im-awesome-Aadi:fix/my-awesome-fix

Conversation

@Im-awesome-Aadi

@Im-awesome-Aadi Im-awesome-Aadi commented Feb 8, 2026 •

Copy link
Copy Markdown

Changelog: [Android] [Fixed] Reset Hermes timezone cache on system timezone change
Summary:

When the device timezone changes while the app is running, JavaScript Date still return the local time according to the previous timezone.

This happens because Hermes caches timezone information to improve performance. If the device timezone changes during app runtime, local time calculations continue using the old cached timezone.

Hermes provides a native API, resetTimezoneCache(), to reset this cached information. This PR adds an internal Android module that calls this API whenever the platform detects a timezone change.

Changes:

Kotlin: TimeZoneModule

Listens for Intent.ACTION_TIMEZONE_CHANGED broadcasts

Calls a JNI method to reset Hermes timezone cache

Safely registers/unregisters the receiver during Catalyst lifecycle

Open for unit testing

C++: TimeZoneCache

Provides resetNativeHermesTimeZoneCache() JNI method

Calls hermes::IHermes::resetTimezoneCache()

No-op if runtime pointer is null or runtime is not Hermes

Test Plan:

Java unit tests

Verify broadcast receiver registration/unregistration

Verify native reset hook is invoked on timezone change

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Feb 8, 2026
@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Feb 8, 2026
…mezone change

Summary:

When the device timezone changes while the app is running, JavaScript Date still return the local time according to the previous timezone.

This happens because Hermes caches timezone information to improve performance. If the device timezone changes during app runtime, local time calculations continue using the old cached timezone.

Hermes provides a native API, resetTimezoneCache(), to reset this cached information. This PR adds an internal Android module that calls this API whenever the platform detects a timezone change.

Changes:

Kotlin: TimeZoneModule

Listens for Intent.ACTION_TIMEZONE_CHANGED broadcasts

Calls a JNI method to reset Hermes timezone cache

Safely registers/unregisters the receiver during Catalyst lifecycle

Open for unit testing

C++: TimeZoneCache

Provides resetNativeHermesTimeZoneCache() JNI method

Calls hermes::IHermes::resetTimezoneCache()

No-op if runtime pointer is null or runtime is not Hermes

Test Plan:

Java unit tests

Verify broadcast receiver registration/unregistration

Verify native reset hook is invoked on timezone change

private fun handleTimeZoneChange() {
try {
val catalystInstance = reactApplicationContext.catalystInstance ?: return

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.

catalystInstance is a legacy arch concept and javaScriptContextHolder won't work there. Please ensure this works with the new arch.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks — that’s a good point. I’ll remove the dependency on CatalystInstance and switch to using RuntimeExecutor so this works correctly in the New Architecture and Bridgeless mode as well. I’ll update the JNI signature to accept a jsi::Runtime& instead of relying on JavaScriptContextHolder.

@troZee

troZee commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

Hello @javache @Im-awesome-Aadi
I fixed that issue in this library https://github.com/callstack/timezone-hermes-fix

Do you have plans to merge this PR?

@Im-awesome-Aadi

Copy link
Copy Markdown
Author

Hello @javache @Im-awesome-Aadi I fixed that issue in this library https://github.com/callstack/timezone-hermes-fix

Do you have plans to merge this PR?

Hey @troZee , i saw your repo addresses the same issue. I'm trying to fix it in core react-native.
Working on the comment from javache.

@react-native-bot

Copy link
Copy Markdown
Collaborator

This PR is stale because it has been open for 180 days with no activity. It will be closed in 7 days unless you comment on it or remove the "Stale" label.

@react-native-bot react-native-bot added the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 18, 2026
@react-native-bot

Copy link
Copy Markdown
Collaborator

This PR is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@react-native-bot react-native-bot removed the Stale There has been a lack of activity on this issue and it may be closed soon. label Aug 18, 2026
@renelaerke

Copy link
Copy Markdown

Hi @Im-awesome-Aadi, @javache — thanks for working on this; it is a real bug and an easy one to
miss.

We hit it in production (a travel-expense app: entries captured after a flight were dated with the
departure zone's calendar day) and have been running a fix for both platforms since August. I've
written it up with measurements, the real-world cases and a reproducer in #59129.

On the open review point (catalystInstance / javaScriptContextHolder being legacy-arch): on
Android we reach the runtime through ReactContext.getJSCallInvokerHolder() and schedule the
reset on the JS thread with the CallInvoker, then castInterface<IHermes>(runtime) and call
resetTimezoneCache(). That works under the New Architecture and bridgeless mode, and it keeps the
reset on the JS thread by construction. It is triggered from onHostResume and from
Intent.ACTION_TIMEZONE_CHANGED — the broadcast repairs the cache even while the app is
backgrounded.

iOS needs the same treatment (this PR is Android-only): there we observe
UIApplicationDidBecomeActiveNotification and NSSystemTimeZoneDidChangeNotification and run
the reset through the RCTCallInvoker. Measured on an iPhone 17e and a Galaxy S24, same process
across Copenhagen → Sydney: getTimezoneOffset() went 120 → 600 with the reset, and stayed at
120 without it.

Since this PR has been quiet since February, I'm planning to open a PR covering both platforms on
that approach, referencing this one and #59129. If you'd rather carry it forward here, I'm happy
to contribute the iOS half and the New-Arch Android wiring to your branch instead — just say which
you prefer.

@renelaerke

Copy link
Copy Markdown

Small correction: the issue is now #59131 (re-filed through the bug-report form; #59129 is closed as a duplicate).

@troZee

troZee commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

This branch has not been deployed

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants