The case against DateTime.Now

Posted on April 25, 2013

3


One of the first things most .Net developers learn to do with DateTime is to get the current value from the system clock.

DateTime now = DateTime.Now;

This is probably the most horrible introduction to date and time that you could have. In my less-than-humble opinion, this is one method that should be deprecated from the .Net framework. Let's look at some of the deficiencies:

The "Local" kind is not all that useful

If you examine the DateTime.Kind property, you will find that it can be one of three values, Utc, Local, or Unspecified.

  • The Utc kind is very useful.  It unambiguously identifies the instant in time that the value represents.  Anyone told that these are UTC times will know what they mean. (They do have to be told that separately though.)
  • As strange as it may seem, the Unspecified kind is actually fairly useful.  While it can't resolve to a particular instant, it does represent a point on a calendar.  It doesn't pretend to know who's calendar it is talking about.  Sometimes, that's exactly what we need.
  • But a Local kind puts us in an uncomfortable position - we pretend to be representing something meaningful, when really the value we have is completely ambiguous.  The fact that the time was local is lost as soon as you pass it off to someone else.  Their idea of "local" might be completely different than yours.  In other words - a Local kind becomes Unspecified when the DateTime crosses any type of context boundary.

Who cares about the server's time anyway?

A great deal of code that we write ends up executing on a server.  In the modern era of cloud computing, we have no guarantees about where that server might be located.  Calling DateTime.Now from an ASP.Net web page, or a web service, or the equivalent from a databases (such as getdate() in SQL Server) provides a result that is often meaningless.

Ideally, everyone should set their servers to UTC.  That avoids a lot of issues with how operating systems track time zone changes and sync with the computer's BIOS.  But there are no guarantees of this.  One can't just take the value from DateTime.Now that came from a server and use it in some random client.  Chances are - that client's time zone is completely different than the server's.

Local kinds don't round trip!

Ok, let's just assume for a moment that you are writing code that will never go out of your local office.  You have a database server somewhere on your LAN (or on your own computer perhaps), and your network admin has all machines running in the same time zone.  Let's take the value from DateTime.Now and store it to the database (using the proper parameratized inputs in ADO.Net or Entity Framework, for example).  Then go and retrieve it back from the database into a DateTime field.  One would expect that since the value was taken locally, persisted locally, and retrieved locally, that it would still be local.  But no! Take a look at the .Kind property and now it is Unspecified!

This is actually a good thing, because we didn't transmit any time zone information to the database.  It would be bad if we made the assumption that it was local when we retrieved it.  But then, what good is Local for then?

Daylight Saving Time

People often forget that computer clocks are adjusted when transitioning to or from Daylight Saving Time.  Have you ever seen (or written) code like this?

DateTime then = DateTime.Now;
// ... do some work ...
DateTime now = DateTime.Now;
TimeSpan duration = now - then;

If so, and your code is running at the right moment, then you have introduced a major problem that will show up at least twice a year in many places around the world. In the spring, when Daylight Saving Time (aka "Summer Time") is introduced, you could have a full extra hour in your calculated duration! Then in the fall, when Daylight Saving Time ends and standard time is restored, you could actually have a negative value for your duration!

Clearly, if you want to measure the elapsed time of some operation, you should be using the Stopwatch class. But sometimes you can't do that. Perhaps you are leaving this thread and coming back at a much later time.

Ok then, what should I use instead?

If you want to stick to DateTime, the only appropriate property that unambiguously represents a moment in time is DateTime.UtcNow. This will give you the current date and time at UTC, using the UTC kind. If you do any math on these values, like the code above, your answers will be correct (although not as precise as a Stopwatch).

But many times, that's not good enough. I may actually want to know the time of the local computer. Maybe I'm writing a WinForms or WPF client application that is talking to the client's own clock. In that case, you should get to know and love the DateTimeOffset object. If you aren't familiar with this object, you should look at my write-up at StackOverflow.

We have two options to poll the system clock as a DateTimeOffset:

// Get the computer's local time
DateTimeOffset dto1 = DateTimeOffset.Now;

// Get the computer's time in UTC
DateTimeOffset dto2 = DateTimeOffset.UtcNow;

Either of these values will accurately represent a single instant in time. If we are asking for DateTimeOffset.Now, we will get a value that represents the local time on the computer where the code is running - but the offset we get will tell us how that value relates to UTC, and will yield a different offset when in standard time or Daylight Saving Time.

TL;DR

When getting the current system time, values such as 2013-04-25 9:26 AM are not very useful except for display. What we want instead is 2013-04-25 9:26 AM -0700. That way, when the values are stored or transmitted, we don't lose track of the context. This is easily done from .Net with DateTimeOffset.Now, so there really isn't any reason to use DateTime.Now at all. If we don't want to show the offset for display, we don't have to. And we can easily (and implicitly) use a DateTimeOffset in place of where a DateTime would normally go.

Call For Radical Change!

Please, Microsoft - in the next version of .Net framework, mark DateTime.Now and DateTimeKind.Local as [Obsolete]. Developers should be encouraged to use DateTimeOffset.Now instead.

Additional Reading

Even more details on this subject are in Jon Skeet's excellent post, What's wrong with DateTime anyway?. This is the basis and inspiration for his excellent NodaTime library, which provides a much better API for working with dates and times. I highly recommend using NodaTime over any of the built-in date time objects whenever possible.

Posted in: .Net, DateTime