The Darkness Behind DateTime.Now
 
Published: 31 May 2011
Unedited - Community Contributed
Abstract
DateTime.Now is one of the commonly-used properties in the .NET Framework in the majority of applications designed. Although this property is designed to serve for particular purposes, the lack of understanding and training has driven many .NET developers to use it in wrong circumstances where other options like DateTime.UtcNow property and Stopwatch class should be used and are recommended. In this article we discuss these three options along with the main applications of each, and provide a quantitative comparison between them to show why DateTime.Now is expensive and should not be misused in many cases.
by Keyvan Nayyeri
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 39217/ 54

Introduction

Although there has been an incredible development for the .NET Framework in the past decade and many technologies and tools are added to enhance the programming experience for software developers, still there are some basic facts about .NET programming that are not taught well and have had an important impact on the performance of .NET applications written around the world and deployed in different scales.

One of these mistakes that has been common among many developers is the misuse of DateTime.Now property that is designed for particular purposes but is used widely in circumstances where DateTime.UtcNow property or Stopwatch class are recommended.

In this article we discover three options for working with DateTime values in the .NET Framework in order to clarify their internal workings, and where and when they should be used, and then we discover a quantitative comparison between these options to see why the use of DateTime.Now should be restricted.

DateTime.Now

A very commonly-used and well-known property of the DateTime structure in .NET is Now which simply returns the current date and time on the machine executing the code. Such a property is provided in almost all programming languages as a built-in feature and has many applications. Unfortunately, most of the .NET developers have been misusing this property for years for purposes other than what it’s supposed to serve for. I can outline two possible reasons for this problem:

·       Most of the developers are careless about all the properties and methods provided by built-in types in a language, so they don’t discover all the details about them. In fact, they use something that just solves their problem regardless of the side-effects.

·       The traditional use of similar methods in languages used by .NET developers in the past has left some bad habits for them while the internal working of this property is slightly different from what is available in some other languages.

I have to admit that I was a developer who used to apply this property for wrong purposes in the early years of my .NET development, and may still misuse that for simple codes where I don’t care much about performance, but it’s not something to use in each and every situation.

To clarify the use of the Now property of DateTime structure I have to state that this property is not designed to be used in cases such as when you want to retrieve the ti

me for internal calculations in your program, store a DateTime value in database, or calculate the runtime performance of a piece of code. In contrast, it is designed to be used when you want to display the current date and time on a machine to your users or store such a value in local log files where you want the local time to be used.

Although it’s hard to give a general guideline on good and bad uses of this property, I guess that the abovementioned examples can give you enough hints on when and where to use this property. A good question that you may ask about this property is why we distinguish these cases and consider some uses as bad practices. The answer is laid in the internal implementation of the Now property shown in listing 1.

Listing 1: Internal implementation of DateTime.Now

public static DateTime Now
{
      get
      {
            DateTime utcNow = DateTime.UtcNow;
            bool isAmbiguousDst = false;
            long ticks = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utcNow, 
                  out isAmbiguousDst).Ticks;
            long num = utcNow.Ticks + ticks;
            if (num > 3155378975999999999L)
            {
                  return new DateTime(3155378975999999999L, DateTimeKind.Local);
            }
            if (num < 0L)
            {
                  return new DateTime(0L, DateTimeKind.Local);
            }
            return new DateTime(num, DateTimeKind.Local, isAmbiguousDst);
      }
}

Essentially, this property is converting the current date and time in UTC to the local values and has an extra processing associated with this which is the source of the overhead that I will discuss later.

DateTime.UtcNow

Another commonly-used property of DateTime is UtcNow even though it’s not used as commonly as the Now property. This property returns the current date and time in UTC. This property is designed to serve for many places where Now is being misused and I listed some of the highlights in the previous section.

In general, if you’re going to store DateTime values in database or perform calculations on such values, it’s better to use UtcNow because in the former case, this helps you have a universal value regardless of the local time of the machine where you host your program and in the latter case there is no difference between the duration of time calculated by Now and UtcNow.

As you can see below, UtcNow has a simpler implementation than Now (listing 2) and in fact, Now is written based on UtcNow with some additions.

Listing 2: Internal implementation of DateTime.UtcNow

public static DateTime UtcNow
{
      [TargetedPatchingOptOut("Performance critical to inline across NGen 
            image boundaries"), SecuritySafeCritical]
      get
      {
            long systemTimeAsFileTime = DateTime.GetSystemTimeAsFileTime();
            return new DateTime((ulong)(systemTimeAsFileTime + 
                  504911232000000000L | 4611686018427387904L));
      }
}
Stopwatch

A lesser-used aspect of DateTime calculations in .NET is Stopwatch type that is designed to assist programmers to calculate the runtime performance of a piece of code. Most programmers tend to use DateTime.Now and DateTime.UtcNow before and after a piece of code to find the duration of time that it takes to execute.

Stopwatch relies on a public static method called GetTimeStamp which works in two modes. If it’s not used in high resolution mode, it applies DateTime.UtcNow, otherwise it applies QueryPerformanceCounter from Windows API (listing 3) that I will discuss in the next section.

Listing 3: GetTimeStamp implementation in Stopwatch

public static long GetTimestamp()
{
      if (Stopwatch.IsHighResolution)
      {
            long result = 0L;
            SafeNativeMethods.QueryPerformanceCounter(out result);
            return result;
      }
      return DateTime.UtcNow.Ticks;
}
QueryPerformanceCounter

While this is something that a .NET developer doesn’t need to use and care about, I have to point out that QueryPerformanceCounter is a Windows API method that is used by some built-in .NET types behind the scenes. This is considered an outdated approach in .NET development and you need to use interoperability APIs to have access to this function which provides access to a high-resolution performance counter (listing 4).

Listing 4: Using interoperability to apply QueryPerformanceCounter

[DllImport("Kernel32.dll")]
public static extern void QueryPerformanceCounter(ref long ticks);

In the next section I apply this method to compute the time taken to execute my benchmarks and the main reason is that I want to have a higher precision, so using a more fundamental operating system function can help.

Comparison

Nothing is better than seeing some numbers reflecting the points mentioned above, so here I try to write simple programs that compare the performance of DateTime.Now, DateTime.UtcNow, and Stopwatch. Normally, it’s hard to compare the properties of DateTime structure with Stopwatch as they are different by nature, however, thinking a little bit about good examples, it’s possible to relate them together and compare their runtime performance.

Here I develop three pieces of code that accomplish the same goal using these three approaches. I generate different sample sizes (incremented by a unit of 500 for 10 sample data batches) and perform a very basic (and senseless) task. I use DateTime.Now, DateTime.UtcNow, and Stopwatch to calculate the time that it takes to run my code. I measure my elapsed time using QueryPerformanceCounter to have a good precision.

Listing 5: Code to test DateTime.Now

private static void TestDateTimeNow()
{
    Console.WriteLine("Testing DateTime.Now ...");
 
    for (int sampleSize = 500; sampleSize <= 5000; sampleSize += 500)
    {
        long start = 0;
        QueryPerformanceCounter(ref start);
 
        for (int counter = 0; counter < sampleSize; counter++)
        {
            DateTime startTime = DateTime.Now;
 
                int dumbSum = 0;
                for (int temp = 0; temp < 5; temp++)
                    dumbSum++;
 
            DateTime endTime = DateTime.Now;
 
            TimeSpan duration = endTime - startTime;
        }
 
        long end = 0;
        QueryPerformanceCounter(ref end);
            
        long time = 0;
        time = end - start;
 
        Console.WriteLine("Sample Size: {0} - Time: {1}", sampleSize, time);
    }
}

A very similar code can be used with DateTime.UtcNow (listing 6).

Listing 6: Code to test DateTime.Now

private static void TestDateTimeUtcNow()
{
    Console.WriteLine("Testing DateTime.UtcNow ...");
 
    for (int sampleSize = 500; sampleSize <= 5000; sampleSize += 500)
    {
        long start = 0;
        QueryPerformanceCounter(ref start);
 
        for (int counter = 0; counter < sampleSize; counter++)
        {
            DateTime startTime = DateTime.UtcNow;
 
                int dumbSum = 0;
                for (int temp = 0; temp < 5; temp++)
                    dumbSum++;
 
            DateTime endTime = DateTime.UtcNow;
 
            TimeSpan duration = endTime - startTime;
        }
 
        long end = 0;
        QueryPerformanceCounter(ref end);
            
        long time = 0;
        time = end - start;
 
        Console.WriteLine("Sample Size: {0} - Time: {1}", sampleSize, time);
    }
}

And finally, Stopwatch can be applied to measure the duration of time to execute this code using its Start and Stop methods as well as its Elapsed property (listing 7).

Listing 7: Code to test Stopwatch

private static void TestStopwatch()
{
    Console.WriteLine("Testing Stopwatch ...");
 
    for (int sampleSize = 500; sampleSize <= 5000; sampleSize += 500)
    {
        long start = 0;
        QueryPerformanceCounter(ref start);
 
        for (int counter = 0; counter < sampleSize; counter++)
        {
            Stopwatch watch = new Stopwatch();
 
            watch.Start();
 
            int dumbSum = 0;
            for (int temp = 0; temp < 5; temp++)
                dumbSum++;
 
            watch.Stop();
 
            TimeSpan duration = watch.Elapsed;
        }
 
        long end = 0;
        QueryPerformanceCounter(ref end);
 
        long time = 0;
        time = end - start;
 
        Console.WriteLine("Sample Size: {0} - Time: {1}", sampleSize, time);
    }
}

Sometimes a picture is worth a thousand words and figure 1 can reflect whatever I’ve mentioned so far.

Figure 1: Quantitative comparison between DateTime.Now, DateTime.UtcNow, and Stopwatch

Here we see some interesting results and it’s more interesting when we consider the fact that these codes are tested with realistic sample data sizes between 500 and 5000. Here DateTime.Now (the blue line) has the worst performance at top followed by Stopwatch (the green line) and DateTime.UtcNow (the red line). UtcNow stands much lower than the other options with a very steady pace (unlike other two options) and surprisingly, performs better than Stopwatch. This is of course expected as I showed you the internal implementation of Stopwatch which is using DateTime.UtcNow with some extra processing.

Here a good question is why we need to use Stopwatch when DateTime.UtcNow is performing better. The answer is that it’s possible to do that and actually, it’s much better if you’re sure that you get such a significant difference by applying UtcNow rather than Stopwatch. But there is a major and a minor advantage for Stopwatch over UtcNow. The major advantage is that Stopwatch can perform in a higher resolution by applying QueryPerformanceCounter rather than DateTime.UtcNow. The minor advantage is that Stopwatch provides a more fluent and human-readable method to perform this task.

Summary

Many .NET developers misuse the properties of DateTime structure and apply Now for purposes where it’s not designed for which reduces the performance of the code. UtcNow is what should be used for many cases rather than Now, and even for timing measurements there is a particular class called Stopwatch.

Seeing a quantitative comparison between these approaches visualized in a simple diagram, you can understand why it’s recommended to use each property or type in its own place. I am very sure that the misuse of these options in the .NET codes written in the past decade has had a major impact on many .NET applications and I hope that better training on this topic and similar ones prevents such mistakes from happening.

I’ve uploaded the sample code for this post that you can run on your machine to compare these three approaches yourself.

Download

[Download Sample]

The source code sample for this article is available for download.



User Comments

Title: 你们不能明白   
Name: 巴拿马
Date: 2012-12-08 3:41:36 AM
Comment:
巴拿马






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-03-28 6:07:45 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search