TimeZone.CurrentTimeZone not updated when machine's time zone changes.
-
I have a C# windows service that depends on time comparisons that is encountering problems due to the time zone on the machine being changed. Unfortunately, this time zone change is outside of my control. I use the TimeZone.CurrentTimeZone.ToUniversalTime() and TimeZone.CurrentTimeZone.GetUtcOffset() methods to make sure all my times are converted and compared as UTC. The problem is that TimeZone.CurrentTimeZone is not updating when the machine's time zone has changed. You can reproduce this easily by creating a console app with the following lines of code, and manually changing your machine's time zone during the app's pause.
Console.WriteLine("Current time zone is {0}.", TimeZone.CurrentTimeZone.StandardName); Console.Write("Waiting for time zone change. Hit 'Enter' to continue ..."); Console.ReadLine(); // manually change time zone here before app continues Console.WriteLine("Current time zone is {0}.", TimeZone.CurrentTimeZone.StandardName);
Does anyone have any idea about how to refresh or update the TimeZone.CurrentTimeZone? Thanks! -
I have a C# windows service that depends on time comparisons that is encountering problems due to the time zone on the machine being changed. Unfortunately, this time zone change is outside of my control. I use the TimeZone.CurrentTimeZone.ToUniversalTime() and TimeZone.CurrentTimeZone.GetUtcOffset() methods to make sure all my times are converted and compared as UTC. The problem is that TimeZone.CurrentTimeZone is not updating when the machine's time zone has changed. You can reproduce this easily by creating a console app with the following lines of code, and manually changing your machine's time zone during the app's pause.
Console.WriteLine("Current time zone is {0}.", TimeZone.CurrentTimeZone.StandardName); Console.Write("Waiting for time zone change. Hit 'Enter' to continue ..."); Console.ReadLine(); // manually change time zone here before app continues Console.WriteLine("Current time zone is {0}.", TimeZone.CurrentTimeZone.StandardName);
Does anyone have any idea about how to refresh or update the TimeZone.CurrentTimeZone? Thanks!I think I found a work-around, but I don't fully like it. If I call the GetSystemTime WinAPI function, it seems to always return the correct UTC time. I got some decent sample code from Anson Goldade's GotDotNet user sample to nicely encapsulate the API calls. Too bad Microsoft's framework methods don't call the API correctly.
-
I have a C# windows service that depends on time comparisons that is encountering problems due to the time zone on the machine being changed. Unfortunately, this time zone change is outside of my control. I use the TimeZone.CurrentTimeZone.ToUniversalTime() and TimeZone.CurrentTimeZone.GetUtcOffset() methods to make sure all my times are converted and compared as UTC. The problem is that TimeZone.CurrentTimeZone is not updating when the machine's time zone has changed. You can reproduce this easily by creating a console app with the following lines of code, and manually changing your machine's time zone during the app's pause.
Console.WriteLine("Current time zone is {0}.", TimeZone.CurrentTimeZone.StandardName); Console.Write("Waiting for time zone change. Hit 'Enter' to continue ..."); Console.ReadLine(); // manually change time zone here before app continues Console.WriteLine("Current time zone is {0}.", TimeZone.CurrentTimeZone.StandardName);
Does anyone have any idea about how to refresh or update the TimeZone.CurrentTimeZone? Thanks!The problem is that the
CurrentTimeZone
property caches the result for the lifetime of theAppDomain
, so you won't see any changes until yourAppDomain
is restarted. The simplest option is to use reflection to create a new instance of the internalCurrentSystemTimeZone
class. The following code will cache the current time zone for 5 minutes, and allow you to manually refresh the time zone as well:using System;
using System.Reflection;public sealed class CurrentTimeZone
{
private const int RefreshAfterMinutes = 5;
private static readonly object _lockMe = new object();
private static readonly Type _timeZoneType;
private static readonly ConstructorInfo _timeZoneConstructor;
private static TimeZone _instance = TimeZone.CurrentTimeZone;
private static DateTime _instanceCreated = DateTime.UtcNow;static CurrentTimeZone() { \_timeZoneType = TimeZone.CurrentTimeZone.GetType(); \_timeZoneConstructor = \_timeZoneType.GetConstructor( BindingFlags.Instance | BindingFlags.NonPublic, null, new Type\[0\], null); } private static TimeZone CreateInstance() { return (TimeZone)\_timeZoneConstructor.Invoke(null); } private static void UpdateIfStale() { TimeSpan age = \_instanceCreated - DateTime.UtcNow; if (age.TotalMinutes > RefreshAfterMinutes) { \_instance = CreateInstance(); \_instanceCreated = DateTime.UtcNow; } } public static TimeZone Instance { get { lock(\_lockMe) { UpdateIfStale(); return \_instance; } } } public static void Refresh() { lock(\_lockMe) { \_instance = CreateInstance(); \_instanceCreated = DateTime.UtcNow; } }
}
All you need to do is replace each instance of
TimeZone.CurrentTimeZone
withCurrentTimeZone.Instance
, and you should see the changes within 5 minutes.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
The problem is that the
CurrentTimeZone
property caches the result for the lifetime of theAppDomain
, so you won't see any changes until yourAppDomain
is restarted. The simplest option is to use reflection to create a new instance of the internalCurrentSystemTimeZone
class. The following code will cache the current time zone for 5 minutes, and allow you to manually refresh the time zone as well:using System;
using System.Reflection;public sealed class CurrentTimeZone
{
private const int RefreshAfterMinutes = 5;
private static readonly object _lockMe = new object();
private static readonly Type _timeZoneType;
private static readonly ConstructorInfo _timeZoneConstructor;
private static TimeZone _instance = TimeZone.CurrentTimeZone;
private static DateTime _instanceCreated = DateTime.UtcNow;static CurrentTimeZone() { \_timeZoneType = TimeZone.CurrentTimeZone.GetType(); \_timeZoneConstructor = \_timeZoneType.GetConstructor( BindingFlags.Instance | BindingFlags.NonPublic, null, new Type\[0\], null); } private static TimeZone CreateInstance() { return (TimeZone)\_timeZoneConstructor.Invoke(null); } private static void UpdateIfStale() { TimeSpan age = \_instanceCreated - DateTime.UtcNow; if (age.TotalMinutes > RefreshAfterMinutes) { \_instance = CreateInstance(); \_instanceCreated = DateTime.UtcNow; } } public static TimeZone Instance { get { lock(\_lockMe) { UpdateIfStale(); return \_instance; } } } public static void Refresh() { lock(\_lockMe) { \_instance = CreateInstance(); \_instanceCreated = DateTime.UtcNow; } }
}
All you need to do is replace each instance of
TimeZone.CurrentTimeZone
withCurrentTimeZone.Instance
, and you should see the changes within 5 minutes.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Unfortunately, I need to know the time zone at the exact moment that the code runs, and even 5 minutes late could cause issues. Anyway, while it's pretty cool, do you really think that your class is the simplest way to fix the problem when you can just call the GetSystemTime API?
-
Unfortunately, I need to know the time zone at the exact moment that the code runs, and even 5 minutes late could cause issues. Anyway, while it's pretty cool, do you really think that your class is the simplest way to fix the problem when you can just call the GetSystemTime API?
If you just need the current UTC system time, you can simply call
DateTime.UtcNow
, which will return the same time as calling theGetSystemTime
API. From your initial post, it sounded like you need the current time zone information as well, in which case the code I posted will be the simplest solution. If you want the current time zone information without any caching, you can simply remove the caching code and always return a new instance:using System;
using System.Reflection;public sealed class CurrentTimeZone
{
private static readonly Type _timeZoneType;
private static readonly ConstructorInfo _timeZoneConstructor;static CurrentTimeZone() { \_timeZoneType = TimeZone.CurrentTimeZone.GetType(); \_timeZoneConstructor = \_timeZoneType.GetConstructor( BindingFlags.Instance | BindingFlags.NonPublic, null, new Type\[0\], null); } private static TimeZone CreateInstance() { return (TimeZone)\_timeZoneConstructor.Invoke(null); } public static TimeZone Instance { get { return CreateInstance(); } }
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
If you just need the current UTC system time, you can simply call
DateTime.UtcNow
, which will return the same time as calling theGetSystemTime
API. From your initial post, it sounded like you need the current time zone information as well, in which case the code I posted will be the simplest solution. If you want the current time zone information without any caching, you can simply remove the caching code and always return a new instance:using System;
using System.Reflection;public sealed class CurrentTimeZone
{
private static readonly Type _timeZoneType;
private static readonly ConstructorInfo _timeZoneConstructor;static CurrentTimeZone() { \_timeZoneType = TimeZone.CurrentTimeZone.GetType(); \_timeZoneConstructor = \_timeZoneType.GetConstructor( BindingFlags.Instance | BindingFlags.NonPublic, null, new Type\[0\], null); } private static TimeZone CreateInstance() { return (TimeZone)\_timeZoneConstructor.Invoke(null); } public static TimeZone Instance { get { return CreateInstance(); } }
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Thanks, this code will be very useful to me. However, I just want to point out that in my tests DateTime.UtcNow doesn't return the correct time because of the time zone caching. You can try it yourself by using a test similar to the first one I posted.
-
Thanks, this code will be very useful to me. However, I just want to point out that in my tests DateTime.UtcNow doesn't return the correct time because of the time zone caching. You can try it yourself by using a test similar to the first one I posted.
That's very strange. The
DateTime.UtcNow
property simply calls theGetSystemTimeAsFileTime
API to get the current UTC system time. There is no caching involved, so the result should be the same as calling theGetSystemTime
API directly.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer