Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ namespace System
{
public readonly partial struct DateTimeOffset
{
private static bool s_androidTZDataLoaded;
private static readonly object s_localUtcOffsetLock = new();
private static Thread? s_loadAndroidTZData;
private static bool s_startNewBackgroundThread = true;
// 0 == in process of being loaded, 1 == loaded
private static volatile int s_androidTZDataLoaded = -1;

// Now on Android does the following
// 1) quickly returning a fast path result when first called if the right AppContext data element is set
Expand All @@ -29,52 +27,38 @@ public static DateTimeOffset Now
{
DateTime utcDateTime = DateTime.UtcNow;

if (s_androidTZDataLoaded) // The background thread finished, the cache is loaded.
if (s_androidTZDataLoaded == 1) // The background thread finished, the cache is loaded.
{
return ToLocalTime(utcDateTime, true);
}

if (s_startNewBackgroundThread) // The cache isn't loaded and no background thread has been created
object? localDateTimeOffset = AppContext.GetData("System.TimeZoneInfo.LocalDateTimeOffset");
if (localDateTimeOffset == null) // If no offset property provided through monovm app context, default
{
lock (s_localUtcOffsetLock)
{
// Now may be called multiple times before a cache is loaded and a background thread is running,
// once the lock is available, check for a cache and background thread.
if (s_androidTZDataLoaded)
return ToLocalTime(utcDateTime, true);
// no need to create the thread, load tzdata now
s_androidTZDataLoaded = 1;
return ToLocalTime(utcDateTime, true);
}

if (s_loadAndroidTZData == null)
// The cache isn't loaded yet.
if (Interlocked.CompareExchange(ref s_androidTZDataLoaded, 0, -1) == -1)
{
new Thread(() =>
{
try
{
s_loadAndroidTZData = new Thread(() => {
// Delay the background thread to avoid impacting startup, if it still coincides after 1s, startup is already perceived as slow
Thread.Sleep(1000);

_ = TimeZoneInfo.Local; // Load AndroidTZData
s_androidTZDataLoaded = true;
// Delay the background thread to avoid impacting startup, if it still coincides after 1s, startup is already perceived as slow
Thread.Sleep(1000);

lock (s_localUtcOffsetLock)
{
s_loadAndroidTZData = null; // Ensure thread is cleared when cache is loaded
}
});
s_loadAndroidTZData.IsBackground = true;
_ = TimeZoneInfo.Local; // Load AndroidTZData
}
}

if (s_startNewBackgroundThread)
{
// Because Start does not block the calling thread,
// setting the boolean flag to false immediately after should
// prevent two calls to DateTimeOffset.Now in quick succession
// from both reaching here.
s_loadAndroidTZData.Start();
s_startNewBackgroundThread = false;
}
finally
{
s_androidTZDataLoaded = 1;
}
}) { IsBackground = true }.Start();
}


object? localDateTimeOffset = AppContext.GetData("System.TimeZoneInfo.LocalDateTimeOffset");
if (localDateTimeOffset == null) // If no offset property provided through monovm app context, default
return ToLocalTime(utcDateTime, true);

// Fast path obtained offset incorporated into ToLocalTime(DateTime.UtcNow, true) logic
int localDateTimeOffsetSeconds = Convert.ToInt32(localDateTimeOffset);
TimeSpan offset = TimeSpan.FromSeconds(localDateTimeOffsetSeconds);
Expand Down