Skip to content

Commit 46d9b31

Browse files
authored
[Android][libraries] TimeZoneInfo Android imp (#54845)
Fixes #41867 Android has removed zone.tab, so TimeZoneInfo.Unix.cs will no longer work properly on Android as GetTimeZoneIds directly depends on the zone.tab file. The chain of dependency is as follows GetSystemTimeZones -> 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 reads zone.tab to obtain the TimeZoneId in that file PopulateAllSystemTimeZones caches all the TimeZone Ids in cachedData GetSystemTimeZones returns a ReadOnlyCollection containing all valid TimeZone’s from the local machine, and the entries are sorted by their DisplayName. It relies on cachedData._systemTimeZones being populated. The problem is that the time zone data for Android can be found in the file tzdata at the possible locations /apex/com.android.tzdata/etc/tz/tzdata /apex/com.android.runtime/etc/tz/tzdata /data/misc/zoneinfo/tzdata /system/usr/share/zoneinfo/tzdata The rest of unix the time zone data can be found in the file zone.tab at /usr/share/zoneinfo/zone.tab Android'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: 1. Splits TimeZoneInfo.Unix.cs into TimeZoneInfo.Unix.cs, TimeZoneInfo.Unix.NonAndroid.cs (non-Android), and TimeZoneInfo.Unix.Android.cs (Android specific) 2. Adds an interop to obtain the default time zone on Android based on persist.sys.timezone 3. Implements GetLocalTimeZoneCore TryGetTimeZoneFromLocalMachineCore and GetTimeZoneIds for Android based on mono/mono implementation https://github.com/mono/mono/blob/main/mcs/class/corlib/System/TimeZoneInfo.Android.cs 4. Adds new string resources to throw exceptions 5. Refactors the mono/mono implementation of parsing tzdata Android tzdata files are found in the format of Header <Beginning of Entry Index> Entry Entry Entry ... Entry <Beginning of Data Index> <TZDATA> https://github.com/aosp-mirror/platform_bionic/blob/master/libc/tzcode/bionic.cpp The header (24 bytes) contains the following information signature - 12 bytes of the form "tzdata2012f\0" where 2012f is subject to change index offset - 4 bytes that denotes the offset at which the index of the tzdata file starts data offset - 4 bytes that denotes the offset at which the data of the tzdata file starts final offset - 4 bytes that used to denote the final offset, which we don't use but will note. Each Data Entry (52 bytes) can be used to generate a TimeZoneInfo and contain the following information id - 40 bytes that contain the id of the time zone data entry timezone<id> byte offset - 4 bytes that denote the offset from the data offset timezone<id> data can be found length - 4 bytes that denote the length of the data for timezone<id> unused - 4 bytes that used to be raw GMT offset, but now is always 0 since tzdata2014f (L). When GetLocalTimeZoneCore TryGetTimeZoneFromLocalMachineCore or GetTimeZoneIds 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 from TimeZoneInfo.Unix.cs are leveraged to create a TimeZoneInfo from there.
1 parent 1d8ad03 commit 46d9b31

File tree

8 files changed

+942
-448
lines changed

8 files changed

+942
-448
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
internal static partial class Interop
8+
{
9+
internal static partial class Sys
10+
{
11+
[DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetDefaultTimeZone", CharSet = CharSet.Ansi, SetLastError = true)]
12+
internal static extern string? GetDefaultTimeZone();
13+
}
14+
}

src/libraries/Native/Unix/System.Native/pal_datetime.c

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
#include "pal_config.h"
5-
6-
#include <stdlib.h>
5+
#include "pal_datetime.h"
76
#include <stdint.h>
8-
#include <time.h>
7+
#include <stdlib.h>
8+
#include <string.h>
99
#include <sys/time.h>
10-
11-
#include "pal_datetime.h"
10+
#if defined(TARGET_ANDROID)
11+
#include <sys/system_properties.h>
12+
#endif
13+
#include <time.h>
1214

1315
static const int64_t TICKS_PER_SECOND = 10000000; /* 10^7 */
1416
#if HAVE_CLOCK_REALTIME
@@ -39,3 +41,18 @@ int64_t SystemNative_GetSystemTimeAsTicks()
3941
// in failure we return 00:00 01 January 1970 UTC (Unix epoch)
4042
return 0;
4143
}
44+
45+
#if defined(TARGET_ANDROID)
46+
char* SystemNative_GetDefaultTimeZone()
47+
{
48+
char defaulttimezone[PROP_VALUE_MAX];
49+
if (__system_property_get("persist.sys.timezone", defaulttimezone))
50+
{
51+
return strdup(defaulttimezone);
52+
}
53+
else
54+
{
55+
return NULL;
56+
}
57+
}
58+
#endif

src/libraries/Native/Unix/System.Native/pal_datetime.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,10 @@
44
#pragma once
55

66
#include "pal_compiler.h"
7+
#include "pal_types.h"
78

89
PALEXPORT int64_t SystemNative_GetSystemTimeAsTicks(void);
10+
11+
#if defined(TARGET_ANDROID)
12+
PALEXPORT char* SystemNative_GetDefaultTimeZone(void);
13+
#endif

src/libraries/System.Private.CoreLib/src/Resources/Strings.resx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3802,4 +3802,16 @@
38023802
<data name="Arg_MemberInfoNotFound" xml:space="preserve">
38033803
<value>A MemberInfo that matches '{0}' could not be found.</value>
38043804
</data>
3805+
<data name="InvalidOperation_BadTZHeader" xml:space="preserve">
3806+
<value>Bad magic in '{0}': Header starts with '{1}' instead of 'tzdata'</value>
3807+
</data>
3808+
<data name="InvalidOperation_ReadTZError" xml:space="preserve">
3809+
<value>Unable to fully read from file '{0}' at offset {1} length {2}; read {3} bytes expected {4}.</value>
3810+
</data>
3811+
<data name="InvalidOperation_BadIndexLength" xml:space="preserve">
3812+
<value>Length in index file less than AndroidTzDataHeader</value>
3813+
</data>
3814+
<data name="TimeZoneNotFound_ValidTimeZoneFileMissing" xml:space="preserve">
3815+
<value>Unable to properly load any time zone data files.</value>
3816+
</data>
38053817
</root>

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,6 +1958,9 @@
19581958
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetCwd.cs">
19591959
<Link>Common\Interop\Unix\System.Native\Interop.GetCwd.cs</Link>
19601960
</Compile>
1961+
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetDefaultTimeZone.Android.cs" Condition="'$(TargetsAndroid)' == 'true'">
1962+
<Link>Common\Interop\Unix\System.Native\Interop.GetDefaultTimeZone.Android.cs</Link>
1963+
</Compile>
19611964
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetEGid.cs">
19621965
<Link>Common\Interop\Unix\System.Native\Interop.GetEGid.cs</Link>
19631966
</Compile>
@@ -2131,6 +2134,8 @@
21312134
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelMonitor.Unix.cs" />
21322135
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\TimerQueue.Unix.cs" />
21332136
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.cs" />
2137+
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.Android.cs" Condition="'$(TargetsAndroid)' == 'true'" />
2138+
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Unix.NonAndroid.cs" Condition="'$(TargetsAndroid)' != 'true'" />
21342139
</ItemGroup>
21352140
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
21362141
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetEUid.cs">

0 commit comments

Comments
 (0)