TimeZoneInfo.ConvertTimeToUtc issue - c#

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

Related

TimeZoneInfo conversion of DateTimeOffset fails

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.

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();

NodaTime Invalid DateTime.Kind for Instant.FromDateTimeUtc

I'm trying to get ahold of this timezone issue we are having. We would like to store all DateTimes in UTC, and then convert the DateTime to the user's timezone.
We decided to use NodaTime for this, as it seems like the right approach. However, we are experiencing an issue with it.
This is how we convert the DateTime to UTC (note - I hardcoded the usersTimeZone for now):
public static DateTime ConvertLocaltoUTC(this DateTime dateTime)
{
LocalDateTime localDateTime = LocalDateTime.FromDateTime(dateTime);
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
var usersTimezoneId = "Europe/Copenhagen";
var usersTimezone = timeZoneProvider[usersTimezoneId];
var zonedDbDateTime = usersTimezone.AtLeniently(localDateTime);
var returnThis = zonedDbDateTime.ToDateTimeUtc();
return zonedDbDateTime.ToDateTimeUtc();
}
And here is how we convert it back:
public static DateTime ConvertUTCtoLocal(this DateTime dateTime)
{
Instant instant = Instant.FromDateTimeUtc(dateTime);
IDateTimeZoneProvider timeZoneProvider = DateTimeZoneProviders.Tzdb;
var usersTimezoneId = "Europe/Copenhagen"; //just an example
var usersTimezone = timeZoneProvider[usersTimezoneId];
var usersZonedDateTime = instant.InZone(usersTimezone);
return usersZonedDateTime.ToDateTimeUnspecified();
}
However, when we convert it back to local time, it throws an exception:
Argument Exception: Invalid DateTime.Kind for Instant.FromDateTimeUtc
at the first line of ConvertUTCtoLocal().
An example of the DateTime could be: "9/18/2017 5:28:46 PM" - yes this has been through the ConvertLocalToUTC method.
Am I providing an incorrect format? What am I doing wrong here?
The exception you show:
Argument Exception: Invalid DateTime.Kind for Instant.FromDateTimeUtc
Is thrown from this code:
Instant instant = Instant.FromDateTimeUtc(dateTime);
It means that dateTime.Kind needs to be DateTimeKind.Utc to be convertible to an Instant, and for whatever reason it is not.
If you look at the result of your ConvertLocaltoUTC method, you'll find that it does have .Kind == DateTimeKind.Utc.
So, the problem lies elsewhere in your code, wherever you created the dateTime you're passing in to ConvertUTCtoLocal.
You may find the solution could be any of the following:
You might need to call DateTime.SpecifyKind to set the kind to UTC, but be careful to only do this when your values are actually UTC and it's just not setting the kind. For example, use this when loading a UTC-based DateTime from a database.
You might need to call .ToUniversalTime(), but be careful to only do this if the machine's local time zone is relevant to your situation. For example, do this in desktop or mobile apps where a UI control is picking a date, but you meant it to mean UTC instead of local time.
You might need to change how you parse strings into DateTime values, such as by passing DateTimeStyles.RoundTripKind to a DateTime.Parse call (or any of its variants. For example, do this if you are reading data from text, csv, etc.
If you want to avoid having to decide, don't write functions that take DateTime as input or give DateTime as output. Instead, use DateTimeOffset, or use Noda-Time types like Instant, LocalDateTime, etc. as early as possible.
This is what worked for me:
Instant instant = Instant.FromDateTimeUtc(DateTime.SpecifyKind(datetime, DateTimeKind.Utc));

How to convert a UTC DateTimeOffset to a DateTime that uses the systems timezone

Quartz.net offers a method to get the next time of the next trigger event: http://quartznet.sourceforge.net/apidoc/1.0/html/html/cc03bb79-c0c4-6d84-3d05-a17f59727c98.htm
The docs claim that this Trigger.GetNextFireTimeUtc() method return a DateTime? but it actually returns a DateTimeOffset?. I don't really get what DateTimeOffset is for or why this function return one instead of a regular DateTime. All I want is the next time the trigger is going to run but in my timezone.
I did this trigger.GetNextFireTimeUtc().Value.DateTime but it gave me a time 2 hours early, i.e. the UTC time. How can I get the correct time according to my computer?
You can just use the DateTimeOffset.LocalDateTime property:
trigger.GetNextFireTimeUtc().Value.LocalDateTime
From the documentation:
If necessary, the LocalDateTime property converts the current DateTimeOffset object's date and time to the local system's date and time. The conversion is a two-step operation:
The property converts the current DateTimeOffset object's time to Coordinated Universal Time (UTC).
The property then converts UTC to local time.
You should really look into DateTimeOffset though - it's an important type to understand if you're using the BCL for date/time work.
I bumped to this problem and none of these was real answer (by this question title). Solution examples for question title:
var myDateTimeOffset = (DateTimeOffset)DateTime.UtcNow;
var ans1 = myDateTimeOffset.DateTime.ToLocalTime();
var ans2 = myDateTimeOffset.DateTime.ToLocalTime().ToLocalTime(); // ans1==ans2
.NET5 C# 9.0
This code is to convert utc to local
var local = utc.ToLocalTime();

Converting UK times (both BST and GMT) represented as strings to UTC (in C#)

I have to use some dates and times from a legacy database. They are represented as strings. Dates are dd/MM/yy. Times are HH:mm.
I'd like to convert these to UTC as soon as I pull them from the database. I'm working on US systems, so need a common time.
The problem I'm facing is how to convert them to UTC DateTime values. I can do the parsing, etc. The real problem I have concerns the timezone.
I'm trying to use the following approach:
DateTime ukTime = // Parse the strings in a DateTime value.
TimeZoneInfo timeZoneInformation = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTimeOffset utcTime = new DateTimeOffset(ukTime, timeZoneInformation.BaseUtcOffset);
However, this gives incorrect values if the date is in the British Summer Time period.
I can use "GMT Daylight Time" on those dates, but that requires me to know when the switchover is. I'm sure there must be a less laborious way.
As I'm not using a machine with UK time settings I can't rely on local time.
Basically, I need something like:
// Works for both GMT (UTC+0) and BST (UTC+1) regardless of the regional settings of the system it runs on.
DateTime ParseUkTimeAsUtcTime(string date, string time)
{
...
}
I've scoured the posts, but couldn't find anything that addressed this directly. Surely this is also an issue with EST, EDT, etc?
Try using the GetUtcOffset() method on your TimeZoneInfo instance, which takes "adjustment rules" into consideration.
Using this should work basically the same as your original example, but you'll use that method instead of the BaseUtcOffset property.
DateTime ukTime = // Parse the strings in a DateTime value.
TimeZoneInfo timeZoneInformation = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTimeOffset utcTime = new DateTimeOffset(ukTime, timeZoneInformation.GetUtcOffset(ukTime));
How about:
DateTime.Parse(dateTimeString).ToUniversalTime();
Assuming that the database server stores its datetimes in the same timezone as your application server.

Categories

Resources