Context: https://bugzilla.xamarin.com/show_bug.cgi?id=24947
When an Android user changes the timezone, those TimeZone changes
aren't reflected in Xamarin.Android apps. Part of this is a
Xamarin.Android bug, in which it needs to listen for TimeZone change
notifications and in turn let Mono know that the TimeZone changed.
Xamarin.Android can/will call Thread.CurrentCulture.ClearCachedData()
and Thread.CurrentUICulture.ClearCachedData() when the TimeZone has
changed. This will allow Mono to lookup the TimeZone info again.
At which point we hit two problems within Mono:
1. TimeZoneInfo.AndroidTimeZones.Local is unnecessarily cached.
2. DateTime itself caches the UTC offset for use by DateTime.Now.
TimeZoneInfo.Local is *already* cached, and TimeZoneInfo.Local is
properly cleared by TimeZoneInfo.ClearCachedData() while
TimeZoneInfo.AndroidTimeZones.Local wasn't. Removing the extra
cache allows the TimeZoneInfo to be looked up again.
Which brings us to DateTime.Now: for my simple test case, after
calling CultureInfo.ClearCachedData() the result of
DateTime.Now.ToString() didn't reflect the newly current TimeZone. The
cause for this was DateTime.to_local_time_span_object and
DateTime.last_now, which DateTime.Now uses as a cache for the
TimeZone's UTC offset.
Since these DateTime fields weren't cleared, subsequent DateTime.Now
invocations reported the *previous* TimeZone instead of the current
timezone.
To fix this, add an `internal` DateTime.ClearCachedData() method, and
call DateTime.ClearCachedData() from CultureInfo.ClearCachedData().
return sign * (hour * 60) * 60;
}
- static readonly object _lock = new object ();
-
static TimeZoneInfo defaultZone;
internal static TimeZoneInfo Local {
get {
- lock (_lock) {
- if (defaultZone != null)
- return defaultZone;
- var id = GetDefaultTimeZoneName ();
- return defaultZone = GetTimeZone (id, id);
- }
+ var id = GetDefaultTimeZoneName ();
+ return defaultZone = GetTimeZone (id, id);
}
}
RegionInfo.ClearCachedData ();
TimeZone.ClearCachedData ();
+ DateTime.ClearCachedData ();
#if NET_4_5
TimeZoneInfo.ClearCachedData ();
#endif
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal static extern long GetNow ();
+ internal static void ClearCachedData ()
+ {
+ to_local_time_span_object = null;
+ }
+
//
// To reduce the time consumed by DateTime.Now, we keep
// the difference to map the system time into a local
long now = GetNow ();
DateTime dt = new DateTime (now);
- if (Math.Abs (now - last_now) > TimeSpan.TicksPerMinute){
+ if (to_local_time_span_object == null || Math.Abs (now - last_now) > TimeSpan.TicksPerMinute){
to_local_time_span_object = TimeZone.CurrentTimeZone.GetLocalTimeDiff (dt);
last_now = now;