[mscorlib/Android] CultureInfo.ClearCachedData() clears DateTime.
authorJonathan Pryor <jonpryor@vt.edu>
Fri, 12 Dec 2014 17:17:53 +0000 (12:17 -0500)
committerJonathan Pryor <jonpryor@vt.edu>
Fri, 12 Dec 2014 17:29:04 +0000 (12:29 -0500)
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().

mcs/class/System.Core/System/TimeZoneInfo.Android.cs
mcs/class/corlib/System.Globalization/CultureInfo.cs
mcs/class/corlib/System/DateTime.cs

index 8805dfe898213aad2f9c123b06af58043c192a02..b0f65d61dff1c4d4d0a8d0b851fe157c8165977d 100644 (file)
@@ -531,17 +531,11 @@ namespace System {
                                        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);
                                }
                        }
                        
index 5edf1fab803ff9f9743d644515f995d8aebe7df8..1fde454eae056fda27574aa49fa476b1ab09cd93 100644 (file)
@@ -377,6 +377,7 @@ namespace System.Globalization
 
                        RegionInfo.ClearCachedData ();
                        TimeZone.ClearCachedData ();
+                       DateTime.ClearCachedData ();
 #if NET_4_5
                        TimeZoneInfo.ClearCachedData ();
 #endif
index 8ff4901e4a3e47e10c46213b93de77d1c8d8b6da..8e64a783ecf947bda8b249b66debb99b70774134 100644 (file)
@@ -460,6 +460,11 @@ namespace System
                [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
@@ -474,7 +479,7 @@ namespace System
                                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;