TimeZoneInfo conversion of DateTimeOffset fails - c#

In trying to convert a DateTimeOffset between two time zones, I've successfully used TimeZoneInfo.ConvertTime(), but I found a case where the conversion doesn't work. That is - conversion between UTC+11.00 and UTC+10:30 which has no effect.
Here is my completely vanilla source:
public static DateTimeOffset ConvertToNewTimeZone(this DateTimeOffset dt, string newTz)
{
var tzi = DeduceTimeZone(newTz); // converts out of proprietary format into std format
var result = TimeZoneInfo.ConvertTime(dt, tzi);
return result;
}
And here's a debug view of the values of my variables.
Notice how the result is still in UTC+11:00. I expected the result to be in 10:30. Can anyone explain why the conversion didn't happen? I'm not aware of any adjustments that might apply.

Because of the daylight saving time (DST), you may know that January is summer in the southern hemisphere where Lord Howe Island is located, if you try to convert a different date in winter, the offset will be 10:30.
var dt = new DateTime(2022, 7, 1);
TimeZoneInfo.ConvertTime(new DateTimeOffset(dt), tzi);
// 22/7/1 02:30:00 +10:30
TimeZoneInfo.GetUtcOffset
Time zone essentials

In the end I narrowed it down when I noticed that the implementation of the TimeZoneInfo in Windows is different from that in Linux and MacOS. My tests were inadvertently comparing local time (debugging on windows) with a different set of time zones (where I was logging from a Docker container running on AKS on Azure, which was a Linux container).
In the end, it appears that my time zone conversion defaulted to UTC, because the System Timezone Database is empty. That is, TimeZoneInfo.GetSystemTimeZones().Count == 0.
Not sure what to do about it, but at least I know now why my code was failing.

Related

Converting from CEST /CET date string to UTC date string when CEST timezone not installed on the machine

I need to parse some dates that are in CEST/CET, coming as strings from an external provider, and convert them to UTC.
My current culture on my computer is en-GB and I have the BST timezone installed on my machine, but I do not have CEST. Therefore I cannot use TimeZoneInfo to convert between time zones as I cannot instantiate the one for CEST.
How do I do this in C#? I have looked at similar questions here on StackOverflow and googled this but I cannot find a solution that works.
My code currently works with this, but I think it's a hack, still:
// dateToParse: "2018-09-04T19:17:37.022363"
var theDate = DateTime.ParseExact(dateToParse,
#"yyyy-MM-ddTHH\:mm\:ss\.ffffff",
new CultureInfo("sq-AL"), // this culture info is in CEST. Tried using CultureInfo.InvariantCulture as well - nothing changed
DateTimeStyles.AssumeLocal); // tried putting here DateTimeStyles.None
// Our local time is in BST.
// CEST and BST are always one hour apart so parsing date during daylight saving times will (probably) still work.
// I think. Except for that 1h window when the switch happens...
theDate = theDate.AddHours(-1); // I was hoping to not need this!
var utcDate = theDate.ToUniversalTime();
return utcDate;
So essentially I am looking for something that understands that the date is in CEST, or CET depending on the time of the year, and not in my current BST time, and knows how to turn that into UTC, factoring in things like daylight saving times.
I don't mind using a library - I have very very briefly looked at NodaTime but didn't find an obvious solution there (there very well might be but I didn't put in the time to reliably look for it).
Any help greatly appreciated.
After help from the comments and searching a bit more, this code seems to work well:
var theDate = DateTime.ParseExact(dateToParse,
#"yyyy-MM-ddTHH\:mm\:ss\.ffffff",
CultureInfo.InvariantCulture,
DateTimeStyles.None);
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time");
var utcDate = TimeZoneInfo.ConvertTimeToUtc(theDate, timeZoneInfo);
return utcDate;

Is there functionality to convert from UTC time to local time using country name? [duplicate]

We are developing a C# application for a web-service client. This will run on Windows XP PC's.
One of the fields returned by the web service is a DateTime field. The server returns a field in GMT format i.e. with a "Z" at the end.
However, we found that .NET seems to do some kind of implicit conversion and the time was always 12 hours out.
The following code sample resolves this to some extent in that the 12 hour difference has gone but it makes no allowance for NZ daylight saving.
CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);
As per this date site:
UTC/GMT Offset
Standard time zone: UTC/GMT +12 hours
Daylight saving time: +1 hour
Current time zone offset: UTC/GMT +13 hours
How do we adjust for the extra hour? Can this be done programmatically or is this some kind of setting on the PC's?
For strings such as 2012-09-19 01:27:30.000, DateTime.Parse cannot tell what time zone the date and time are from.
DateTime has a Kind property, which can have one of three time zone options:
Unspecified
Local
Utc
NOTE If you are wishing to represent a date/time other than UTC or your local time zone, then you should use DateTimeOffset.
So for the code in your question:
DateTime convertedDate = DateTime.Parse(dateStr);
var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified
You say you know what kind it is, so tell it.
DateTime convertedDate = DateTime.SpecifyKind(
DateTime.Parse(dateStr),
DateTimeKind.Utc);
var kind = convertedDate.Kind; // will equal DateTimeKind.Utc
Now, once the system knows its in UTC time, you can just call ToLocalTime:
DateTime dt = convertedDate.ToLocalTime();
This will give you the result you require.
I'd look into using the System.TimeZoneInfo class if you are in .NET 3.5. See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx. This should take into account the daylight savings changes correctly.
// Coordinated Universal Time string from
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z";
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date);
DateTime utcDateTime = localDateTime.ToUniversalTime();
// ID from:
// "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);
TimeZone.CurrentTimeZone.ToLocalTime(date);
DateTime objects have the Kind of Unspecified by default, which for the purposes of ToLocalTime is assumed to be UTC.
To get the local time of an Unspecified DateTime object, you therefore just need to do this:
convertedDate.ToLocalTime();
The step of changing the Kind of the DateTime from Unspecified to UTC is unnecessary. Unspecified is assumed to be UTC for the purposes of ToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx
I know this is an older question, but I ran into a similar situation, and I wanted to share what I had found for future searchers, possibly including myself :).
DateTime.Parse() can be tricky -- see here for example.
If the DateTime is coming from a Web service or some other source with a known format, you might want to consider something like
DateTime.ParseExact(dateString,
"MM/dd/yyyy HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)
or, even better,
DateTime.TryParseExact(...)
The AssumeUniversal flag tells the parser that the date/time is already UTC; the combination of AssumeUniversal and AdjustToUniversal tells it not to convert the result to "local" time, which it will try to do by default. (I personally try to deal exclusively with UTC in the business / application / service layer(s) anyway. But bypassing the conversion to local time also speeds things up -- by 50% or more in my tests, see below.)
Here's what we were doing before:
DateTime.Parse(dateString, new CultureInfo("en-US"))
We had profiled the app and found that the DateTime.Parse represented a significant percentage of CPU usage. (Incidentally, the CultureInfo constructor was not a significant contributor to CPU usage.)
So I set up a console app to parse a date/time string 10000 times in a variety of ways. Bottom line:
Parse() 10 sec
ParseExact() (converting to local) 20-45 ms
ParseExact() (not converting to local) 10-15 ms
... and yes, the results for Parse() are in seconds, whereas the others are in milliseconds.
I'd just like to add a general note of caution.
If all you are doing is getting the current time from the computer's internal clock to put a date/time on the display or a report, then all is well. But if you are saving the date/time information for later reference or are computing date/times, beware!
Let's say you determine that a cruise ship arrived in Honolulu on 20 Dec 2007 at 15:00 UTC. And you want to know what local time that was.
1. There are probably at least three 'locals' involved. Local may mean Honolulu, or it may mean where your computer is located, or it may mean the location where your customer is located.
2. If you use the built-in functions to do the conversion, it will probably be wrong. This is because daylight savings time is (probably) currently in effect on your computer, but was NOT in effect in December. But Windows does not know this... all it has is one flag to determine if daylight savings time is currently in effect. And if it is currently in effect, then it will happily add an hour even to a date in December.
3. Daylight savings time is implemented differently (or not at all) in various political subdivisions. Don't think that just because your country changes on a specific date, that other countries will too.
#TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
Don't forget if you already have a DateTime object and are not sure if it's UTC or Local, it's easy enough to use the methods on the object directly:
DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();
How do we adjust for the extra hour?
Unless specified .net will use the local pc settings. I'd have a read of: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx
By the looks the code might look something like:
DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );
And as mentioned above double check what timezone setting your server is on. There are articles on the net for how to safely affect the changes in IIS.
In answer to Dana's suggestion:
The code sample now looks like:
string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);
The original date was 20/08/08; the kind was UTC.
Both "convertedDate" and "dt" are the same:
21/08/08 10:00:26; the kind was local
I had the problem with it being in a data set being pushed across the wire (webservice to client) that it would automatically change because the DataColumn's DateType field was set to local. Make sure you check what the DateType is if your pushing DataSets across.
If you don't want it to change, set it to Unspecified
I came across this question as I was having a problem with the UTC dates you get back through the twitter API (created_at field on a status); I need to convert them to DateTime. None of the answers/ code samples in the answers on this page were sufficient to stop me getting a "String was not recognized as a valid DateTime" error (but it's the closest I have got to finding the correct answer on SO)
Posting this link here in case this helps someone else - the answer I needed was found on this blog post: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - basically use DateTime.ParseExact with a format string instead of DateTime.Parse
This code block uses universal time to convert current DateTime object then converts it back to local DateTime. Works perfect for me I hope it helps!
CreatedDate.ToUniversalTime().ToLocalTime();

TimeZoneInfo.ConvertTimeToUtc issue

We had an issue where one developer creates the below code and it works on his DEV environment. But when it's checked into QA, the code breaks with the below error message:
myRecord.UTCStartTime = TimeZoneInfo.ConvertTimeToUtc(myRecord.StartTime, myTimeZone);
The conversion could not be completed because the supplied DateTime
did not have the Kind property set correctly. For example, when the
Kind property is DateTimeKind.Local, the source time zone must be
TimeZoneInfo.Local.
On my DEV environment, the above code generates the same error as the QA server. I applied the below change to fix the problem:
DateTime utcStart = DateTime.SpecifyKind(myRecord.StartTime, DateTimeKind.Unspecified);
myRecord.UTCStartTime = TimeZoneInfo.ConvertTimeToUtc(utcStart, myTimeZone);
Why does the first code example work on DEV1's environment but break on my DEV environment and on our QA server?
It depends on how the myRecord.StartTime was originated.
If you got it from DateTime.Now, then it will have a Local kind.
If you got it from DateTime.UtcNow then it will have an Utc kind.
If you got it from new DateTime(2013,5,1) then it will have an Unspecified kind.
It also depends on where you got myTimeZone from. For example:
TimeZoneInfo.Local
TimeZoneInfo.Utc
TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")
The TimeZoneInfo.ConvertTimeToUtc function will only operate if it can match the zone to the kind you give it. If both are local, or both are UTC, then it will work. If you are giving it a specific zone, then the kind should be unspecified. This behavior is documented on MSDN.
You can easily reproduce the exception consistently:
var tz = TimeZoneInfo.FindSystemTimeZoneById("Fiji Standard Time");
var utc = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now, tz);
Assuming you don't live in Fiji, this will error every time. You basically said, "convert my local time, in some other zone, to utc" - which doesn't make sense.
It probably works in your dev environment because the value you're testing for myTimeZone happens to be the local zone for the developer.
Regarding your change - sure you can force the kind to be unspecified, and that changes the meaning of what you are doing such that it makes sense. But are you sure this is what you want? What is the .Kind of the date before hand? If it's not already Unspecified, then it is carrying some intent. You should probably go back to the source of this data and make sure it is what you expect.
If all of this sounds crazy, mad, frustrating, and bizarre, it's because the DateTime object stinks. Here's some additional reading:
What's wrong with DateTime anyway?
The case against DateTime.Now
You might consider using NodaTime instead. Its API will prevent you from making these types of common mistakes.
in this example i have converted the local timezone to unspecified type so it is working fine for me by using "DateTime.SpecifyKind()" method
DateTime.SpecifyKind(utc,DateTimeKind.Unspecified);
this method Creates a new DateTime object that has the same number of ticks as the specified DateTime, but is designated as unspecified type of DateTimeKind.
public static DateTime ConvertLocalDate(DateTime utc)
{
string id = ConfigurationManager.AppSettings["Timezone"].ToString();
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById(id);
utc = DateTime.SpecifyKind(utc,DateTimeKind.Unspecified);
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(utc, cstZone);
return cstTime;
}
I found a really simple solution here
https://kiranpatils.wordpress.com/2011/01/09/the-conversion-could-not-be-completed-because-the-supplied-datetime-did-not-have-the-kind-property-set-correctly-for-example-when-the-kind-property-is-datetimekind-local-the-source-time-zone-must/
Which appears only to be happening when you use DateTime.Now. I update my code like following and it is working again :)
DateTime currentTime = new DateTime(DateTime.Now.Ticks, DateTimeKind.Unspecified);
Basically what was said in previous answers, but to trim it down:
DateTime.Now or DateTime.Today sets the Kind to Local, so change this to Unspecified
In the method that handles the dates:
// Incoming date has DateTimeKind.Local
var localDateTime = DateTime.Now;
// Set to unspecified
var unspecifiedDateTime = DateTime.SpecifyKind(localDateTime, DateTimeKind.Unspecified);
I was only receiving this when deployed, then realised a library was passing DateTime.Now rather than new DateTime(...) to a method.
The accepted answer does a great job explaining the cause. But I'm still adding my 2 cents for completeness:
When serializing DateTime objects, some serializers will mess up the Kind of your DateTime properties. We've had this problem when caching POCO objects in Redis using ProtoBuf and MessagePack serializers.
So, if you know that's the case, then it's perfectly fine to force the kind of your datetime variable to "Unspecified" like this:
myDateTime = DateTime.SpecifyKind(myDateTime, DateTimeKind.Unspecified);
in C#
public static DateTime IndianDateTime(DateTime currentTime)
{
DateTime cstTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentTime, TimeZoneInfo.Local.Id, "India Standard Time");
return cstTime;
}
In VB
Public Shared Function IndianDateTime(ByVal currentTime As DateTime) As DateTime
Dim cstTime As DateTime = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentTime, TimeZoneInfo.Local.Id, "India Standard Time")
Return cstTime
End Function

How do I stop a date/time comparison from failing when a user is in a different time zone?

I am getting the "LastWriteTime" of my executable and comparing it to an internal DateTime that I have set. If the LastWriteTime is less than or equal to the internal DateTime then I will clear two tables from a database.
This code works great for me in the Pacific Time Zone. But if a user is in another time zone, (example 4 hours ahead of me), then it does not work because the "LastWriteTime" returns the time converted to their time zone. For example, I am looking for the value of "12/12/2012 8:38:12 AM" and if they are 4 hours ahead of me, this value gets automatically changed to "12/12/2012 12:38:12 PM" on their systems.
Can someone please show me what I should modify in my code to take into account for different time zones so the "LastWriteTime" and my 'build2023_EXE_Date' variable both return the same Date/Time so my comparision of the two date/time values don't fail regardless of what time zone my end user is in?
I am using .NET 3.5, not .Net 4.x
//http://stackoverflow.com/questions/1600962/displaying-the-build-date
string w_file = "MyEXE.exe";
string w_directory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) +
Path.DirectorySeparatorChar + "MyEXE";
DateTime currentExeTime = File.GetLastWriteTime(System.IO.Path.Combine(w_directory, w_file));
DateTime build2023_EXE_Date = new DateTime(2012, 12, 12, 8, 38, 12); //"12/12/2012 8:38:12 AM"
//We need to truncate the millisecond time off of the EXE LastWriteTime
//or else when we compare to our internal DateTime build2323_EXE_Date value,
//it will not match
//http://stackoverflow.com/questions/1004698/how-to-truncate-milliseconds-off-of-a-net-datetime
currentExeTime = new DateTime(
currentExeTime.Ticks - (currentExeTime.Ticks % TimeSpan.TicksPerSecond),
currentExeTime.Kind
);
if (currentExeTime <= build2023_EXE_Date) //If previous build matches or is before the Build 2023 date then clear these two tables.
{
//This will fail the comparision if the user is in a different time zone than me.
//Clear tables
}
Unless you've got a specific need to keep dates in local time or have an associated time zone, I suggest you use universal time instead. This makes working with dates far easier because they all compare sanely, and it can actually be more performant (when you request DateTime.Now, .NET calls DateTime.UtcNow and then performs a relatively expensive adjustment to local time).
Another option is to use DateTimeOffset, which stores a date with an offset (not a time zone! -- for instance, DST will give you different offsets) and makes comparisons as easy as a universal DateTime. Unfortunately, though, GetLastWriteTime doesn't use DateTimeOffset so this might not work for you.
Use the DateTime ToUniversalTime() method

How to convert Datetimes into UTC based timing values to compare timings- C#

I've string (variable is fileDate) with Date values in the following format:
2/12/2011 11:58 AM
Now I want to convert this to a date and then to UTC time based as I've problems in comparing dates in different machines, so *I always want to convert all strings (which are getting compared) to Utc_date values.*
I tried this code below but it did not work as I'm not able to convert the above string to Datetime based (as it does not have seconds).
DateTime date = Convert.ToDateTime(fileDate);
date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
fileDate = date.ToString("MM/dd/yyyy hh:mm tt");
Above did not work showing FormatException.
Can you pl help?
To start with, I'd suggest using DateTime.ParseExact or TryParseExact - it's not clear to me whether your sample is meant to be December 2nd or February 12th. Specifying the format may well remove your FormatException.
The next problem is working out which time zone you want to convert it with - are you saying that 11:58 is a local time in some time zone, or it's already a UTC time?
If it's a local time in the time zone of the code which is running this, you can use DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal to do it as part of parsing.
If it's already a universal time, use DateTimeStyles.AssumeUniversal
If it's a local time in a different time zone, you'll need to use TimeZoneInfo to perform the conversion.
Also, if it's a local time you'll need to consider two corner cases (assuming you're using a time zone which observes daylight saving time):
A local time may be skipped due to DST transitions, when the clocks go forward. So if the clocks skip from 1am to 2am, then 1:30am doesn't exist at all.
A local time may be ambiguous due to DST transitions, when the clocks go back. So if the clocks go back from 2am to 1am, then 1:30am occurs twice at different UTC times - which occurrence are you interested in?
You should decide how you want to handle these cases, and make sure they're covered in your unit tests.
Another option is to use my date and time library, Noda Time, which separates the concepts of "local date/time" and "date/time in a particular time zone" (and others) more explicitly.
you should be using DateTime.ParseExact to get the value into a proper DateTime instance, and then you can use .ToUniversalTime() to get the UTC time (this would be with respect to the difference of time as in your server machine)
you can use :
DateTime.Now.ToUniversalTime();
i don't mean to say to you should use "DateTime.Now" but you get the point that as a part of the DateTime object you have a method to transform it to Universal time
http://msdn.microsoft.com/en-us/library/system.datetime.touniversaltime.aspx

Categories

Resources