Reputation: 1034
I am using TZ4Net library from http://www.babiej.demon.nl/Tz4Net/main.htm in order to use accurate timezones in ASP.NET
I recently started getting "Source time in transition period."
from this part of the code
static DateTime Convert(DateTime srcTime, string srcName, string dstName)
{
if (OlsonTimeZone.LookupName(srcName) == null)
{
throw new ArgumentException("Unknown source timezone name.");
}
if (OlsonTimeZone.LookupName(dstName) == null)
{
throw new ArgumentException("Unknown destintation timezone name.");
}
OlsonTimeZone srcZone = OlsonTimeZone.GetInstance(srcName);
TimeCheckResult srcCheckRes = srcZone.CheckLocalTime(srcTime);
switch (srcCheckRes)
{
case TimeCheckResult.Valid :
{
OlsonTimeZone dstZone = OlsonTimeZone.GetInstance(dstName);
DateTime dstTime = dstZone.ToLocalTime(srcZone.ToUniversalTime(srcTime));
return dstTime;
}
case TimeCheckResult.InSpringForwardGap :
case TimeCheckResult.InFallBackRange :
{
throw new ArgumentException("Source time in transition period."); // THIS PART HERE
}
default :
{
throw new ArgumentException("Source time out of range.");
}
}
}
What is TimeCheckResult.InFallBackRange and what should I do to handle this type of error?
Upvotes: 1
Views: 462
Reputation: 1034
Response from the author of TZ4NET
Hi Matthew,
This means that 11/3/2013 1:26:00 is an ambiguous time in the source timezone. For example in America/New_York on 11/3/2013 at 02:00 the time will be set back to 01:00. This means that time 01:26 will occur twice at that day hence during conversion it is not clear if you refer to first occurrence or to second one as they will correspond to different UTC times. I think the best is to detect this and ask user to elaborate on what exactly time he/she refers to. If this is not interactive process, the easiest way is to detect it and add 1 hour to source time and then subtract 1 hour from destination time.
I hope this answers your question, Regards, ZB
Upvotes: 0
Reputation: 241768
The terms "Spring Forward" and "Fall Back" refer to changes for daylight saving time. You can read more in the DST tag wiki.
During the "Spring Forward" transition, there is a gap values that doesn't exist in the local time. For example, in the United States, most time zones skip from 1:59:59 to 3:00:00 on the second Sunday in March. So a time of 2:00:00 would be invalid because it's in the gap.
During the "Fall Back" transition, there is a range of values that exist twice in the local time. For example, in the United States, most time zones go from 1:59:59 back to 1:00:00 on the first Sunday in November. So a time of 1:00:00 exists twice and is therefore ambiguous as to which of the two moments it could refer to.
Here's what you should you do when you are trying to convert from a local time that falls in a transition period:
For a time that falls in a gap created by the "Spring Forward" transition, that's not really a valid time at all.
You should probably present an error message to your user so they can enter a valid time.
Alternatively, you might want to advance the time by the saving amount (usually 1 hour) if you want to assume that they just forgot to adjust for DST. This is often used for events that recur daily.
For a time that is ambiguous because it falls in the "Fall Back" transition, you need to determine which of the two possibilities you actually want to use.
In many cases, you should prompt your user with the two choices so they can decide. Ask the question, "Did you mean 1:00 EDT (-0400) or 1:00 EST (-0500)?"
Sometimes you will want to pick for them. You might pick the first occurrence or the second occurrence depending on your requirements.
TZ4Net is a great library, and the author (Zbigniew Babiej) has been kind enough to maintain it over the years. But it was originally written around the time of .NET 2.0, so it doesn't handle DateTimeOffset
values. It also doesn't consider the .Kind
property of any DateTime
values used. Though DateTimeKind
was introduced in .Net 2.0, it doesn't appear this was ever incorporated into TZ4Net. So you must be very careful that you feed its functions with correct values.
If you'd like to continue using Olson time zones, you could continue with TZ4Net, but I'd also like to recommend you try Noda Time. It is a community-developed open source project (rather than single author), and its lead developer is Jon Skeet. You will have the same concerns regarding DST transitions, but the API of Noda Time will force you to deal with these concerns up front, rather then having to find out after your application is deployed.
If you'd just like to keep what you have today, you could modify your above function as follows to handle the Fall Back transition:
To assume the first (daylight) instance:
case TimeCheckResult.InFallBackRange:
{
OlsonTimeZone dstZone = OlsonTimeZone.GetInstance(dstName);
DateTime dstTime = dstZone.ToLocalTime(srcZone.ToUniversalTime(srcTime.AddHours(-1)).AddHours(1));
return dstTime;
}
To assume the second (standard) instance:
case TimeCheckResult.InFallBackRange:
{
OlsonTimeZone dstZone = OlsonTimeZone.GetInstance(dstName);
DateTime dstTime = dstZone.ToLocalTime(srcZone.ToUniversalTime(srcTime.AddHours(1)).AddHours(-1));
return dstTime;
}
For the Spring Forward transition:
You should probably keep this:
case TimeCheckResult.InSpringForwardGap:
throw new ArgumentException("Source time in transition period.");
But if you want to make the assumption that the user just forgot to advance their clocks, you can do this to advance it in the conversion:
case TimeCheckResult.InSpringForwardGap:
{
OlsonTimeZone dstZone = OlsonTimeZone.GetInstance(dstName);
DateTime dstTime = dstZone.ToLocalTime(srcZone.ToUniversalTime(srcTime.AddHours(1)));
return dstTime;
}
Upvotes: 2
Reputation: 27944
case TimeCheckResult.InSpringForwardGap :
case TimeCheckResult.InFallBackRange :
Means that clock is moving forward or backward in the timeperiod you are trying to calculate. It is probably not defined how to handle this over diffrent time periods. It happens only twice a year ;). Handling it really depends on why your are calculating the timezone differences.
Upvotes: 0