wpqs
wpqs

Reputation: 677

Why does TimeZoneInfo.IsValidTime() give unexpected result for DateTimeKind.Local?

I am surprised by the behaviour of TimeZoneInfo.IsValidTime() as it doesn't work as I expected with an invalid DateTime set as DateTimeKind.Local

[Fact]
public void DateTimeInvalidForTimeZone()
{
    TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");

    var testTimeUnspec = new DateTime(2020, 3, 29, 02, 01, 0, DateTimeKind.Unspecified);
    var testTimeLocal = new DateTime(2020, 3, 29, 02, 01, 0, DateTimeKind.Local);
    var testTimeUtc = new DateTime(2020, 3, 29, 02, 01, 0, DateTimeKind.Utc);

    Assert.False(timeZone.IsInvalidTime(testTimeUtc));     //as anticipated - UTC so cannot be invalid
    Assert.True(timeZone.IsInvalidTime(testTimeUnspec));   //as anticipated - the time is invalid
    Assert.False(timeZone.IsInvalidTime(testTimeLocal));   //unexpected - the time is invalid
}

Note: Central European Standard Time transitions to daylight saving on 29 March 2020 at 02:00AM so the time sequence in terms of local time is 01:59:58, 01:59:59, 03:00:00, 03:00:01. Accordingly all local times between 02:00:00 and 02:59:59 are invalid.

The Microsoft Documentation explains:

This suggests that DateTime objects that are DateTimeKind.Local will be always be valid which is not what I expect or want. Can anyone explain the logic behind Microsoft's implementation? Yet another reason to consider Nodatime?

Upvotes: 2

Views: 727

Answers (1)

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241808

As you pointed out, the docs for TimeZoneInfo.IsInvalidTime explain that returning false is the documented behavior when the TimeZoneInfo is not the local time zone, and is correct regardless of whether the time is valid in either the local time zone or the one belonging to the TimeZoneInfo object. Indeed, the implementation simply returns false in this case.

If you want to know if local times are valid or not, then you need to use the TimeZoneInfo.Local time zone, rather than one obtained by id. It doesn't matter if that named time zone happens to be the local one or not, you need to be explicitly working with TimeZoneInfo.Local if you're testing DateTimeKind.Local values.

In other words, if your computer's system local time zone is indeed "Central European Standard Time" (assuming "Adjust for daylight saving time automatically" is turned on) then the following will return True as expected.

TimeZoneInfo.Local.IsInvalidTime(new DateTime(2020, 3, 29, 02, 01, 0, DateTimeKind.Local))

Upvotes: 1

Related Questions