Converting DateTimes that are near DayLight Savings time? - c#

I'm working on software that runs reports for GPS devices that are running 24/7/365. Part of the report output requires that we convert our stored database times (kept in Central Standard Time) to user timers (any requested time zone). Twice a year we run into an issue with DST when people run reports that start before and finish after the time change. It fails at one line:
return TimeZoneInfo.ConvertTime(dateToConvert, DatabaseTime, UserTime);
dateToConvert is a DateTime to be converted. DatabaseTime and UserTime are both TimeZoneInfo objects. I'm not doing anything tricky or complicated but DateTimes near the DST time change throw exceptions. Such as 3/10/2013 2:02:11 AM even though it's being "converted" from Central Time to Central Time.
What is the best method for handling DateTimes near DST time changes?

You have garbage in your database, 3/10/2013 2:02:11 AM never existed. The minute after 1:59 AM that morning was 3:00 AM, the clock was moved by an hour. .NET is not going to put up with that junk date.
You will need to find out how that garbage timestamp ended up in your dbase. Clearly a conversion of a time from one timezone to another that disregards daylight savings rules, like active in one but not the other, is a highly likely source of that garbage. If you can't fix your dbase to use UTC then at least do it in your code. First going to UTC in the one timezone and then back to local time in the other. Use the TimeZoneInfo class, ConvertTimeFrom/ToUtc methods.

I ran into this problem. I fixed it by adding a new GMT time column. This allowed the application to work with the original data and any fixes to work with GMT. Then I changed the application so that any code that was having problems with daylight savings would access this new column. Also, as time went on, I re-pointed any code that was used for calculation to this new column leaving the displays to work with the old column. It's not elegant, but it works and it is easy.

Conversion should work properly as the time is not truly junk, as Hans stated, rather it is just non-adjusted (a term I just invented). 3/10/2013 2:02:11 AM CDT == 3/10/2013 8:02:11 AM UTC == 3/10/2013 3:02:11 AM CDT...they are ALL semantically equivalent. If you do not believe me, do the conversion at timeanddate.com and see they all equate (round to nearest 5 minutes for their calculator though). Whether .NET code will allow this semantic equivalence, I have not tried it because I am not in front of my dev box currently.
UPDATE #1:
Run the following code on a computer set to CST time zone:
using System;
namespace TimeZoneSample
{
public static class Program
{
public static void Main()
{
DateTime t = DateTime.Parse("3/10/2013 2:02:11 AM");
Console.WriteLine(t);
Console.WriteLine(t.ToUniversalTime());
Console.WriteLine(t.ToUniversalTime().ToLocalTime());
}
}
}
This yields the following console output:
3/10/2013 2:02:11 AM
3/10/2013 8:02:11 AM
3/10/2013 3:02:11 AM
Proof that my original explanation is correct. quod erat demonstrandum

I would follow one of the other answers if that's at all possible - you want to fix this properly. If your time is incorrect during the fall transition it won't generate an exception, it's just going to randomly be an hour off.
There's a workaround to get you out of your current jam. Since it's only the missing hour during the spring that will cause the exception, you can catch the exception and add an hour into the time before repeating the conversion.

Related

Best practice for adding/subtracting from universal or local DateTime

I'm trying to add a wrapper around DateTime to include the time zone information. Here's what I have so far:
public struct DateTimeWithZone {
private readonly DateTime _utcDateTime;
private readonly TimeZoneInfo _timeZone;
public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone) {
_utcDateTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified), timeZone);
_timeZone = timeZone;
}
public DateTime UniversalTime { get { return _utcDateTime; } }
public TimeZoneInfo TimeZone { get { return _timeZone; } }
public DateTime LocalTime { get { return TimeZoneInfo.ConvertTimeFromUtc(_utcDateTime, _timeZone); } }
public DateTimeWithZone AddDays(int numDays) {
return new DateTimeWithZone(TimeZoneInfo.ConvertTimeFromUtc(UniversalTime.AddDays(numDays), _timeZone), _timeZone);
}
public DateTimeWithZone AddDaysToLocal(int numDays) {
return new DateTimeWithZone(LocalTime.AddDays(numDays), _timeZone);
}
}
This has been adapted from an answer #Jon Skeet provided in an earlier question.
I am struggling with with adding/subtracting time due to problems with daylight saving time. According to the following it is best practice to add/subtract the universal time:
https://msdn.microsoft.com/en-us/library/ms973825.aspx#datetime_topic3b
The problem I have is that if I say:
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
var date = new DateTimeWithZone(new DateTime(2003, 10, 26, 00, 00, 00), timeZone);
date.AddDays(1).LocalTime.ToString();
This will return 26/10/2003 23:00:00. As you can see the local time has lost an hour (due to daylight saving time ending) so if I was to display this, it would say it's the same day as the day it's just added a day to. However if i was to say:
date.AddDaysToLocal(1).LocalTime.ToString();
I would get back 27/10/2003 00:00:00 and the time is preserved. This looks correct to me but it goes against the best practice to add to the universal time.
I'd appreciate it if someone could help clarify what's the correct way to do this. Please note that I have looked at Noda Time and it's currently going to take too much work to convert to it, also I'd like a better understanding of the problem.
Both ways are correct (or incorrect) depending upon what you need to do.
I like to think of these as different types of computations:
Chronological computation.
Calendrical computation.
A chronological computation involves time arithmetic in units that are regular with respect to physical time. For example the addition of seconds, nanoseconds, hours or days.
A calendrical computation involves time arithmetic in units that humans find convenient, but which don't always have the same length of physical time. For example the addition of months or years (each of which have a varying number of days).
A calendrical computation is convenient when you want to add a coarse unit that does not necessarily have a fixed number of seconds in it, and yet you still want to preserve the finer field units in the date, such as days, hours, minutes and seconds.
In your local time computation, you add a day, and presuming a calendrical computation is what you intended, you preserve the local time of day, despite the fact that 1 day is not always 24 hours in the local calendar. Be aware that arithmetic in local time has the potential to result in a local time that has two mappings to UTC, or even zero mappings to UTC. So your code should be constructed such that you know this can never happen, or be able to detect when it does and react in whatever way is correct for your application (e.g. disambiguate an ambiguous mapping).
In your UTC time computation (a chronological computation), you always add 86400 seconds, and the local calendar can react however it may due to UTC offset changes (daylight saving related or otherwise). UTC offset changes can be as large as 24h, and so adding a chronological day may not even bump the local calendar day of the month by one. Chronological computations always have a result which has a unique UTC <-> local mapping (assuming the input has a unique mapping).
Both computations are useful. Both are commonly needed. Know which you need, and know how to use the API to compute whichever you need.
Just to add to Howard's great answer, understand that the "best practice" you refer to is about incrementing by an elapsed time. Indeed, if you wanted to add 24 hours, you'd do that in UTC and you'd find you'd end up on 23:00 due to there being an extra hour in that day.
I typically consider adding a day to be a calendrical computation (using Howard's terminology), and thus it doesn't matter how many hours there are on that day or not - you increment the day in local time.
You do then have to verify that the result is a valid time on that day, as it very well may have landed you on an invalid value, in the "gap" of a forward transition. You'll have to decide how to adjust. Likewise, when you convert to UTC, you should test for ambiguous time and adjust accordingly.
Understand that by not doing any adjusting on your own, you're relying on the default behavior of the TimeZoneInfo methods, which adjust backward during an ambiguous time (even though the usually desired behavior is to adjust forward), and that ConvertTimeFromUtc will throw an exception during an invalid time.
This is the reason why ZonedDateTime in Noda Time has the concept of "resolvers" to allow you to control this behavior more specifically. Your code is missing any similar concept.
I'll also add that while you say you've looked at Noda Time and it's too much work to convert to it - I'd encourage you to look again. One doesn't necessarily need to retrofit their entire application to use it. You can, but you can also just introduce it where it's needed. For example, you might want to use it internally in this DateTimeWithZone class, in order to force you down the right path.
One more thing - When you use SpecifyKind in your input, you're basically saying to ignore whatever the input kind is. Since you're designing general purpose code for reuse, you're inviting the potential for bugs. For example, I might pass in DateTime.UtcNow, and you're going to assume it's the timezone-based time. Noda Time avoids this problem by having separate types instead of a "kind". If you're going to continue to use DateTime, then you should evaluate the kind to apply an appropriate action. Just ignoring it is going to get you into trouble for sure.

How to handle when timezone goes backwards in the future

I am generating two sets of repeating events in seperate loop iterations but am having a conflict when comparing the generated results for conflicts. This seems to be when the times go backwards and I am unsure how to solve this?
The first repeat event will:
repeat everyday at 00:00 to 01:00 in "Europe/Stockholm" time
from 03/11/2015
looping until forever.
The second repeat event will:
repeat everyday at 01:00 to 02:00 in "Europe/Stockholm" time
from 03/11/2015
again looping forever.
To generate the events I am looping through everyday in the local time zone "Europe/Stockholm" using Nodatime like this:
String timeZone = "Europe/Stockholm";
for (ZonedDateTime date_Local = repeatSeriesStartDate_Local; date_Local <= LoopEndDate_Local; date_Local = new ZonedDateTime(Instant.FromDateTimeUtc(date_Local.ToDateTimeUtc().AddDays(1).ToUniversalTime()),timeZone))
My issue arises on October 29/30th 2016 When the clocks go backwards and the 2nd rule conflicts with the first.
http://www.timeanddate.com/time/change/sweden/stockholm?year=2016
The conflict times are as follows:
"2016-10-29T23:00:00Z" to "2016-10-30T01:00:00Z"
"2016-10-30T00:00:00Z" to "2016-10-30T01:00:00Z"
I am using an algorithm like this one to test for conflicts
https://stackoverflow.com/a/325964/884132
How should I handle these time shifting conflicts?
Though it would really help if you'll clarify the question, I'll make a few assumptions for now. I can edit the question later if necessary.
What you probably want to do is something like this:
for (LocalDate date = startDate; date <= endDate; date = date.PlusDays(1))
{
ZonedDateTime zdt = date.At(eventTime).InZone(tz, SchedulingResolver);
Console.WriteLine(zdt); // or whatever you want to do from here
}
The SchedulingResolver implementation is here, and is only necessary if you are using the 1.x version of Noda Time. If you are using 2.x, then you can just use InZoneLeniently(tz) instead, as the behavior of the lenient resolver in 2.x has changed to match (see "lenient resolver changes" in the 2.x migration guide).
The key points are:
ZonedDateTime is often best used as an intermediary type.
You have daily events that are based on the local day, so LocalDate is more appropriate.
If you had events based on a fixed 24-hour rotation (aka, the UTC day), then Instant would be more appropriate.
Resolvers are used to map ambiguous or invalid LocalDateTime values back to specific moments in time. The resolver I recommend for scheduling purposes is the one that:
Advances by the DST bias (usually 1 hour) when the clocks go forward (spring)
Picks the first instance when the clocks go back (fall)
Though as Jon mentioned - your needs may vary, and really we can't answer what you should do. There are indeed business that need different resolver rules than the ones I am recommending.

Why is it so hard to convert from one time zone to another?

I have read through multiple posts here until my head was ready to explode with little lines on the atlas strewn all over.
Here is what I wanted to do:
I have a calendar full of appointments to display for a business.
The business is in New York (EDT)
My dev machine is in PST
Production Servers are in California (PST)
Calendar data is stored in the DB in UTC
All I want to do is take a time range specified in EDT (say May 15, 9am - 5pm) and show all the appointments on the calendar.
So, my calendar control tells me "I want the appointments from 5/15/12 9am EDT - 5/15/12 5pm EDT"
I say fine, I will call a db proc and pass the date values in UTC, ie (5/15/12 13:00 UTC - 5/15/12 21:00 UTC). Then when I get them, I will convert them back to EDT before handing them to you.
However, Little did I know that .NET will find this simple task to trip me up.
I got the TimeZoneInfo just fine by using:
TimeZoneInfo zoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
But that is as far as things worked.
Here's what I tried next:
DateTime rangeStartUTC = TimeZoneInfo.ConvertTimeToUtc(rangeStart, zoneInfo);
EXCEPTION: Kind is not properly specified.
(WTF is Kind?) I told you the time I want to convert, I told you it is in EDT, I told you I want to convert it to UTC. What more do you want? I could do it by hand. F**ing do it already.
So I tried to set the Kind, but it only has two values! Local or Utc. But my time is neither local, nor Utc. Why in the hell are you asking me for a time zone info then? Cant you just tell what the local time zone is by asking the system clock? Predictably, neither worked. (yeah, according to the docs there is UnSpecified, but again according to the docs, it doesn't really do anything and yes I tried that one too).
Then, I tried:
TimeZoneInfo.ConvertTime(rangeStart, zoneInfo, TimeZoneInfo.Utc)
SAME LAME EXCUSE!
Time to read some more of St. Jon Skeet's passages.
What do you know, there is a new class DateTimeOffset. It will solve all your problems.
God bless for all the nice and merciful .NET 4.0 bounties...
DateTimeOffset offStartTime = new DateTimeOffset(rangeStart, zone.GetUtcOffset(rangeStart));
rangeStartUTC = offStartTime.UtcDateTime;
EXCEPTION: "Offset should be 0 for Utc dates"
Gaaaaah!
How the heck did you conclude that the rangeStart is Utc? Did I ever tell you that?
A bunch of people are quoting
TimeZoneInfo.ConvertTime(rangeStart, zone) as the solution, how is it "Kind" enough to work for them? Unless their source time zone conveniently happens to be the same as their local time zone.
So what is a poor .NET C# developer to do?
Try DateTime.SpecifyKind(rangeStart, DateTimeKind.Unspecified) before DateTimeOffset or TimeZoneInfo. It should not do any conversion of the time itself, just change the Kind.
As I understand it, the Kind on DateTime should be either Unspecified or correspond to the result of private TimeZoneInfo.GetCorrespondingKind() method, which returns Local for
TimeZoneInfo.Local and Utc for TimeZoneInfo.Utc.
UPDATE: Sorry, get the original answer wrong, should be all good now.

How to convert Datetimes into UTC based timing values to compare timings- C#

I've string (variable is fileDate) with Date values in the following format:
2/12/2011 11:58 AM
Now I want to convert this to a date and then to UTC time based as I've problems in comparing dates in different machines, so *I always want to convert all strings (which are getting compared) to Utc_date values.*
I tried this code below but it did not work as I'm not able to convert the above string to Datetime based (as it does not have seconds).
DateTime date = Convert.ToDateTime(fileDate);
date = DateTime.SpecifyKind(date, DateTimeKind.Utc);
fileDate = date.ToString("MM/dd/yyyy hh:mm tt");
Above did not work showing FormatException.
Can you pl help?
To start with, I'd suggest using DateTime.ParseExact or TryParseExact - it's not clear to me whether your sample is meant to be December 2nd or February 12th. Specifying the format may well remove your FormatException.
The next problem is working out which time zone you want to convert it with - are you saying that 11:58 is a local time in some time zone, or it's already a UTC time?
If it's a local time in the time zone of the code which is running this, you can use DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal to do it as part of parsing.
If it's already a universal time, use DateTimeStyles.AssumeUniversal
If it's a local time in a different time zone, you'll need to use TimeZoneInfo to perform the conversion.
Also, if it's a local time you'll need to consider two corner cases (assuming you're using a time zone which observes daylight saving time):
A local time may be skipped due to DST transitions, when the clocks go forward. So if the clocks skip from 1am to 2am, then 1:30am doesn't exist at all.
A local time may be ambiguous due to DST transitions, when the clocks go back. So if the clocks go back from 2am to 1am, then 1:30am occurs twice at different UTC times - which occurrence are you interested in?
You should decide how you want to handle these cases, and make sure they're covered in your unit tests.
Another option is to use my date and time library, Noda Time, which separates the concepts of "local date/time" and "date/time in a particular time zone" (and others) more explicitly.
you should be using DateTime.ParseExact to get the value into a proper DateTime instance, and then you can use .ToUniversalTime() to get the UTC time (this would be with respect to the difference of time as in your server machine)
you can use :
DateTime.Now.ToUniversalTime();
i don't mean to say to you should use "DateTime.Now" but you get the point that as a part of the DateTime object you have a method to transform it to Universal time
http://msdn.microsoft.com/en-us/library/system.datetime.touniversaltime.aspx

Difference in usage of DateTime.Now vs DateTime.UtcNow

As this Question's Answer from hightechrider mentions that code block as below is more right
var start = DateTime.Parse("08/10/2011 23:50:31").Utc;
if(start.AddMinutes(20) > DateTime.UtcNow)
then using as this by TimeSpan
var start = DateTime.Now;
var oldDate = DateTime.Parse("08/10/2011 23:50:31");
if(start - oldDate).TotalMinutes >= 20)
Here since the DateTime is executed and also parsed in the same culture then, How it will make difference ??
I am feeling very Phoney by this answer.
In a nutshell: UTC is a continuous, single-valued time scale, whereas local time is not continuous or single-valued. The primary reason is Daylight Savings Time, which doesn't apply to UTC. So UTC never jumps forward or back an hour, whereas local time does. And when it jumps backward, the same time value occurs twice.
Making comparisons is best done using the continuous, single-valued time scale, unless you want to mess around with DST yourself. Even if you do, there is no way to distinguish between the first and second "2am" when Daylight Savings Time ends and the clocks are set back an hour.
Technical note: even though UTC is continuous, it does have the occasional extra leap second inserted to keep up with the slowing down of the Earth's rotation. Those seconds are usually added at the end of the day and are listed with 60 seconds. So you'd have 23:59:59, 23:59:60, 00:00:00.
The United States transitions from Daylight Savings Time to Standard Time at 2AM on November 6th, 2011. If, at 2:10AM, I ask how far in the past 1:50AM was, .NET will tell me 20 minutes. In truth, it was an hour and 20 minutes, since we set our clocks back an hour at 2AM. I won't run into these issues if I use UTC - libraries like the .NET Framework have all the logic needed to correctly deal with discontinuities like this.
The whole Daylight Savings Time scheme is a mess, and it's hard for anyone whose country, like yours, (sensibly) doesn't implement it, to understand the issues that arise. It gets even more interesting when governments start changing the switchover days around.

Categories

Resources