Repository navigation
Conversation
bootstrapTimezone() set TZ from localtime_r(...).tm_zone, which is only the abbreviation (e.g. "CEST"). As a POSIX TZ string without an offset that means UTC to bionic, so after the bootstrap libc's localtime and a DateFormatter without an explicit timeZone ran in UTC, while TimeZone.current mapped the abbreviation to some other zone with the same offset (Europe/Paris for a Europe/Berlin device). Read the system time zone from the persist.sys.timezone property (what bionic itself uses when TZ is unset) and only set TZ when it isn't set already. Co-Authored-By: Claude Opus 5.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.
bootstrapTimezone()setsTZfromlocaltime_r(...).tm_zone, which is only the time zone abbreviation ("CEST"on a Europe/Berlin device). bionic reads a POSIX TZ string without an offset as UTC, so once the bootstrap has run:localtime/mktimerun in UTCDateFormatterwithout an explicittimeZoneformats and parses in UTCTimeZone.currentresolves the abbreviation through Foundation's abbreviation table, so it gets another zone that happens to share the offset (Europe/Paris instead of Europe/Berlin)In our app (Skip Fuse, native) every "x hours ago" was off by 2 hours, because the API's local timestamps were parsed in UTC.
This PR reads the device's Olson ID from the
persist.sys.timezonesystem property, which is what bionic itself uses whenTZis unset. It setsTZonly if it isn't set already. With the Olson ID, Foundation, libc and ICU all agree.How I checked it: I built a small native Swift executable with the Swift 6.4 Android SDK and ran it on an API 36 emulator set to Europe/Berlin. It's a separate test program, not the bridge's test suite. Each run is a fresh process, because
TimeZone.currentis cached:TZset byTimeZone.currentlocaltimeDateFormatter()GMT, offset 022:59 GMT+2bootstrapTimezone()→TZ=CESTEurope/Paris, +720020:59 UTCTZ=Europe/BerlinEurope/Berlin, +720022:59 GMT+2SKIP_DYNAMIC_LIBRARIES=1 swift buildandskip android buildboth succeed. The AndroidlibSkipAndroidBridge.solinks__system_property_get@LIBC.Notes:
PROP_VALUE_MAX(92) isn't importable as a Swift constant from the Android module, so the buffer size is a literal with a comment.__system_property_getitself is visible without extra imports.#if os(Android). Its only call site already was.TZstays at the value it had at launch (same as before this PR). The Kotlin side would need to listen forACTION_TIMEZONE_CHANGED, then updateTZ, calltzset()andNSTimeZone.resetSystemTimeZone(). A real fix belongs in swift-foundation:TimeZone_Cache.findCurrentTimeZone()could readpersist.sys.timezoneon Android before falling back totm_zone, which isn't a valid identifier there either.TZthemselves beforeProcessInfo.launchinMain.kt; the bootstrap usesoverwrite = 0, so their value wins:android.system.Os.setenv("TZ", java.util.TimeZone.getDefault().id, true).