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?

有帮助吗?

解决方案 2

  • The main thing you are missing is that "Greenwich Standard Time" is not the TimeZoneInfo id for London. That one actually belongs to "(UTC) Monrovia, Reykjavik".

    You want "GMT Standard Time", which maps to "(UTC) Dublin, Edinburgh, Lisbon, London"

    Yes, Windows time zones are weird. (At least you don't live in France, which gets strangely labeled as "Romance Standard Time"!)

  • Also, you should not be doing this part:

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

    That will make various other code think it came from the local machine's time zone. Leave it with the "Unspecified" kind. (Or even better, use a DateTimeOffset instead of a DateTime)

  • Then you also need to use the .IsDaylightSavingsTime method on the TimeZoneInfo object, rather than the one on the .DateTime object. (There are two different methods, with the same name, on different objects, with differing behavior. Ouch!)

But I wholeheartedly agree that this is way too complicated and error prone. Just use Noda Time. You'll be glad you did!

其他提示

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...

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top