I've seen that many websites (usually forums) allow users to specify their TimeZone preferences by selecting:
As far as I know, when doing conversions, .NET always takes DST into account, so the question is:
Below this point I present what I managed to do so far, but it feels hacky and I was wondering if there was a cleaner/better approach.
First, to make sure that DST is applied automatically, I wrote the following test:
[Test]
public void DateTimeConversion_ToLocalTime_HandlesDSTByDefault()
{
var utcDateInDstInterval = new DateTime(2012, 07, 15, 0, 0, 0, DateTimeKind.Utc);
var utcDateOutisdeDstInterval = new DateTime(2012, 02, 15, 0, 0, 0, DateTimeKind.Utc);
var roTimezone = TimeZoneInfo.FindSystemTimeZoneById("GTB Standard Time");
Assert.AreEqual(3, TimeZoneInfo.ConvertTimeFromUtc(utcDateInDstInterval, roTimezone).Hour);
Assert.AreEqual(2, TimeZoneInfo.ConvertTimeFromUtc(utcDateOutisdeDstInterval, roTimezone).Hour);
}
This test passes, showing that:
Next, I noticed the TimeZoneInfo.GetAdjustmentRules()
method that returns an array of AdjustmentRule
objects and figured out that if I undo the effects of these rules I could get the DST-unaffected value.
So I wrote the following method that does this if the DateTime
object is affected by DST:
private DateTime RemoveDSTFromDateTime(DateTime dateTime, TimeZoneInfo timeZoneInfo)
{
if (!dateTime.IsDaylightSavingTime())
return dateTime;
var result = dateTime;
foreach (var adjustmentRule in timeZoneInfo.GetAdjustmentRules())
result = result.Subtract(adjustmentRule.DaylightDelta);
return result;
}
Back to the original DST/No DST scenario, but this time forcing the result to be unaffected by DST:
[Test]
public void DateTimeConversion_ToLocalTime_WithoutDST()
{
var utcDateInDstInterval = new DateTime(2012, 07, 15, 0, 0, 0, DateTimeKind.Utc);
var utcDateOutisdeDstInterval = new DateTime(2012, 02, 15, 0, 0, 0, DateTimeKind.Utc);
var roTimezone = TimeZoneInfo.FindSystemTimeZoneById("GTB Standard Time");
var convertedDateWithDst = TimeZoneInfo.ConvertTimeFromUtc(utcDateInDstInterval, roTimezone);
var convertedDateWithoutDst = TimeZoneInfo.ConvertTimeFromUtc(utcDateOutisdeDstInterval, roTimezone);
Assert.AreEqual(2, RemoveDSTFromDateTime(convertedDateWithDst, roTimezone).Hour);
Assert.AreEqual(2, RemoveDSTFromDateTime(convertedDateWithoutDst, roTimezone).Hour);
}
This test also passes, showing that now the effect of DST is cancelled (we always get UTC + 2h, regardless of the time of the year).
While writing this down I got another idea that appears to work: instead of using any TimeZoneInfo.Convert...()
methods, simply add roTimezone.BaseUtcOffset
to the UTC date.
Can anyone indicate what is the right way to do this?
Parsing your question, it seems you have two proposed solutions:
Determine the DST adjustment and remove it from the output post-conversion.
Take the UTC DateTime
and add the BaseUtcOffset
so you are always dealing with standard time.
Since your question was how to not implement DST, I would say you answered it yourself with option #2.
That said, your initial assumption is flawed. The reason many sites ask for time zone and DST is usually because their time zone implementation doesn't support DST to begin with. What they are doing is having you pick a base offset and decide whether or not you want an hour added during a set time of year. They may call it a "time zone", but it is in no way as robust as either .Net's TimeZoneInfo
classes or libraries like Noda Time.
If you are truly picking a "time zone", such as by TimeZoneInfo.GetSystemTimeZones()
(you should be saving the Id
property and showing the DisplayName
property) then you absolutely should NOT ask the user for whether or not to use DST. If the user lives somewhere where DST is not used (like in Arizona), there is already a selection for that. Users are usually pretty good about picking the right selection to get the effect they want, especially if they live in an area with weird DST rules (like Indiana).
The same idea would apply if you are using IANA/Olson style time zones like "America/New York"
, whether they are provided by Noda Time or some other implementation. You should entrust the library with the DST decision - not the user.
One very valid argument for entrusting this to a library is that these DST rules can change, and have changed at many points in their lifetime. If you are writing any kind of application or site that deals with information in the past, you may find that what is valid today is not the correct adjustment for what happened in the past. Also, there is a lot of weirdness in the world of time zones, like Australia where certain areas are offset by 30 minutes instead of a full hour. Do you really want to manage this yourself? Probably not. :)
I recently did this for removing the DST for Arizona when calculating its current time remotely. In my case, I had a DST (y/n) flag in a database by ZIP Code. For anyone making use of the above code (sample #2) for removing the DST adjustment, you may want to qualify the timeframe for the adjustment you intend to apply since some adjustments are present but expired (and I presume can be future dated when a change is coming). Here is an example of what worked for me:
private DateTime RemoveDSTFromDateTime(DateTime dateTime, TimeZoneInfo timeZoneInfo)
{
#region detail...
//removes daylight savings time adjustment as defined in current year
int _currYear = DateTime.Now.Year;
if (!dateTime.IsDaylightSavingTime())
return dateTime;
var result = dateTime;
foreach (var adjustmentRule in timeZoneInfo.GetAdjustmentRules())
{
if (adjustmentRule.DateStart.Year <= _currYear && adjustmentRule.DateEnd.Year >= _currYear)
{
result = result.Subtract(adjustmentRule.DaylightDelta);
}
}
return result;
#endregion
}
One suggestion that I have is to create a custom TimeZoneInfo
and use that to generate the DateTime
object
myTimeZoneInfo = TimeZoneInfo.CreateCustomTimeZone(
TimeZoneInfo.Local.Id,
TimeZoneInfo.Local.BaseUtcOffset,
TimeZoneInfo.Local.DisplayName,
TimeZoneInfo.Local.StandardName, "", null, true);
The key parameter is the true
, which is disableDaylightSavingsTime
.
Usage:
DateTime noDaylightSavings = TimeZoneInfo.ConvertTimeFromUtc(reading, myTimeZoneInfo);
or
DateTime noDaylightSavings = TimeZoneInfo.ConvertTimeToUtc(reading, myTimeZoneInfo);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With