UTC date.ToLocalTime() still produces date in UTC time - c#

I'm working with an ASP.Net backend. I'm saving all my dates from the client side to the database in UTC time.
I have a function in the backend that exports some records and I would like to convert the dates extracted, from UTC time to the user's local time before displaying them.
I have tried tons of solutions proposed here on StackOverflow, but none of them seem to convert the date to local time, even though this works when displaying some of these dates on the client.I suspect the server already thinks the date is in local time, but I'm not sure how else to solve it.
Below are the different solutions I have tried:
//1.
var alertTime = record.TimeRecorded.GetValueOrDefault().ToLocalTime().ToString("hh:mm tt");
// 2.
var alertTime = record.TimeRecorded;
if (alertTime.HasValue)
{
var timeInUtc = TimeZoneInfo.ConvertTimeToUtc(alertTime.Value);
string alertTimeToLocalTime = timeInUtc.ToLocalTime().ToString("hh:mm tt");
}
//alertTimeToLocalTime is still in UTC time here
// 3.
if (alertTime.HasValue)
{
var localTime = TimeZoneInfo.ConvertTimeFromUtc(timeInUtc, TimeZoneInfo.Local);
string alertTimeToLocalTime = localTime.ToString("hh:mm tt");
}
None of these have managed to convert the alertTime to local time.
Am I missing something?
EDIT
//4. Another approach I had already tried which didn't work as well
var alertTime = DateTime.SpecifyKind(record.TimeRecorded.GetValueOrDefault(), DateTimeKind.Utc);
alertTimeToLocalTime = alertTime.ToLocalTime().ToString("hh:mm tt");

You said:
... from UTC time to the user's local time ...
Nothing in ASP.Net will tell you the user's local time zone. Calling ToLocalTime will convert from UTC to the server's time zone (unless the .Kind is already DateTimeKind.Local).
In many cases, the best practice of setting the server's time zone to UTC will mean that you will see no change with ToLocalTime or ToUniversalTime, other than the kind. And since the server's time zone is irrelevant in most cases, this is not the correct approach.
Instead, you either need to know the user's time zone through some other mechanism (such as them selecting it in your application) so you can use the TimeZoneInfo.ConvertTime (or Noda Time) to convert server-side, or you need to send UTC time down to the client and do the utc-to-local in JavaScript (since the browser is running in the user's time zone).
In general, any use of "local time" in a server application (such as ASP.NET) should be avoided. This includes ToLocalTime, ToUniversalTime, DateTimeKind.Local, TimeZoneInfo.Local, DateTime.Now, and a few other miscellaneous things.

It's because your DateTime's Kind is either Local or Unspecified. See the documentation for ToLocalTime():
Starting with the .NET Framework version 2.0, the value returned by the ToLocalTime method is determined by the Kind property of the current DateTime object. The following table describes the possible results.
Utc - This instance of DateTime is converted to local time.
Local - No conversion is performed.
Unspecified - This instance of DateTime is assumed to be a UTC time, and the conversion is performed as if Kind were Utc.
You can use the SpecifyKind method to set the Kind prior to doing the conversion.

Related

Change server time to local time that is sent by Facebook

I am using Facebook C# SDK to get some data from my Facebook page.
When I get objects of data, at that time I get one field:
created_time : 2014-05-23T11:55:00+0000
As it is not same as my current time how do I convert it to my current time or to standard UTC time?
For me the code:
DateTime.Parse("2014-05-23T11:55:00+0000").ToString()
returns
2014-05-23 12:55:00
Because 'DateTime.Parse()` understands the timezone in this string, and by default produces a local time. My timezone being Irish Summer Time, one hour ahead of UTC, that means 2014-05-23T12:55:00 for me, for you it'll be a different time depending on your timezone.
For the converse, to get the time parsed in UTC (more useful for behind-the-scenes web stuff or storage, rather than user interface), use DateTime.Parse("2014-05-23T11:55:00+0000").ToUniversalTime()
If you need to deal with timezones other than local to the machine the code is running on, or UTC, then you will need to use DateTimeOffset, with the exception that DateTimeParse will handle other timezones in the string, but from that point on only has the concepts of "local time", "universal time" and "unknown timezone".
I would use DateTimeOffset.ParseExact, specifying the format string an the invariant culture:
DateTimeOffset value = DateTimeOffset.ParseExact(text, "yyyy-MM-dd'T'HH:mm:ssK",
CultureInfo.InvariantCulture);
I strongly recommend this over using DateTime.Parse for the following reasons:
This always uses the invariant culture. It's explicitly stating that you don't want to use the local culture, which might have a different default calendar system etc
This specifies the format exactly. If the data becomes "10/06/2014 11:55:00" you will get an exception, which is better than silently guessing whether this is dd/MM/yyyy or MM/dd/yyyy
It accurately represents the data in the string: a date/time and an offset. You can then do whatever you want with that data, but separating the two steps is clearer
The first two of these can be fixed by using DateTime.ParseExact and specifying the culture as well, of course.
You can then convert that to your local time zone, or to any other time zone you want, including your system local time zone. For example:
DateTimeOffset localTime = value.ToLocalTime(); // Applies the local time zone
or if you want a DateTime:
DateTimeOffset localTime = value.ToLocalTime().DateTime;
It's just one extra line of code, but it makes it clearer (IMO) what you're trying to do. It's also easier to compare DateTimeOffset values without worrying about what "kind" they are, etc.
In fact, I wouldn't personally use DateTimeOffset or DateTime - I'd use OffsetDateTime from my Noda Time project, but that's a different matter.

Correct insert DateTime from c# to mongodb

I try to insert local time in MongoDB
var time = DateTime.Now; // 03.05.2014 18:30:30
var query = new QueryDocument
{
{ "time", nowTime}
};
collection3.Insert(query);
But in database I see ISODate("2014-05-03T15:30:30.170Z"),
that must be ISODate("2014-05-03T18:30:30.300Z").
Please help me!
I think you're getting confused by time zones. The Z at the end of the string indicates that it's in UTC. When you posted this question, it was just after 15:30 UTC.
I strongly suspect that the correct instant in time is being recorded - but it's being recorded as an instant in time without reference to a particular time zone. You can then convert that to whatever time zone you want later on, but recording the UTC time is almost always the correct approach.
As an aside, you can make this clearer by using UtcNow to start with. That way it's more obvious that you're not trying to obtain a "local" time.
Looking at the MongoDB documentation, it seems that the internal representation is simply a number of milliseconds since the Unix epoch - so again, that has no indication of time zone or an offset between UTC and local time. If you want to store a value which can be converted back to the local time you saw when it was recorded (even if you're now in a different time zone) you should store a time zone ID and/or the UTC offset as a separate value. That's not needed terribly often, but it's an option.
The MongoDB driver is converting your DateTime to UTC.
If you don't want to bother with time zones, you could change the kind to UTC:
time = DateTime.SpecifyKind(time, DateTimeKind.Utc);
I would not recommed this because the database will store a "wrong" Date, but it will show you ISODate("2014-05-03T18:30:30.170Z").
Just define the "Kind" of the DateTime by the "BsonDateTimeOptions" attribute and set it to local:
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime SomeDateProperty {get;set;}
It could work with:
DateTime t = DateTime.Now;
var update = Builders<BsonDocument>.Update
.Set("key1", t.ToUniversalTime())
.CurrentDate("key_updatetime");

Date time time Convertion From UTC to Local Twice

I want to convert my time to universal time when I store date time in database and convert back to system time when I retrieve from the database.
Both of them I have done it success fully by creating functions ConvertToLocalTime()
and ConvertToUniversalTime().
The code for convert to local time is given below:
public void ConvertToLocalTime()
{
string szTimezoneinfoid = TimeZoneInfo.Local.Id.ToString();
TimeZoneInfo tzInfo = TimeZoneInfo.FindSystemTimeZoneById(szTimezoneinfoid);
this.m_dtCreatedDate = TimeZoneInfo.ConvertTimeFromUtc(m_dtCreatedDate, tzInfo);
}
When I call a function Shipment.ConvertToLocalTime() it changes the shipment created date to local time till this it works fine.
Now if I call the Shipment.ConvertToLocalTime() again then it consider the local time again as UTC and convert it to local time.
Is there any way to block the conversion to local time if it is already in local time?
Does there any way that we can get the time zone of the datetime?
The result of TimeZoneInfo.ConvertTimeFromUtc is a DateTime with a Kind of Unspecified, unless the time zone itself is UTC.
When you pass that result into TimeZoneInfo.ConvertTimeFromUtc, it will assume you understand it to be UTC DateTime - otherwise you wouldn't be calling ConvertTimeFromUtc...
Basically, DateTime.Kind is a hack to try to work around the problem that DateTime tries to represent too many different things. Your method starts with potentially ambiguous data, and ends up with ambiguous data... which doesn't bode well.
Either you could just make sure you always know what you're doing, and avoid calling your method with its own result, or you could consider using Noda Time, my alternative .NET date/time API which avoids this sort of thing by having different data types to represent different kinds of data.
I'm not sure exactly what's going wrong in your example, but if you simply want to get a date/time you know to be UTC from the database and convert it to local time, you can do the following:
var dbTime = //whatever, from database.
var utcTime = DateTime.SpecifyKind(dbTime, DateTimeKind.Utc);
var localTime = utcTime.ToLocalTime();
In my own project, I check dbTime.Kind != DateTimeKind.Local first, jut in case my data reader has decided to start performing the conversion for me.

Having problems with converting my DateTime to UTC

I am storing all my dates in UTC format in my database. I ask the user for their timezone and I want to use their time zone plus what I am guessing is the server time to figure out the UTC for them.
Once I have that I want to do a search to see what the range is in the database using their newly converted UTC date.
But I always get this exception.
System.ArgumentException was unhandled by user code
Message="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.
Parameter name: sourceTimeZone"
I don't know why I am getting this.
I tried 2 ways
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(id);
// I also tried DateTime.UtcNow
DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone );
This failed so I tried
DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
var utc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now,
ZoneId, TimeZoneInfo.Utc.Id);
This also failed with the same error. What am I doing wrong?
Edit Would this work?
DateTime localServerTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(id);
var usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);
var utc = TimeZoneInfo.ConvertTimeToUtc(usersTime, userInfo);
Edit 2 # Jon Skeet
Yes, I was just thinking about that I might not even need to do all this. Time stuff confuses me right now so thats why the post may not be as clear as it should be. I never know what the heck DateTime.Now is getting (I tried to change my Timezone to another timezone and it kept getting my local time).
This is what I wanted to achieve: User comes to the site, adds some alert and it gets saved as utc (prior it was DateTime.Now, then someone suggested to store everything UTC).
So before a user would come to my site and depending where my hosting server was it could be like on the next day. So if the alert was said to be shown on August 30th (their time) but with the time difference of the server they could come on August 29th and the alert would be shown.
So I wanted to deal with that. So now I am not sure should I just store their local time then use this offset stuff? Or just store UTC time. With just storing UTC time it still might be wrong since the user still probably would be thinking in local time and I am not sure how UTC really works. It still could end up in a difference of time.
Edit3
var info = TimeZoneInfo.FindSystemTimeZoneById(id)
DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(DataBaseUTCDate,
TimeZoneInfo.Utc, info);
You need to set the Kind to Unspecified, like this:
DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified);
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone);
DateTimeKind.Local means the in local time zone, and not any other time zone. That's why you were getting the error.
The DateTime structure supports only two timezones:
The local timezone the machine is running in.
and UTC.
Have a look at the DateTimeOffset structure.
var info = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTimeOffset localServerTime = DateTimeOffset.Now;
DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);
DateTimeOffset utc = localServerTime.ToUniversalTime();
Console.WriteLine("Local Time: {0}", localServerTime);
Console.WriteLine("User's Time: {0}", usersTime);
Console.WriteLine("UTC: {0}", utc);
Output:
Local Time: 30.08.2009 20:48:17 +02:00
User's Time: 31.08.2009 03:48:17 +09:00
UTC: 30.08.2009 18:48:17 +00:00
Everyone else's answer seems overly complex. I had a specific requirement and this worked fine for me:
void Main()
{
var startDate = DateTime.Today;
var StartDateUtc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.SpecifyKind(startDate.Date, DateTimeKind.Unspecified), "Eastern Standard Time", "UTC");
startDate.Dump();
StartDateUtc.Dump();
}
Which outputs (from linqpad) what I expected:
12/20/2013 12:00:00 AM
12/20/2013 5:00:00 AM
Props to Slaks for the Unspecified kind tip. That's what I was missing. But all the talk about there being only two kinds of dates (local and UTC) just muddled the issue for me.
FYI -- the machine I ran this on was in Central Time Zone and DST was not in effect.
As dtb says, you should use DateTimeOffset if you want to store a date/time with a specific time zone.
However, it's not at all clear from your post that you really need to. You only give examples using DateTime.Now and you say you're guessing that you're using the server time. What time do you actually want? If you just want the current time in UTC, use DateTime.UtcNow or DateTimeOffset.UtcNow. You don't need to know the time zone to know the current UTC time, precisely because it's universal.
If you're getting a date/time from the user in some other way, please give more information - that way we'll be able to work out what you need to do. Otherwise we're just guessing.
UTC is just a time zone that everyone agreed on as the standard time zone. Specifically, it's a time zone that contains London, England. EDIT: Note that it's not the exact same time zone; for example, UTC has no DST. (Thanks, Jon Skeet)
The only special thing about UTC is that it's much easier to use in .Net than any other time zone (DateTime.UtcNow, DateTime.ToUniversalTime, and other members).
Therefore, as others have mentioned, the best thing for you to do is store all dates in UTC within your database, then convert to the user's local time (by writing TimeZoneInfo.ConvertTime(time, usersTimeZone) before displaying.
If you want to be fancier, you can geolocate your users' IP addresses to automatically guess their time zones.

Convert UTC/GMT time to local time

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

Categories