I've got an asp.net application that must run some code every day at a specific time in the Eastern Time Zone (DST aware). So my first thought is, get the eastern time value and convert it to local server time.
So I need something like this:
var eastern = DateTime.Today.AddHours(17); // run at 5pm eastern
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var utc = TimeZoneInfo.ConvertTimeToUtc(eastern, timeZoneInfo);
var local = TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZoneInfo.Local);
But how do I specify that the eastern DateTime object should be in the EST timezone.
Am I approaching this the wrong way?
First, there are several things you have to consider. You have to deal with Daylight Savings Time, which from time to time seems to change (the start and end dates have changed twice in the last 10 years). So in the Northern Hemisphere Winter, Eastern time is -5 GMT (or UTC). But, in the Summer it's -6 GMT or is that -4 GMT, I can never keep it straight (nor should I have to).
There are some DNF library functions to deal with time zone information, however you really need .net 3.5 for the most useful stuff. There's the TimeZoneInfo class in .net 3.5.
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime dt = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.Now,
TimeZoneInfo.IsDaylightSavingsTime(tzi) ?
tzi.DaylightName : tzi.StandardName);
if (dt.Hour == 17)
....
Also, keep in mind that twice every year an hour is lost or gained, so you also have to account for that if, for example, you have a countdown timer you display "time until next processing" or something like that. The fact is, time handling is not as easy as it would seem at first thought, and there are a lot of edge cases.
Seems I was able to answer my own question. Here's the code I'm using to get a next-run DateTime object.
private DateTime GetNextRun()
{
var today = DateTime.Today;
var runTime = new DateTime(today.Year, today.Month, today.Day, 17, 0, 0);
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var offset = timeZoneInfo.GetUtcOffset(runTime);
var dto = new DateTimeOffset(runTime, offset);
if (DateTime.Now > dto.LocalDateTime)
dto = dto.AddDays(1);
return dto.LocalDateTime;
}
Doing all the conversion using DateTimeOffset instead of DateTime proved effective. It even seems to handle Daylight Savings Time correctly.
A DateTime doesn't know about a time zone. Even DateTimeOffset doesn't really know about a time zone - it knows about a UTC instant and an offset from that.
You can write your own struct which does have a TimeZoneInfo and a DateTime, but I'm not sure you need it in this case. Are you just trying to schedule 5pm in Eastern time, or is this actually more general? What are you doing with the DateTime (or whatever) afterwards? Using DateTimeOffset and TimeZoneInfo you can definitely get the UTC instant of the time you're interested in; if you just need to know the time between "now" and then, that's fairly easy.
I feel duty-bound to point out that when Noda Time is production-ready, it would almost certainly be the right answer :)
You could use the DateTime.UtcNow to get UTC central time(which I believe is GMT 0) and from htere on just figure out how many time zones the one you want is and remove/add an hour for each zone.
Related
With what I understood from other questions I've used below code for checking a date if it is Daylight saving time and altering as required. I do not know which region would the application be used hence I am using Utc. However it is not working as expected.
DateTime dateValWithOffset = dateVal;
TimeZoneInfo timeZoneInfo = TimeZoneInfo.Utc;
if(timeZoneInfo.IsDaylightSavingTime(dateValWithOffset))
{
dateValWithOffset = dateValWithOffset.AddMinutes(60);
}
Example: for sample date (06-JUL-21 06.16.34.547000000 AM) above code should be showing dateValWithOffset as 07/06/2021 02:16:34.547 AM but it returns 07/06/2021 01:16:34.547 AM . If someone can point out where am I going wrong please.
Datetime values should always be in UTC. To format a datetime in the machine or user's local timezone, you should convert to a DateTimeOffset. Knowing the local time and knowing if that time is in daylight saving time are two different things.
// machine local
var timeZoneInfo = TimeZoneInfo.Local;
// or by name
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(name);
var localTime = new DateTimeOffset(dateVal, timeZoneInfo.GetUtcOffset(dateVal));
var isDst = timeZoneInfo.IsDaylightSavingTime(localTime)
IMHO DateTime is a terrible type, designed before the age of cloud computing. All DateTimeKind values other than Utc encourage programmers to continue to handle dates incorrectly and should be deprecated.
As jason.kaisersmith said in comments, "UTC is universal, so there is no daylight saving."
To elaborate, UTC does not have any daylight saving time. Instead, daylight saving time is observed differently at each time zone across the world. Some time zones don't use it at all. Some time zones start and stop DST at different dates or times than other time zones. There's even one time zone that shifts for DST by 30 minutes instead of the usual 1 hour. Without a time zone reference, the concept of DST is meaningless.
For clarity and reference, here's an overview of anticipated DST dates for 2022 by country, and a detailed list of dates and times for the first half and second half of 2022.
I host my website (asp.net webforms, .Net 4.5, SQL Server 2012) on Godaddy and they (the server) use Mountain Standard Time (-7 compare to UTC time) which is never changed and does not observe daylight saving time.
So if I do
Response.Write("UTC Time: " + DateTime.UtcNow.ToString() + "<br/>");
Response.Write("Server Time: " + DateTime.Now.ToString() + "<br/>");
Response.Write("Server DateTimeOffset: " + DateTimeOffset.Now.ToString() + "<br/>");
It will display this:
UTC Time: 9/18/2015 5:14:09 PM
Server Time: 9/18/2015 10:14:09 AM
Server DateTimeOffset: 9/18/2015 10:14:09 AM -07:00
But my users are located in Atlanta, GA which does observe daylight saving time and according to timeanddate.com they use EDT in the summer and EST in the winter
How do I get correct current time of user? Let say user open my web-application and hit Show my time button, it will display a correct current user's time?
You should never rely on the time zone settings of a server. Therefore DateTime.Now should not be ever used in an ASP.NET application.
There are many other reasons to avoid DateTime.Now. Read:The case against DateTime.Now.
The local time of the server is never guaranteed to be the local time of the user of your website anyway. If it is, it's just coincidental.
The easiest way to get the user's current time in their time zone is via JavaScript:
var now = new Date();
Though this is based on the user's clock, which may or may not be correctly set. To have any guarantees about the time, you'd have to use the UTC time of the server's clock, with the user's time zone applied.
One approach you might consider is to send the UTC time of the server down to the client, then load that into JavaScript in the client to project it to their time zone:
// C#
string serverUtcTime = DateTime.UtcNow.ToString("o"); // "2015-09-18T17:53:15.6988370Z"
// JS
var now = new Date("2015-09-18T17:53:15.6988370Z");
Actually detecting the user's time zone is a hard problem that does not currently have a solution. Some may recommend new Date().getTimezoneOffset(), but that only gives you the current numeric offset, not the time zone. Offsets can change for DST, and many time zones use similar offsets. There are also complications for historical dates near DST transitions that will work against you.
Scripts like jsTimeZoneDetect can guess your IANA time zone ID, such as America/New_York for Eastern time, but they are not 100% accurate. If you need the user's time zone on your server, then ultimately should ask the user for their time zone somewhere in your application.
In .NET, you can use Noda Time to work with IANA time zones. Without Noda Time, .NET has the TimeZoneInfo class, but it can only work with Windows time zones.
If you know for certain that the users are in Atlanta, GA (which is in the US Eastern time zone), then you can do this:
DateTime utc = DateTime.UtcNow;
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime eastern = TimeZoneInfo.ConvertTimeFromUtc(utc, tz);
Or, with Noda Time and IANA time zone IDs:
Instant now = SystemClock.Instance.Now;
DateTimeZone tz = DateTimeZoneProviders.Tzdb["America/New_York"];
ZonedDateTime eastern = now.InZone(tz);
You can use TimeZoneInfo.ConvertTime to convert from one time zone to another. However, if you just convert to EST or EDT, it will always display that value. If you want to always display the correct time for the client, you'll need to do something like using Javascript to store the client browser's local time zone in a cookie, then use that value as the time zone to convert to.
It might be a little more streamlined to get UtcTime from the server and convert from that using TimeZoneInfo.ConvertTimeFromUtc, if you expect to do much of this. Basically the same process, though.
Take a look at this example. This is quite easy.
[Test, TestCase(1), TestCase(9)]
public void Test(int month)
{
var serverTime = new DateTime(2015, month, 18, 10, 14, 09);
// No Daylight saving in Russia
var serverTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
// Daylight saving in Atlanta
var localTimeZone = TimeZoneInfo.FindSystemTimeZoneById("Atlantic Standard Time");
// check if ConvertTime take care of daylight saving
var localTime = TimeZoneInfo.ConvertTime(serverTime, serverTimeZone, localTimeZone);
// it does
if (localTimeZone.IsDaylightSavingTime(localTime))
Assert.AreEqual(string.Format("{0}/18/15 04:14 AM", month), localTime.ToString("M/dd/yy hh:mm tt", System.Globalization.CultureInfo.InvariantCulture));
else
Assert.AreEqual(string.Format("{0}/18/15 03:14 AM", month), localTime.ToString("M/dd/yy hh:mm tt", System.Globalization.CultureInfo.InvariantCulture));
}
I want to specify a time in GMT timezone, and then convert it to the local TimeZone which is EST.
This appears to do what I want, but seems a long way to get there!
Is there a simpler way to achieve this:
public static TimeZoneInfo edtZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
public static TimeZoneInfo gmtZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
public static CultureInfo ci = CultureInfo.InvariantCulture;
DateTime edtStartDT = TimeZoneInfo.ConvertTime(DateTime.SpecifyKind(DateTime.Now.Date.Add(new TimeSpan(18, 00, 00)), DateTimeKind.Unspecified), gmtZone, edtZone);
This is probably what you are looking for:
// A timespan isn't really a time-of-day, but we'll let that slide for now.
TimeSpan time = new TimeSpan(18, 00, 00);
// Use the current utc date, at that time.
DateTime utcDateTime = DateTime.UtcNow.Date.Add(time);
// Convert to the US Eastern time zone.
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime easternDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, tz);
Note that we're pairing the current UTC date with the time you provided. Since US Eastern time is either 5 or 4 hours behind UTC, with 18:00 you will always get the same date. But if you were using a different time, such as 00:00, you would find that the resulting Eastern time was on the prior day. This is normal.
A couple of notes about your previous code:
The Windows Time Zone ID "Eastern Standard Time" refers to both EST and EDT. It really should have been called "Eastern Time". Don't let the name confuse the issue.
GMT and UTC are mostly the same for all modern usage. Unless you are referring to the time zone used in London, you should prefer the term UTC.
The Windows Time Zone ID "GMT Standard Time" is not actually for GMT/UTC. It is specifically the time zone used in London, which alternates between GMT (UTC+00:00) and BST (UTC+01:00). If you want a TimeZoneInfo representing UTC, the ID is just "UTC". (However, you don't really need that in this case.)
Your original code assumed used DateTime.Now.Date, which would assume the date in the computer's local time zone - which might not be either UTC or Eastern.
If you find yourself using DateTime.SpecifyKind, in most situations, you're probably doing something wrong. (The exception would be when loading or deserializing.)
Regarding my note about a TimeSpan not being a true time-of-day, this is how .NET would have you handle that:
DateTime time = DateTime.Parse("18:00:00", CultureInfo.InvariantCulture);
DateTime utcDateTime = DateTime.UtcNow.Date.Add(time.TimeOfDay);
Or even uglier in one line:
DateTime utcDateTime = DateTime.Parse("18:00:00", CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
Personally, I would rather use Noda Time, which has a separate LocalTime type explicitly for a time-of-day that's not bound to a specific date. I'm also working to get System.TimeOfDate and System.Date types added to the CoreCLR.
Problem:
I Need to execute a task on a server that is on UTC at specific time in different time zones. Say for example I want to execute at 9:00AM Pacific Time, irrespective of Daylight Savings changes.
What do I have?
I checked the enumeration of time zones by doing
var infos = TimeZoneInfo.GetSystemTimeZones();
foreach (var info in infos)
{
Console.WriteLine(info.Id);
}
I could see only "Pacific Standard Time" for the pacific time for example and If I do the following,
TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time").GetUtcOffset(DateTime.UtcNow)
I get -07:00:00 as output but as of now the offset is -08. That means it doesn't consider the daylight changes.
I was planing to to create a DateTime instance based on the offset I get above, which doesn't seem to work as I expected.
Also, I can't use frameworks like NodaTime
Any idea how I can get it working?
You've already got your answer, using TimeZoneInfo.GetUtcOffset and passing a DateTime with DateTimeKind.Utc will work.
I get -07:00:00 as output but as of now the offset is -08. That means it doesn't consider the daylight changes.
Actually, -7 is indeed the current offset, as Pacific time is currently using daylight saving time. In the winter, the offset reverts to -8, which is the standard offset. I think you just have them backwards.
Also, keep in mind that the Id property of a TimeZoneInfo object is the identifier for the entire time zone. Some of them are misleading, like "Pacific Standard Time" - which would make you believe that it only uses Pacific Standard Time (PST), but actually it represents the entire North American Pacific Time zone - including Pacific Standard Time and Pacific Daylight Time. It will switch offsets accordingly at the appropriate transitions.
You are probably looking for something like this:
var chinaTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.FindSystemTimeZoneById("China Standard Time"));
var pacificTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"));
The string passed to FindSystemTimeZoneById is picked form TimeZoneInfo.GetSystemTimeZones()
Update: cleansed the code
How do I convert US-style DateTime such as 5/1/2012 3:38:27 PM returned from the server to user's local time? I am developing for windows phone.
I've tried
DateTime localTime = serverTime.ToLocalTime();
but the result is off a couple of hours. I thought ToLocalTime() will take care of the conversion to any timezone the user are in? Perhaps I need to get the user's timezone info first?
EDIT 1
I think the serverTime is in the PST time zone
EDIT 2
My timezone is GMT +8. I tried the following, but the resulting localTime is 15 hour behind.
TimeZoneInfo localZone = TimeZoneInfo.Local;
DateTime localTime = TimeZoneInfo.ConvertTime(serverTime, localZone);
EDIT 3
This result in 7 hours behind my local time.
TimeZoneInfo localZone = TimeZoneInfo.Local;
DateTime dateTimeKind = DateTime.SpecifyKind(serverTime, DateTimeKind.Utc);
DateTime localTime = TimeZoneInfo.ConvertTime(dateTimeKind, localZone);
EDIT 4
OK I think I am getting there but not sure if this is applicable for all time zones. I think I still have to consider day light saving because the resulting local time is just one hour ahead now.
TimeZoneInfo localZone = TimeZoneInfo.Local;
double offset = localZone.GetUtcOffset(DateTime.Now).TotalHours;
DateTime dateTimeKind = DateTime.SpecifyKind(serverTime, DateTimeKind.Utc);
DateTime localTime = TimeZoneInfo.ConvertTime(dateTimeKind, localZone).AddHours(offset);
But then how do you get DLS is in effect for a particular time zone in Windows Phone? TimeZoneInfo.FindSystemTimeZoneById does not seem to be supported?
For this to work, the DateTime-object serverTime must be of the UTC-form - or at least know what Kindit is. Read all the details around this under the remarks section of this page.
Best of luck!
What does the time represent? If it is a specific moment in time, such as the date and time that something happened, then you should update your server code to return the time in one of the following formats:
// ISO8601 local time with offset.
// get from DateTimeOffset.ToString("o")
2012-05-01T15:38:27-07:00
// ISO8601 UTC time
// get from DateTime.ToString("o") when kind is UTC
2012-05-01T22:38:27Z
It's really important that you do this, because local times can be ambiguous when daylight savings ends. You must either provide the correct offset, (-8 for PST, -7 for PDT), or send as UTC.
There are very few scenarios where sending local time by itself makes sense. If you think you have one, please elaborate about what the time represents.