I'm struggling with converting DateTime to UTC, the concept and all, something I'm not understanding correctly.
When I get a date time string, say "7/10/2013", I simply do
Convert.ToDateTime("7/10/2013").ToUniversalTime();
This will record it as "7/10/2013 4:00:00 AM" in the database. Server is located at U.S East Coast (-5). Of course, during July 2013, DST is still being observed, so offset during that time is -4, such the extra 4 hours 4:00:00 AM" recorded as UTC.
As I'm writing this post, it's Feb 2014 and DST is not in effect, so offset right now is -5. In my application, that's the offset I choose in my application.
If I apply -5 offset to "7/10/2013 4:00:00 AM", the date will be will "7/09/2013 11:00:00PM".
Which is wrong and off by one day.
Question #1
How then do I properly convert the UTC time back? Meaning, when a user loads my application right now in Feb 2014 (with timezone offset -5 currently), 7/10/2013 4:00:00AM should be still 7/10/2013, NOT 7/09/2013.
What confuses me is, since .ToUniversalTime() takes server DST into consideration, is there a hard set "universal time" that's not affected by locations of where the server is????
Question #2
What happens, when I have servers in both west and east coast, writing to the database? How can an application tell, if a UTC time being recorded is based on east or west coast?
basically, how then can the code tell, "7/10/2013 4:00:00 AM" is a UTC time created on the East coast (that indicates 7/10/2013 00:00:00AM for U.S East Coast) and not by a server on the west coast (that indicates it's 7/09/2013 20:00:00pm" for U.S West Coast)?
Sorry, if that sounds stupid. Any advice is appreciated it.
==========Final Edit, My current solution===============
MiMo's answer makes sense. I was confused with two things.
My application can be used by users from different timezone and some users are in the same timezone as the server, some are not. Some travels and so even if they were in the same timezone as the server, they might land in a different timzone all the time. My application allows them to choose which timezone they are in and such reflect the time appropriately.
Originally, I simply get the UTC time out of the database and subtract user's timezone offset from it. As Mimo suggested, it's wrong. The reason can be seen in my post above.
My original solution was to get server's timezone offset right now and use that to add/subtract from UTC, and that's wrong too. As on 7/10/2013, server's offset at the time was -4. Right now, in Feb 2014, server timezone offset is -5. Solution to that is of course is to use .ToLocalTime()
Before I dig deeper into Mimo's suggestion on how to use TimeZone.ToLocalTime(), here is what I did to temporarily fix the issue.
Get the UTC date out of the database and convert to .ToLocalTime, which is what the server shows. so to the server, 7/10/2013 4:00:00AM becomes 7/10/2013 12:00:00AM.
Get the server timezone offset. It shows -5 currently, since it's at U.S East Coast.
Get the user's timezone offset. For west coast, user choose -8 right now. For east coast, user choose -5 right now.
Get the difference between user's timezone and server's timezone. West coast is -3. East Coast is 0.
Subtract the differences from 7/10/2013 12:00:00AM, so west coast has a due date 7/09/2013 21:00:00PM and east coast has a due date 7/10/2013 12:00:00AM.
All correct.
Thanks a lot guys. Now time to dig into TimeZone.ToLocalTime() and see if I can reduce step 2-5.
To convert a JavaScript date object to a UTC string, you can use the toUTCString() method of the Date object. The toUTCString() method converts a date to a string, using the universal time zone. Alternatively, you could also use the Date. UTC() method to create a new Date object directly in UTC time zone.
new Date() returns relative time and not UTC time.
UTC() takes comma-delimited date and time parameters and returns the number of milliseconds between January 1, 1970, 00:00:00, universal time and the specified date and time. Years between 0 and 99 are converted to a year in the 20th century (1900 + year) . For example, 95 is converted to the year 1995 .
If you want to store a local time in your database as UTC, you need to first convert it to universal time:
DateTime dbDateTime = localDateTime.ToUniversalTime();
... store dbDateTime in the database ...
When you read it back from the database, it will have its Kind property set to Unspecified. You will need to explicitly set its Kind property to UTC:
dbDateTime = ... get from database, e.g. (DateTime) reader["SomeDateTimeColumn"]
dbDateTime = DateTime.SpecifyKind(dbDateTime, DateTimeKind.Utc);
If you then want to convert it to local time, you can use:
DateTime localDateTime = dbDateTime.ToLocalTime();
You convert back to local time using ToLocalTime()
. It will see that the date/time is in July, so with DST, and hence it will shift by 4 hours and not by 5.
If you have a client (e.g. Web browser) connecting to the server you will ultimately want the date/time converted to the local time of the client, not of the server. To do this the best way is to use TimeZone.ToLocalTime()
: send to the served the time-zone the client is in and then convert directly to that time-zone all the time.
NEVER add/subtract hours - always go through the time zone and use TimeZoone.ToLocalTime(). Adding/subtracting hours won't work when DST is involved.
Note that it is not possible to get the current (local) time-zone from inside a browser. If your client is a browser you need to get the time-zone from some sort of configuration or have the user enter it.
Note also that once you start to handle different time zones you no longer have just dates - you always have to deal with complete date-times: if you strip or loose the time part all the conversions won't work any more.
Concerning question 2: UTC time is universal, not based on any specific time zone, hence once you convert to UTC you don't have to worry any more about the time-zone of the servers.
I have had the same issue and i made the following extension methods.
public const string UTC = "UTC";
public static DateTime Convert(this DateTime date, string fromZone, string toZone)
{
TimeZoneInfo to = TimeZoneInfo.FindSystemTimeZoneById(toZone);
TimeZoneInfo from = TimeZoneInfo.FindSystemTimeZoneById(fromZone);
return new DateTime(TimeZoneInfo.ConvertTime(date, from, to).Ticks, DateTimeKind.Unspecified);
}
public static bool IsDayLightSaving(this DateTime date, string zone)
{
TimeZoneInfo to = TimeZoneInfo.FindSystemTimeZoneById(zone);
return to.IsDaylightSavingTime(date);
}
public static DateTime ConvertToUTC(this DateTime date, string fromZone)
{
return date.Convert(fromZone, DateTime_Extension.UTC);
}
public static DateTime ConvertToLocal(this DateTime date, string toZone)
{
return date.Convert(DateTime_Extension.UTC, toZone);
}
The problem I found is actually finding the current users Time Zone. As this is not passed in the Header (you can get it via JavaScript) I have decided to ask the user to select their time zones when they register
Hope this helps
Perhaps it's too early in the morning and I need another cup of coffee, but (at least for display) isn't the solution just using TimeZoneInfo.ConvertTimeFromUtc
and specifying the user's desired TZ? Or is the point that ConvertTimeFromUtc does not take into account the date/time at which the conversion back to the desired TZ is being performed? Thinking deeper: even if it does, does anyone know if it takes into account the edge case where the server has shifted to DST but the user's desired TZ has not yet?
Do you want to store the date in local time? If you only care ablut the date portion, then you probably want to force the parsed time to Utc before saving it to the database:
DateTime utcDate = DateTime.SpecifyKind(Convert.ToDateTime("7/10/2013"),DateTimeKind.Utc);
That way when it's saved to the database it's already in UTC and will not include the time component.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With