Bug 18132 - DateTime.Now returning time of old timezone after changing IOS system timezone
Summary: DateTime.Now returning time of old timezone after changing IOS system timezone
Status: ASSIGNED
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll (show other bugs)
Version: master
Hardware: All All
: Normal normal
Target Milestone: Untriaged
Assignee: Sebastien Pouliot
URL:
Depends on:
Blocks:
 
Reported: 2014-03-03 15:24 UTC by Chad Bumstead
Modified: 2015-06-22 11:29 UTC (History)
8 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report for Bug 18132 on Developer Community or GitHub if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: Developer Community HTML or GitHub Markdown
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:
Status:
ASSIGNED

Description Chad Bumstead 2014-03-03 15:24:53 UTC
DateTime.Now caches the offset for one minute (see below).   So while it is cached, Date.Now will display an incorrect time.

There should be a way to clear the DateTime.Now cache such as DateTime.ClearOffsetCache().
In addition, the Xamarin.IOS framework could utilize the AppDelegate.ApplicationSignificantTimeChange() method (which is called when timezone is changed, daylight savings time change, midnight) to call the DateTime.ClearOffsetCache() method.


https://bugzilla.xamarin.com/show_bug.cgi?id=9418#c4

This is the source for DateTime.Now:

//
// To reduce the time consumed by DateTime.Now, we keep
// the difference to map the system time into a local
// time into `to_local_time_span', we record the timestamp
// for this in `last_now'
//
static object to_local_time_span_object;
static long last_now;

public static DateTime Now {
get {
long now = GetNow ();
DateTime dt = new DateTime (now);
Comment 2 Ram Chandra 2014-03-04 06:20:45 UTC
I have checked this issue  and i am able to reproduce it.

Steps to reproduce :

(1) Create an "iOS application".
(2) Paste the following code in "AppDelegate.cs" class
.
			
                        DateTime dt1 = DateTime.Now;
			
                        DateTime dt2 = DateTime.Now;


(3) Open "watch window" in "Xamarin Studio" . 
(4) Place a breakpoint on "dt1" variable and  watch the value of  "dt1" variable.
(5) Change the "time zone" when app is running 
(6) Check the value of "dt2" variable.

screencast: http://www.screencast.com/t/SBpaw31C4gte

Environment:

Mac
Xamarin Studio: 4.2.3 (build 59)
Xamarin.iOS: 7.0.7.2
Comment 4 Sebastien Pouliot 2014-03-05 21:02:13 UTC
We won't add a public* API like `DateTime.ClearOffsetCache()` to do this - but we do have plans to fix this. 

The "right" solution is  a bit more complicated since it affects many bits, e.g. DateTime, TimeZone[Info]…, to continuously (and transparently) match the device.

* OTOH you can easily do the same feature in your application by using this code (and it will not crash/fail when we fix this), e.g.

		void ResetDateTimeCache ()
		{
			var f = typeof(DateTime).GetField ("last_now", BindingFlags.NonPublic | BindingFlags.Static);
			if (f == null)
				return; // we fixed this
			f.SetValue (null, long.MinValue);
		}

	Console.WriteLine ("1. {0}", DateTime.Now);
	ResetDateTimeCache ();
	Console.WriteLine ("2. {0}", DateTime.Now);

Inside XS you can add a debugger watch for `DateTime.last_now` and you'll see it (re)assigned which will make `to_local_time_span_object` be recomputed the next time `Now` is called (and/or you can put a breakpoint inside DateTime.cs).
Comment 5 Chad Bumstead 2014-03-10 14:38:26 UTC
Thank you Sebastien!  What would we do without reflection?

The above was a partial solution but we also need to set 'TimeZone.timezone_check' to a min value.
When I run the two methods below together DateTime.Now will give me a value that matches the IOS system time.
I override the AppDelegate ApplicationSignificantTimeChange method and reset the caches there.

    public static class DateTimeUtil
    {  
        public static void ResetDateTimeCache()
        {
            var f = typeof(DateTime).GetField ("last_now", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);

            if (f == null)
                return;
            f.SetValue (null, long.MinValue);
        }

        public static void ResetTimeZoneCache()
        {
            var f = typeof(TimeZone).GetField ("timezone_check", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);

            if (f == null)
                return;

            f.SetValue (null, long.MinValue);
        }  
    }  

    [Register ("AppDelegate")]
    public partial class AppDelegate : UIApplicationDelegate
    {  
        public override void ApplicationSignificantTimeChange(UIApplication application)
        {

            DateTimeUtil.ResetDateTimeCache ();
            DateTimeUtil.ResetTimeZoneCache ();

        }  
    }
Comment 6 Nate Cook 2015-06-08 13:00:03 UTC
This workaround doesn't seem to work anymore. (f == null is true). Is this fixed or is there another workaround?
Comment 7 Brendan Zagaeski (Xamarin Team, assistant) 2015-06-19 19:50:06 UTC
This problem has changed noticeably in Xamarin.iOS 8.10 due to the import of https://github.com/Microsoft/referencesource into Mono.


See for example `DateTime.Now`:

https://github.com/mono/referencesource/blob/ad2788873af4d5f149f83bf400507ad3eb49bffd/mscorlib/system/datetime.cs#L944



From my tests, it appears the behavior of `DateTime.Now` now matches the behavior in .NET [1].

> [1] http://stackoverflow.com/questions/296918/net-datetime-now-returns-incorrect-time-when-time-zone-is-changed



In particular, calling `System.Globalization.CultureInfo.CurrentCulture.ClearCachedData()` between calls to `DateTime.Now` produces the desired outcome in the test scenario from Comment 2.

I believe this bug can be marked as resolved fixed.


-Brendan
Xamarin Customer Support
Comment 8 Nate Cook 2015-06-22 11:29:31 UTC
It looks like that was the key. System.Globalization.CultureInfo.CurrentCulture.ClearCachedData() seems to do the trick.