3
votes

I have some outdated code that attempts to account for the change in time caused by daylight savings that looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 1; i <= 31; i++)
            {
                DateTime dt = new DateTime(1960, 3, i, 0, 0, 0);
                Console.WriteLine(dt.ToUniversalTime());
            }

            Console.WriteLine();

            for (int i = 1; i <= 30; i++)
            {
                DateTime dt = new DateTime(1960, 4, i, 0, 0, 0);
                Console.WriteLine(dt.ToUniversalTime());
            }

            Console.ReadKey();
        }
    }
}

This code iterates through the days in March and April in 1960 and prints the datetime. However, this does not correctly account for the time change in 1960, I believe because the date of the time change was different then. I attempted to fix this using the TimeZoneInfo class. I changed the code to the following:

class Program
    {
        static void Main(string[] args)
        {
            for (int i = 1; i <= 31; i++)
            {
                DateTime dt = new DateTime(1960, 3, i, 0, 0, 0);
                var tz = TimeZoneInfo.Local;
                var utcOffset = new DateTimeOffset(dt, TimeSpan.Zero);
                //use timeZoneInfo class to account for dlst offset
                Console.WriteLine(utcOffset.ToOffset(tz.GetUtcOffset(utcOffset)));
            }

            Console.WriteLine();

            for (int i = 1; i <= 30; i++)
            {
                DateTime dt = new DateTime(1960, 4, i, 0, 0, 0);
                var tz = TimeZoneInfo.Local;
                var utcOffset = new DateTimeOffset(dt, TimeSpan.Zero);
                //use timeZoneInfo class to account for dlst offset
                Console.WriteLine(utcOffset.ToOffset(tz.GetUtcOffset(utcOffset)));

            }

            Console.ReadKey();
        }
    }

Unfortunately, this is printing out: enter image description here

which shows that daylight savings is changing on April 3rd at 4 p.m., while it should be switching over at April 24th at 2:00 a.m. What am I missing to correctly account for daylight savings?

EDIT: My current time zone is eastern.

2
It's very hard to answer this without knowing what time zone you're talking about. Also bear in mind that the Windows idea of time zones isn't always the same as (say) TZDB.Jon Skeet
My timezone is eastern, but would that effect the date at which Daylight Savings is applied in 1960?Christian
@Christian, yes, rules for DST are different for timezones. There are even timezone(s) where the rule is that there is no rule justa arbitrary picked date.Alexei Levenkov
sorry, the issue is with daylight savings, not the timezone, I edited the wording where that was ambiguous.Christian
@Christian: Daylight saving is part of the time zone though. That's the point. I believe that even "Eastern" is ambiguous, as different places would have changed at different times. What is the ID of your TimeZoneInfo?Jon Skeet

2 Answers

2
votes

It looks like the Windows time zone information doesn't match what I'd have expected via TZDB. Here's a program using Noda Time to show all the transitions between ~1960 and 1965 with both the BCL TimeZoneInfo (wrapped) and the TZDB 2012i data:

using System;
using NodaTime;

class Test
{
    static void Main()
    {
        var bcl = DateTimeZoneProviders.Bcl["Eastern Standard Time"];
        var tzdb = DateTimeZoneProviders.Tzdb["America/New_York"];

        ShowTransitions(bcl);
        ShowTransitions(tzdb);
    }

    static void ShowTransitions(DateTimeZone zone)
    {
        Console.WriteLine("Transitions for {0}", zone.Id);
        Instant start = Instant.FromUtc(1960, 1, 1, 0, 0);
        Instant end = Instant.FromUtc(1965, 1, 1, 0, 0);
        var interval = zone.GetZoneInterval(start);
        while (interval.Start < end)
        {
            Console.WriteLine(interval.Start);
            interval = zone.GetZoneInterval(interval.End);
        }
        Console.WriteLine();
    }
}

Output:

Transitions for Eastern Standard Time
1959-10-25T06:00:00Z
1960-04-03T07:00:00Z
1960-10-30T06:00:00Z
1961-04-02T07:00:00Z
1961-10-29T06:00:00Z
1962-04-01T07:00:00Z
1962-10-28T06:00:00Z
1963-04-07T07:00:00Z
1963-10-27T06:00:00Z
1964-04-05T07:00:00Z
1964-10-25T06:00:00Z

Transitions for America/Toronto
1959-10-25T06:00:00Z
1960-04-24T07:00:00Z
1960-10-30T06:00:00Z
1961-04-30T07:00:00Z
1961-10-29T06:00:00Z
1962-04-29T07:00:00Z
1962-10-28T06:00:00Z
1963-04-28T07:00:00Z
1963-10-27T06:00:00Z
1964-04-26T07:00:00Z
1964-10-25T06:00:00Z

There are other time zone IDs which map to "Eastern Standard Time", but I haven't found any which match the Windows behaviour.

I don't think this is a TimeZoneInfo bug - I believe it's a potential problem in the underlying Windows time zone data.

If you want to match TZDB data, of course, you can just use Noda Time :)

1
votes

There is a dt.IsDaylightSavingTime(). And it changes on 4/4 for me (one day later than yours) in 1960 but I am CST. For 2012 it is correct on my system. It may be a bug. It may just be that year or all years up to X.

I think this explains it:

Daylight Saving Time

"In the early 1960s, observance of Daylight Saving Time was quite inconsistent, with a hodgepodge of time observances, and no agreement about when to change clocks."

"The Uniform Time Act of 1966 established a system of uniform (within each time zone) Daylight Saving Time throughout the U.S."