How to handle when timezone goes backwards in the future - c#

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.

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.

NodaTime addition when DST happens

Let's say I have the date 2014-11-2T00:00:00-0600 then I want to add 1 day, and have 2014-11-3T00:00:00-0700 .
Now if I do :
crtDay.Plus(Duration.FromStandardDays(1));
crtDay is a ZonedDateTime object
I end up with : 2014-11-2T23:00:00-0700 .
How do I move to the next day? (I could add hours until I moved to the next day, but it feels like there has to be a better way.)
Thanks.
(I'll assume from the values you gave that you are working in the US Mountain time zone.)
A "standard day" is 24 hours. However, on the day you mentioned, there are 25 hours in the calendar day, because the hour from 01:00 to 02:00 is repeated for the daylight saving time fall-back transition. Thus, you end up on the same day if you only add 24 hours.
In Noda Time, the ZonedDateTime structure is meant to represent a specific moment in time, with respect to a time zone. Thus, you can only move by linear instantaneous time, using Duration.
To move by calendar time, you'll have to take it's LocalDateTime and add a Period to it. If desired, you can then create a new ZonedDateTime from the result.
ZonedDateTime nextDay = zdt.LocalDateTime.PlusDays(1).InZoneLeniently(zdt.Zone);
Here I use InZoneLeniently for convenience. However, you should understand the behavior of that particular resolver. A different resolver (or a custom one) might be more appropriate, depending on your needs.
Say for example that you were starting at 1:30 on that day instead. The lenient resolver will assume you wanted to choose the latter of the two occurrences. Essentially you would be adding 25 hours even though 24 hours would still yield the same clock time. Perhaps your users might expect you to land on the first occurrence.
Also consider the spring-forward transition, where if you started at 2:30 then you'd land on date/time that doesn't exist. The lenient resolver will advance to the next valid clock time for the time zone - which might be acceptable, but it would have a different clock time of 2:00. Your users might expect you'd advance by a whole hour instead of just the next valid clock time.
The built-in resolvers are being reconsidered for Noda Time v2. See issue 295, and this alternate implementation.

Recording and calculating rich elapsed times with Noda Time

I'd like to accurately track key instants for a session object (representing a remote resource), and here is what I would like to do:
Record key events, such as the session start time
Display rich time information to the user when asked
Have the ability to calculate elapsed times between any event, such as the time relative to the session start time
I have some test code where I can successfully gather all of the information that I need, and I'm using IClock as a basis to start with instants, from which I can create ZonedDateTime and LocalDateTime values. I've yet to find any examples other than calculating days and months (using Period). I need to record and display times (e.g. the elapsed time since the session started) at least down to the second.
Are they any examples like this out there? I searched around and have not found anything yet.
Using the BCL, this would be a job for TimeSpan. But I want the much richer data provided by Noda Time for a number of reasons (and for consistency in other areas where I am using Noda Time).
It sounds like you are just looking for the Duration structure. Something like this?
IClock clock = SystemClock.Instance;
Instant i1 = clock.Now;
// some time later...
Instant i2 = clock.Now;
Duration d = i2 - i1;
You may be able to find more examples by searching Stack Overflow using the nodatime tag.
See also, the core types quick reference in the Noda Time user guide.
If you need something more specific, please edit your question to elaborate. Thanks.

Converting DateTimes that are near DayLight Savings time?

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.

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