DateTime arithmetic and Daylight Saving Time

Date and time calculations done with local times can produce erroneous results. For example, consider this code, which adds one day to March 9, 2013 at 2:01 AM.

    DateTime localStart = new DateTime(2013, 03, 09, 02, 01, 00);  // 2:00 AM on 03/09/2013
    DateTime localResult = localStart.AddDays(1.0);

    Console.WriteLine("Local start date/time = {0}", localStart);
    Console.WriteLine("Result of AddDays(1.0) = {0}", localResult);

The result will be March 10, 2013 2:01 AM. That seems right, but in my local time zone it’s incorrect. 2:01 AM never existed on that date. When 2:00 AM rolled around, the clock was zipped forward an hour for the annual Daylight Saving Time “spring ahead” adjustment.

The documentation for DateTime says:

Conversion operations between time zones (such as between UTC and local time, or between one time zone and another) take daylight saving time into account, but arithmetic and comparison operations do not.

The same thing happens with DateTimeOffset.

If you want to do date and time calculations, you should convert the values to UTC first. Perform your calculations and then convert the results back to local time. For example, this code produces the correct result:

    DateTime utcStart = new DateTime(2013, 03, 09, 02, 01, 00).ToUniversalTime();
    DateTime utcResult = utcStart.AddDays(1.0);

    Console.WriteLine("UTC start date/time = {0}", utcStart);
    Console.WriteLine("Result of AddDays(1.0) = {0}", utcResult);

    Console.WriteLine();

    Console.WriteLine("UTC result converted to local time = {0}", utcResult.ToLocalTime());

This is just one of the many problems that can occur when working with dates and times. I’ve found through hard experience that if you’re storing dates and times, you should store them as UTC. And when a user inputs a date and time, you should convert it to UTC as soon as possible. All datetime calculations and all datetime persistence should be UTC.

You could, if you wanted to keep track of where a particular data item was collected, persist times as DateTimeOffset, with the proper offset applied. Remember, though, to convert to UTC before doing any calculations.

Whichever you choose, be consistent. Otherwise you’re going to lose information and produce erroneous results.

Be careful with static classes

I’ve been known to say, “Static classes are evil.” That’s overstating my position somewhat, but a static class will definitely raise a red flag in a code review. The plain truth is that in a language like C# there’s seldom a need for a static class, and there are compelling reasons not to use them. They have their uses, but in my experience you should favor class instances over static classes whenever reasonable.

In C#, static class constructors run immediately before the first static method on its class is called, or (in the case of an instance class that has static members) immediately before the first instance of its class is created. Eric Lippert explains this in detail in his article series about static constructors.

It’s important to understand that, in general, you have no control over when the constructor runs. In addition, you can’t pass parameters to the static constructor. The constructor must have all the information it needs in order to fully initialize the class and all of its members. If the constructor can’t fully initialize things, then what you have is equivalent to a partially initialized object at global scope.

Let me illustrate why this is a bad thing.

First, suppose you have this (non-static) class:

    public class Foo
    {
        public Foo() {}  // parameterless constructor
        public void Initialize(string bar); // sets up the object
        // DoSomeWork does something that depends on Initialize being called.
        public void DoSomeWork();
    }

And you use it in code like this:

    private void SomeMethod()
    {
        var f = new Foo();
        Foo.Initialize("xyzzy");
        Foo.DoSomeWork();
    }

The key here is that if you don’t call Initialize first, then DoSomething is going to fail. This API is somewhat fragile because nothing stops the programmer from forgetting to initialize. In almost all cases, it would be better to have the constructor initialize the object:

    public class Foo
    {
        public Foo(string bar) {}  // create and initialize the object
        public void DoSomeWork();
    }

Now, it’s impossible to create an uninitialized object, meaning that it’s always safe to call DoSomeWork.

As bad as this API is, it’s not too bad because you control the scope of the uninitialized object. It’s not like code in some other method can reach in and access your Foo instance.

Now imagine you create a static class with a similar interface:

    public static class Foo
    {
        static Foo() {}  // static class constructor
        public static void Initialize(string bar); // sets up the object
        // DoSomeWork does something that depends on Initialize being called.
        public static void DoSomeWork();
    }

Remember, the static class is at global scope. Any part of your program can reference the namespace and call methods on this class. They don’t have to construct it. It’s all too easy to write somewhere in your program:

    Foo.DoSomeWork();

If that’s the first reference to Foo in your program, the constructor will run, Initialize won’t be called, and your static class will either crash and burn or, probably worse, do something with the default values. You better hope that your defaults aren’t dangerous.

If your static class requires any explicit initialization on your part (i.e. you have to call an Initialize method), then you probably should revisit your design. It’s almost a certainty that you should re-code that to be an instance class with a constructor that accepts a parameter and creates a fully initialized object. I think you’ll discover that your code is cleaner without the static classes.

Once again: do not use the clock to measure time

In my post, Don’t depend on what you don’t control, I explained why you shouldn’t depend on the system clock for elapsed time measurements. That is, consider this code:

    var startTime = DateTime.Now;
    // perform some long-running operation
    var endTime = DateTime.Now;
    // compute elapsed time
    var elapsed = endTime - startTime;

That code will produce erroneous values if the system clock changes between time checks. Imagine your surprise when you discover that it took -56 minutes to run a 5-minute job, because you just happened to start the job at one minute before 2:00 AM on the day we set our clocks back for Daylight Saving Time.

The system clock can change at any time. You don’t control it.

There’s another way this can trip you up. Say you want your program to do something at 4:00 AM. You start the program some hours before that and set a timer to wake up when the prescribed amount of time has elapsed:

    TimeSpan delayTime = Settings.WakeupTime - DateTime.Now;
    System.Threading.Timer t = new System.Threading.Timer(
        TimerTickHandler, null, delayTime.TotalMilliseconds, -1);

That creates a one-shot timer that will execute TimerTickHandler when the specified amount of time has elapsed.

But it’s “spring ahead” weekend. You wanted the program to wake up at 4:00 AM, but the clock skipped ahead an hour so it’s 5:00 AM before the timer tick happens. Oops.

The cause is the same: assuming that the system clock increases one second for every second of time that elapses. It’s an incorrect assumption.

When the system clock changes due to an external event, any time interval based on the previous setting is invalid.

There are two ways around this problem. One is to create a timer that ticks every minute (or every second, depending on how accurate you want to be). The timer’s callback then checks the current system clock against the desired wakeup time and does whatever it’s supposed to do when they match. This works well, but I find it intellectually dissatisfying because it’s polling. Polling for time is such a waste of resources. (Never mind that a few instructions every second won’t be noticed.)

A better solution is to use a Waitable Timer. Unfortunately, the .NET Framework doesn’t have a Waitable Timer object. But one is available. See my article Waitable Timers in .NET with C#. Full source code is available at http://www.mischel.com/pubs/waitabletimer.zip.

That’s just wrong!

The C# compiler will convert expressions that use the “+” operator on strings into calls to the string.Concat function. So, for example, in this code:

    string a = "hello, ";
    string b = "world";
    string s = a + b;

The compiler changes the last line to the equivalent of:

    string s = string.Concat(a, b);

Seems reasonable, right?

Consider also Console.WriteLine, which treats its arguments as objects, and calls ToString on them. So this code:

    int a = 10;
    int b = 27;
    Console.WriteLine(a + b);

Gets converted to the equivalent of:

    int temp = a + b;
    Console.WriteLine(temp.ToString());

So what happens when you write this?

    Console.WriteLine(a + b + "foo");

You probably won’t be surprised to see the output “37foo”.

How about this one?

    Console.WriteLine(a + (b + "foo"));

The result? “1027foo”!!

So how does that work? The compiler converted that code to:

    string s = String.Concat(a, b, "foo");
    Console.WriteLine(s);

You see, there are String.Concat overloads that treat the arguments as type object. Those methods just call ToString on each argument, and concatenate the results.

That was pretty surprising. Consider this:

    int a = 10;
    int b = 27;
    string s1 = a + b;  // error: can't convert int to string
    string s2 = (a + b) + "foo"; // no problem, "37foo"
    string s3 = a + (b + "foo"); // no problem, "1027foo"

I understand why this happens. I don’t understand why the compiler writers thought it was a good thing. It leads to some very confusing results:

    int m = 10;
    object o = new object();
    string f = "foo";
    var s1 = m + o + f; // error: cannot apply operator "+" to types int and object
    var s2 = m + f + o; // hey, no problem.

That’s just wrong. It’s logically consistent, once you understand the logic. But it’s pretty darned confusing when you first look at it.