Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating daylight savings-aware DateTime when server set to UTC

My application is hosted on Windows Azure, which has all servers set to UTC.

I need to know when any given DateTime is subject to daylight savings time. For simplicity, we can assume that my users are all in the UK (so using Greenwich Mean Time).

The code I am using to convert my DateTime objects is

public static DateTime UtcToUserTimeZone(DateTime dateTime)
{
    dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);

    var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Greenwich Standard Time");

    var userDateTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);

    return DateTime.SpecifyKind(userDateTime, DateTimeKind.Local);
}

However, the following test fails at the last line; the converted DateTime does not know that it should be in daylight savings time.

[Test]
public void UtcToUserTimeZoneShouldWork()
{
    var utcDateTime = new DateTime(2014, 6, 1, 12, 0, 0, DateTimeKind.Utc);
    Assert.IsFalse(utcDateTime.IsDaylightSavingTime());

    var dateTime = Utils.UtcToUserTimeZone(utcDateTime);
    Assert.IsTrue(dateTime.IsDaylightSavingTime());
}

Note that this only fails when my Windows time zone is set to (UTC) Co-ordinated Universal Time. When it is set to (UTC) Dublin, Edinburgh, Lisbon, London (or any other northern-hemisphere time zone that observes daylight savings), the test passes. If you change this setting in Windows a restart of Visual Studio is required in order for the change to fully take effect.

What am I missing?

like image 429
Richard Ev Avatar asked Feb 15 '23 05:02

Richard Ev


1 Answers

Your last line specifies that the value is in the local time zone of the system it's running in - but it's not really... it's converted using a different time zone.

Basically DateTime is somewhat broken, in my view - which makes it hard to use correctly. There's no way of saying "This DateTime value is in time zone x" - you can perform a conversion to find a specific local time, but that doesn't remember the time zone.

For example, from the docs of DateTime.IsDaylightSavingTime (emphasis mine):

This method determines whether the current DateTime value falls within the daylight saving time range of the local time zone, which is returned by the TimeZoneInfo.Local property.

Obviously I'm biased, but I'd recommend using my Noda Time project for this instead - quite possibly using a ZonedDateTime. You can call GetZoneInterval to obtain the ZoneInterval for that ZonedDateTime, and then use ZoneInterval.Savings to check whether the current offset contains a daylight savings component:

bool daylight = zonedDateTime.GetZoneInterval().Savings != Offset.Zero;

This is slightly long-winded... I've added a feature request to consider adding an IsDaylightSavingTime() method to ZonedDateTime as well...

like image 197
Jon Skeet Avatar answered Feb 23 '23 00:02

Jon Skeet