-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Android][libraries] TimeZoneInfo Android imp #54845
Conversation
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
Unlike other Unix implementations where |
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.AnyUnix.cs
Outdated
Show resolved
Hide resolved
@mdh1418 As I suspected, there's a little bit more to this. I have more local and we can talk tomorrow about the details. |
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.AnyUnix.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Android.cs
Outdated
Show resolved
Hide resolved
} | ||
} | ||
|
||
return Environment.GetEnvironmentVariable("ANDROID_ROOT") + DefaultTimeZoneDirectory; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This look odd. Are you sure we should mix 2 rooted locations?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'm missing something, what are the two rooted locations, ANDROID_ROOT
and?
The code was adapted from https://github.com/mono/mono/blob/7791e4d94206dd63acff49268b56a01c0b69983c/mcs/class/corlib/System/TimeZoneInfo.Android.cs#L68-L73 and https://github.com/mono/mono/blob/7791e4d94206dd63acff49268b56a01c0b69983c/mcs/class/corlib/System/TimeZoneInfo.Android.cs#L488-L495 before AndroidTzData's
own Path
was introduced into the PR.
Tagging subscribers to this area: @tarekgh, @safern Issue DetailsFixes #41867 Android has removed
The problem is that the time zone data for Android can be found in the file tzdata at the possible locations
The rest of unix the time zone data can be found in the file
As This PR achieves the following:
|
Tagging subscribers to 'arch-android': @steveisok, @akoeplinger Issue DetailsFixes #41867 Android has removed
The problem is that the time zone data for Android can be found in the file tzdata at the possible locations
The rest of unix the time zone data can be found in the file
As This PR achieves the following:
|
Tagging subscribers to this area: @dotnet/area-system-runtime Issue DetailsFixes #41867 Android has removed
The problem is that the time zone data for Android can be found in the file tzdata at the possible locations
The rest of unix the time zone data can be found in the file
As This PR achieves the following:
|
…Zone into separate Unix and Android functions.
…Zones to map to a method in the mono port
…Core in the mono port, so created TryGetTimeZoneFromLocalMachineCore for both TimeZoneInfo.Unix.cs and Android.cs
…meZoneFromLocalMachineCore
*/ | ||
private sealed class AndroidTzData | ||
{ | ||
private unsafe struct AndroidTzDataHeader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private unsafe struct AndroidTzDataHeader | |
private readonly struct AndroidTzDataHeader |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
error CS8340: Instance fields of readonly structs must be readonly.
/Users/mdhwang/runtime_droid/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs(314,17): error CS0191: A readonly field cannot be assigned to (except in a constructor or init-only setter of the type in which the field is defined or a variable initializer)
I went ahead and removed this struct altogether, using out parameters for indexOffset and dataOffset in LoadTzFile
and LoadHeader
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
Span<byte> buffer = stackalloc byte[length]; | ||
ReadTzDataIntoBuffer(File.OpenRead(_tzFilePath), offset, buffer); | ||
byte[] tzBuffer = buffer.ToArray(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No reason to make a stack buffer here, copy the file into the buffer, and then ToArray it.
- It is unnecessary copying
- We don't know that the length of the file will fit on the stack. In general, stackalloc's should not be unbounded.
Span<byte> buffer = stackalloc byte[length]; | |
ReadTzDataIntoBuffer(File.OpenRead(_tzFilePath), offset, buffer); | |
byte[] tzBuffer = buffer.ToArray(); | |
byte[] tzBuffer = new byte[length]; | |
ReadTzDataIntoBuffer(File.OpenRead(_tzFilePath), offset, tzBuffer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. I see, was it unnecessary copying because we need to still return a byte[]
here, whereas in LoadTzFile
we didn't "need" to use a byte[]
and a Span<byte>
would suffice?
Is there a general rule for how large a stackalloc should be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was it unnecessary copying because we need to still return a byte[] here, whereas in LoadTzFile we didn't "need" to use a byte[] and a Span would suffice?
Correct. In LoadTzFile, you just need a buffer to read the file data into, and then you are parsing the values out of it from the buffer. The buffer didn't need to stick around.
Here, since the method expects you to return a byte[]
, you might as well just read directly into the byte[]
you are going to return.
Is there a general rule for how large a stackalloc should be?
Looking around the codebase, we usually don't let a stackalloc go above 1K of memory.
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.Android.cs
Outdated
Show resolved
Hide resolved
Made a few more changes, including parsing the data entry bytes to construct a string immediately, removing the structs all together, removed unsafe keywords, and updated the description of this PR. Can I get another review @eerhardt @jkotas @steveisok |
Fixes #41867
Android has removed
zone.tab
, soTimeZoneInfo.Unix.cs
will no longer work properly on Android asGetTimeZoneIds
directly depends on thezone.tab
file. The chain of dependency is as followsGetSystemTimeZones
->PopulateAllSystemTimeZones
->GetTimeZoneIds
->zone.tab
TZI.cs TZI.Unix.cs TZI.Unix.cs TZI.Unix.cs
Where TZI is TimeZoneInfo
zone.tab
is a file that is found on the unix system under/usr/share/zoneinfo/
GetTimeZoneIds
readszone.tab
to obtain the TimeZoneId in that filePopulateAllSystemTimeZones
caches all the TimeZone Ids in cachedDataGetSystemTimeZones
returns a ReadOnlyCollection containing all valid TimeZone’s from the local machine, and the entries are sorted by theirDisplayName
. It relies oncachedData._systemTimeZones
being populated.The problem is that the time zone data for Android can be found in the file tzdata at the possible locations
The rest of unix the time zone data can be found in the file
zone.tab
atAndroid's TimeZoneInfo implementation should read time zone data from its locations instead of the general
/usr/share/zoneinfo/zone.tab
path. Moreover,tzdata
contains all timezones byte data.This PR achieves the following:
TimeZoneInfo.Unix.cs
intoTimeZoneInfo.Unix.cs
,TimeZoneInfo.Unix.NonAndroid.cs
(non-Android), andTimeZoneInfo.Unix.Android.cs
(Android specific)persist.sys.timezone
GetLocalTimeZoneCore
TryGetTimeZoneFromLocalMachineCore
andGetTimeZoneIds
for Android based on mono/mono implementation https://github.com/mono/mono/blob/main/mcs/class/corlib/System/TimeZoneInfo.Android.csWhen
GetLocalTimeZoneCore
TryGetTimeZoneFromLocalMachineCore
orGetTimeZoneIds
are called, an android timezone data instance is instantiated and loaded by attempting to load a tzdata file that can be found at four locations mentioned earlier. The file is parsed by first loading the header which contains information about where the data index and data begin. The data index is then parsed to obtain the timezone and the corresponding bytes location in the file to fill the three arrays_ids
_byteOffsets
_lengths
. These arrays are referenced to obtain the corresponding byte data for a timezone, and functions fromTimeZoneInfo.Unix.cs
are leveraged to create a TimeZoneInfo from there.