fix(timezone): compute instance .tz() offset without host DST - #3174
Merged
Conversation
Use tzOffset (Intl formatToParts) in proto.tz instead of toLocaleString + new Date + getTimezoneOffset, so fixed-offset zones like America/Guadeloupe keep UTC-4 across host spring DST.
Add iamkun's requested conversion tests (NY/Paris desync, keepLocalTime, New_York/Auckland hosts) plus passing parse/convert cases from iamkun#3169.
4 tasks
Contributor
Author
|
@iamkun we should be good to go so here |
Contributor
Author
|
One test from #3169 is intentionally not in this PR: That case still fails without the later refactor. |
Owner
|
LGTM, THX |
iamkun
added a commit
that referenced
this pull request
Aug 16, 2026
* chore: update doc * chore: update doc * chore: update doc * fix(plugin): timezone compute instance .tz() offset without host DST (#3174) * fix(timezone): compute .tz() offset without host DST Use tzOffset (Intl formatToParts) in proto.tz instead of toLocaleString + new Date + getTimezoneOffset, so fixed-offset zones like America/Guadeloupe keep UTC-4 across host spring DST. * test(timezone): cover DST-observing zones and host TZ matrix Add iamkun's requested conversion tests (NY/Paris desync, keepLocalTime, New_York/Auckland hosts) plus passing parse/convert cases from #3169. --------- Co-authored-by: Antoine Cormouls <contact.antoine.cormouls@gmail.com>
|
🎉 This PR is included in version 1.11.22 🎉 The release is available on: Your semantic-release bot 📦🚀 |
1 task
2 tasks
guillermoscript
added a commit
to guillermoscript/calistenia-app
that referenced
this pull request
Aug 27, 2026
…) en Hermes (#660) * fix(core): la app no arrancaba en Android — dayjs 1.11.22+ rompe .tz() en Hermes `RangeError: Date value out of bounds` al abrir la v1.12.1 (vc37): el bump de dayjs 1.11.21 → 1.11.23 (#657) cambió el cálculo de offset del plugin timezone (iamkun/dayjs#3174) a parsear `Intl.DateTimeFormat().formatToParts`, que en Hermes devuelve NaN → `todayStr()` = «Invalid Date» → `computeCurrentStreak` hacía `new Date(NaN).toISOString()` dentro del WorkoutProvider y tumbaba el árbol entero. Web (V8) no lo sufre. - dayjs pineado a 1.11.21 (core, web, mcp-server + override de pnpm). - `todayStrIn` cae a la hora local si el plugin devuelve algo que no es YYYY-MM-DD, y `computeCurrentStreak` devuelve 0 con un `today` inválido en vez de lanzar. Test añadido. - Release 1.12.2 / vc38 (hotfix sobre 1.12.1, que nunca salió de internal). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BxJW1Ydsfa6uwQ4VCCFgmC * chore(deps): el override de dayjs va en pnpm-workspace.yaml (pnpm 10 ignora package.json#pnpm) * fix(release): build:aab purga el bundle JS generado antes de gradle createBundleReleaseJsAndAssets solo mira los fuentes de apps/mobile: con cambios solo en packages/core queda UP-TO-DATE y el AAB sale con el bundle viejo (le pasó al hotfix de dayjs de la v1.12.2). * fix(core): un startDate inválido rehidratado de caché ya no tumba la Home Segundo crash de la v1.12.2 en dispositivo: la vc37 (dayjs roto) había persistido settings.startDate = «Invalid Date» en localStorage y en la caché de React Query; al rehidratarlo, `diffDays(todayStr(), 'Invalid Date')` lanzaba `RangeError: Invalid time value` — `dayjs.tz(str, tz)` con una cadena imparseable no devuelve un dayjs inválido, LANZA desde formatToParts. - ensureStartDate repara un startDate que no sea YYYY-MM-DD. - tzDate: addDaysIn/diffDaysIn/localMidnightAsUTCIn/utcToLocalDateStrIn parsean primero en UTC y devuelven un valor neutro (0 / '' / la entrada) con aviso en vez de lanzar. Tests nuevos. - PERSIST_BUSTER v3 para desechar la caché persistida por vc37. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BxJW1Ydsfa6uwQ4VCCFgmC --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes incorrect timezone offsets when converting an instant with instance
.tz()on a host that observes DST.This is the minimal follow-up to #3169, as requested by @iamkun: only the original
tzOffset()change inproto.tz, no wall-clock /set/add/startOfrefactor.Bug:
America/Guadeloupeis a fixed UTC−4 zone (no DST). On hosts such asEurope/ParisorEurope/London, around the EU spring-forward Sunday,dayjs(instant).tz('America/Guadeloupe')could report −03:00 instead of −04:00. Adding one day and converting again could then land on the same calendar day. The same host-DST leak also affects DST-observing target zones when their calendar is out of sync with the host (US vs EU mid-March / late October, southern-hemisphere summer).Root cause:
proto.tzderived the offset from:Both
new Date(target)andgetTimezoneOffset()depend on the host timezone.The static helper
dayjs.tz(string, zone)already usedtzOffsetviaIntl.DateTimeFormat#formatToParts(host-independent). Instance.tz()did not.Fix: reuse the existing
tzOffset(+date, timezone)helper inproto.tzfor the offset. Keep wall-clock construction viatoLocaleStringand the rest of the method unchanged (utcOffset,keepLocalTime,$timezone). No changes to the utc plugin or core parse/$set.Related: #1260
Follow-up: #3169 (calendar arithmetic
set/add/startOfon zoned instances)Tests added
America/Guadeloupearound EU spring DST (offset −240, moment parity, +1 calendar day)keepLocalTimebetween DST-desynced zones (NY → Paris)keepLocalTime, quarter-hour / date-line offsets,diffacross target DSTHost matrix for
test-tz-plugin:Europe/Paris,Europe/London,America/New_York,Pacific/Auckland.Calendar-arithmetic DST cases (
startOf/set/add/ host spring-forward gap via instance.tz()) stay in #3169.Test plan
TZ=Europe/Paris npm run test-tz-pluginTZ=Europe/London npm run test-tz-pluginTZ=America/New_York npm run test-tz-pluginTZ=Pacific/Auckland npm run test-tz-pluginCredits
This PR was prepared with Cursor and assistance from Grok 4.5 High.