Sdílet prostřednictvím


DateTime.UtcNow is generally preferable to DateTime.Now

This seems to be commonly known and accepted best practice to use DateTime.UtcNow for non-user facing scenarios such as time interval and timeout measurement.

I’ve just done an audit of the Roslyn codebase and replaced most DateTime.Now calls with DateTime.UtcNow. I thought it’d be useful to post my changeset description here (although none of it is new – I just summarize some common knowledge readily available in the sources linked below).

====

Replacing DateTime.Now with DateTime.UtcNow in most cases.

We should be using DateTime.Now only in user-facing scenarios. It respects the timezone and Daylight Savings Time (DST), and the user feels comfortable when we show them the string that matches their wall clock time.

In all other scenarios though DateTime.UtcNow is preferable.

First, UtcNow usually is a couple of orders of magnitude faster, since Now is basically first calling UtcNow and then doing a very expensive call to figure out the time zone and daylight savings time information. Here’s a great chart from Keyvan’s blog (modified to avoid allocations of the Stopwatch object):

image

Second, Now can be a big problem because of a sudden 1-hour jump during DST adjustments twice a year. Imagine a waiting loop with a 5-sec timeout that happens to occur exactly at 2am during DST transition. The operation that you expect to timeout after 5 sec will run for 1 hour and 5 seconds instead! That might be a surprise.

Hence, I'm replacing DateTime.Now with DateTime.UtcNow in most situations, especially polling/timeout/waiting and time interval measurement. Also, everywhere where we persist DateTime (file system/database) we should definitely be using UtcNow because by moving the storage into a different timezone, all sorts of confusion can occur (even "time travel", where a persisted file can appear with a future date). Granted, we don't often fly our storage with a supersonic jet across timezones, but hey.

For precise time interval measurements we should be using System.Diagnostics.StopWatch which uses the high resolution timer (QueryPerformanceCounter). The resolution of both DateTime.Now and DateTime.UtcNow is very low – about 10 ms – this is a huge time interval!

For a cheap timestamp, we should be generally using Environment.TickCount - it's even faster than DateTime.UtcNow.Ticks.

I'm only leaving DateTime.Now usages in test code (where it's never executed), in the compiler Version parsing code (where it's used to generate a minor version) and in a couple of places (primarily test logging and perf test reports) where we want to output a string in a local timezone.

Sources:

* https://www.keyvan.ms/the-darkness-behind-datetime-now
* https://stackoverflow.com/questions/62151/datetime-now-vs-datetime-utcnow
* https://stackoverflow.com/questions/28637/is-datetime-now-the-best-way-to-measure-a-functions-performance

Comments

  • Anonymous
    January 10, 2012
    Great tip, never thought about it, even while seeing performance reports pointing to DateTime.Now costs. The small things that make difference. I didn't know I had a cheap alternative.

  • Anonymous
    January 10, 2012
    IMHO using DateTimeOffset (datetimeoffset type in SQL Server) is better for many applications since you 'free' yourself from having to worry about converting to or from UTC - when it comes time to display it to the user, you can have it display in their timezone regardless of what it started out as. So many headaches/bugs come up from people forgetting to 'normalize' onto UTC, that it really doesn't seem worth using 'just' DateTime in the vast majority of cases. And there's still DateTimeOffset.UtcNow to use for that case, which should be very fast since it's DateTime.UtcNow with TimeSpan.Zero :)

  • Anonymous
    January 10, 2012
    The comment has been removed

  • Anonymous
    January 10, 2012
    Consider not using DateTime.UtcNow either but a 'SystemTime' abstraction. Reading DateTime.UtcNow can also be significantly optimized. dhickey.ie/.../SystemClock.aspx

  • Anonymous
    January 11, 2012
    Folks, I just noticed that Keyvan's benchmark counts allocations of the Stopwatch object. If he reuses the same Stopwatch instance, the perf will be much much better than shown on his graph. I've posted my own graph in the post above without allocations for Stopwatch.

  • Anonymous
    January 11, 2012
    Oleg - we just use it as a random number generator when we need a random looking version :)

  • Anonymous
    January 11, 2012
    James - also thanks for the DateTimeOffset tip :)

  • Anonymous
    January 11, 2012
    @Kirill Could you add the labels for the xy-axis on the chart. I am not sure what we are comparing. I just see numbers only which is not that informative. Thanks.

  • Anonymous
    January 11, 2012
    Sk - sorry about that - follow the link to Keyvan's article, he has detailed explanations and the benchmark source code.