Why DateTime.Now and DateTime.UtcNow Isn't My Current Local DateTime? - c#

We use DateTime.Now, but the time is not equal with our server time!
When I run my project, these are the DateTime property values:
DateTime.Now = {15/14/04 05:20:18 AM}
DateTime.UtcNow = {15/14/04 12:20:18 PM}
But my current local system time is:
15/14/04 04:50:18 AM
My time zone is Tehran (UTC+03:30).
This is the first time I've see this behavior! Why doesn't DateTime.Now equal my computer's time?

Make sure you are not manipulating the timezone somewhere in code, or playing with System.Globalization.CultureInfo
Try to search in all your source code for System.Globalization.CultureInfo may be it is somewhere, also may be the time zone is cached somewhere so try to call System.Globalization.CultureInfo.CurrentCulture.ClearCachedData() before DateTime.Now
.NET DateTime.Now returns incorrect time when time zone is changed

Your timezone is UTC +03:30, but because of daylight saving it's +4:30. This means that when DateTime.UtcNow = {15/14/04 12:20:18 PM}, it's 04:50:18 AM in your timezone, which your clock confirms.
So your DateTime.Now doesn't confirm to your machine's datetime settings, making it return a value that doesn't match your clock.
As of why the accepted answer works can be explained by DateTime.Now using the TimeZoneInfo class to calculate the local time from UTC (using timezone and daylight saving), but I don't know why that can run out of sync. Do you never recycle your application pools and never shutdown your machine? ;)
System.Globalization.CultureInfo.CurrentCulture.ClearCachedData() works because it calls TimeZone.ResetTimeZone() and TimeZoneInfo.ClearCachedData().

Related

UTC date.ToLocalTime() still produces date in UTC time

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.

Creating daylight savings-aware DateTime when server set to UTC

My application is hosted on Windows Azure, which has all servers set to UTC.
I need to know when any given DateTime is subject to daylight savings time. For simplicity, we can assume that my users are all in the UK (so using Greenwich Mean Time).
The code I am using to convert my DateTime objects is
public static DateTime UtcToUserTimeZone(DateTime dateTime)
{
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Greenwich Standard Time");
var userDateTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
return DateTime.SpecifyKind(userDateTime, DateTimeKind.Local);
}
However, the following test fails at the last line; the converted DateTime does not know that it should be in daylight savings time.
[Test]
public void UtcToUserTimeZoneShouldWork()
{
var utcDateTime = new DateTime(2014, 6, 1, 12, 0, 0, DateTimeKind.Utc);
Assert.IsFalse(utcDateTime.IsDaylightSavingTime());
var dateTime = Utils.UtcToUserTimeZone(utcDateTime);
Assert.IsTrue(dateTime.IsDaylightSavingTime());
}
Note that this only fails when my Windows time zone is set to (UTC) Co-ordinated Universal Time. When it is set to (UTC) Dublin, Edinburgh, Lisbon, London (or any other northern-hemisphere time zone that observes daylight savings), the test passes. If you change this setting in Windows a restart of Visual Studio is required in order for the change to fully take effect.
What am I missing?
Your last line specifies that the value is in the local time zone of the system it's running in - but it's not really... it's converted using a different time zone.
Basically DateTime is somewhat broken, in my view - which makes it hard to use correctly. There's no way of saying "This DateTime value is in time zone x" - you can perform a conversion to find a specific local time, but that doesn't remember the time zone.
For example, from the docs of DateTime.IsDaylightSavingTime (emphasis mine):
This method determines whether the current DateTime value falls within the daylight saving time range of the local time zone, which is returned by the TimeZoneInfo.Local property.
Obviously I'm biased, but I'd recommend using my Noda Time project for this instead - quite possibly using a ZonedDateTime. You can call GetZoneInterval to obtain the ZoneInterval for that ZonedDateTime, and then use ZoneInterval.Savings to check whether the current offset contains a daylight savings component:
bool daylight = zonedDateTime.GetZoneInterval().Savings != Offset.Zero;
This is slightly long-winded... I've added a feature request to consider adding an IsDaylightSavingTime() method to ZonedDateTime as well...
The main thing you are missing is that "Greenwich Standard Time" is not the TimeZoneInfo id for London. That one actually belongs to "(UTC) Monrovia, Reykjavik".
You want "GMT Standard Time", which maps to "(UTC) Dublin, Edinburgh, Lisbon, London"
Yes, Windows time zones are weird. (At least you don't live in France, which gets strangely labeled as "Romance Standard Time"!)
Also, you should not be doing this part:
return DateTime.SpecifyKind(userDateTime, DateTimeKind.Local);
That will make various other code think it came from the local machine's time zone. Leave it with the "Unspecified" kind. (Or even better, use a DateTimeOffset instead of a DateTime)
Then you also need to use the .IsDaylightSavingsTime method on the TimeZoneInfo object, rather than the one on the .DateTime object. (There are two different methods, with the same name, on different objects, with differing behavior. Ouch!)
But I wholeheartedly agree that this is way too complicated and error prone. Just use Noda Time. You'll be glad you did!

Using TimeZoneInfo to convert between UTC/Specified TimeZone not working

All of our date/time data in our database is stored in UTC time. I'm trying to write a function to convert that UTC time into the user's preferred time zone (they can select their timezone in their profile so it has NOTHING to do with local settings on their computer and everything to do with which timezone they have selected from a dropdown of available choices.
This function is in the context of a DevExpress AspxGridView event (the third party control is not relevant to the question but I thought I'd mention it):
DateTimeOffset utcTime = (DateTimeOffset)e.Value;
TimeZoneInfo destTimeZone = Helper.GetTimeZoneInfo();
DateTime modifiedDate = TimeZoneInfo
.ConvertTimeFromUtc(utcTime.DateTime, destTimeZone);
e.DisplayText = String.Format("{0} {1}",
modifiedDate.ToString("g"),
destTimeZone.Abbreviation());
Helper.GetTimeZoneInfo() simply returns a TimeZoneInfo class that corresponds to the one the user selected, or defaults to "Pacific Standard Time" if they have not chosen one.
This generally works fine until I switch my system clock (which this server is running on) from today (which is Oct. 14, a DST date) to something like January 11, which is NOT DST.
The time seems to always be displayed in DST (i.e. always a 7 hour offset). No matter what I do with my system clock, I can't get the time to adjust for the extra hour.
For example, when I have my timezone set to Pacific Standard Time, for UTC time 10-10-2011 20:00:00, it is always displaying this time:
10-10-2011 13:00:00 (the DST time, offset of -7).
During non-Daylight Savings dates (standard), the time should be:
10-10-2011 12:00:00 (offset of -8).
What am I missing?
The converted local time of a UTC time is always based on the time zone info for that UTC time... not for whatever the current time happens to be.
That is, the PST offset on Oct 10, 2011 UTC is always -7. It doesn't matter what date you are doing the conversion on.
...Or am I misunderstanding what you are asking?
You might have a look at the e.Value.
Is the DateTimeKind for it set to DateTimeKind.Utc or is it DateTimeKind.Unspecified
If it is Unspecified, the conversion will not work correctly.
One cannot set the Kind directly. The best I have come up with is something along the lines of
// the value off the DB, but Kind is unspecified
var fromDb = new DateTime(1999,31,12)
// convert it to a Utc version of the same date
var fromDbInUtc = new DateTime(fromDb.Ticks, DateTimeKind.Utc)
var destTimeZone = Helper.GetTimeZoneInfo();
var local = TimeZoneInfo.ConvertFromUtc(fromDbInUtc, destTimeZone);
Hope this helps,
Alan.

DateTime.kind property

Have a small doubt with the DateTime.kind property. Documentation says that kind property have three fields Unspecified, Local, and Utc to show how the datetime object is represented.
DateTime dt1 = DateTime.Now;
Console.WriteLine(dt1.Kind);
Which shows "Local" but in some microsoft documentation I red that system date and time that Windows maintains is UTC rather than local time.
If that is the case then the above WriteLine should output it as UTC rather than Local?
Any idea?
--Rahul
DateTime.Now is meant to retrieve the current local time. DateTime.UtcNow retrieves the current UTC time.
Note that this is unrelated to how Windows itself stores the time. I believe it stores the current time in UTC, but also keeps track of the current time zone, so it can display the appropriate local time. I believe this is what DateTime.Now does as well.

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.

Categories