2

Is there a way to determine the smallest elapsed time between two ticks of DateTime.Now? I could time it with a stopwatch, but is there some method for the operating system to report this in .NET?

In other words - how accurate is DateTime.Now?

Update

This is important, as I am writing a DateTimePrecision class which stores both DateTime.Now, ticks from StopWatch, and the Stopwatch ticks-per-second constant. When working out the TimeSpan between two DateTimePrecision values, if the difference is less than the quantum for DateTime.Now, then use Stopwatch, otherwise use DateTime.Now.

Update

Several people have queried why its necessary to use DateTime at all, if you are comparing two times.

The reason is that StopWatch will slowly drift over a period of hours or days, until your timing is seconds out.

I correct for this by storing both DateTime and Stopwatch in a structure, and using a metric to return the one which is currently either the most precise, or the most accurate.

This way I have:

  • Microsecond precision when comparing times A and B that are a few seconds apart;
  • Millisecond precision when comparing times A and B that are days apart.

In my timer class, if two times are being compared to calculate a delta, the switchover point is determined by a metric which takes System.Runtime.IOThreadTimer.GetSystemTimeResolution() into account.

The absolute error of the delta between two times A and B:

  • Starts out accurate to the microsecond;
  • Drifts up, over a few hours or days, to a maximum of System.Runtime.IOThreadTimer.GetSystemTimeResolution() which is a few tens of milliseconds.

Again, relying on Stopwatch by itself will result in deltas that are seconds out, if you are measuring across many days.

Update

If you are implementing this yourself, make sure you sure you store Stopwatch.Frequency in the structure as well. This could change if you swap hardware, or change operating systems. If you persist instances of DateTimePrecision, you won't be able to read them back on a different machine without knowing how fast the ticks are coming in.

Here is the beta code I have so far:

    using System;
    using System.Diagnostics;
    using System.IO;
    using MyLogType;
    using ProtoBuf;

    namespace DateTimePrecisionNamespace
    {               
        /// <summary>
        /// This class returns a precision time.
        /// This class combines the best of both worlds, both precision and accuracy.
        /// - It contains a DateTime.Now, which gives us good absolute time during the day.
        /// - It contains the output from a stopwatch, in ticks, which gives us good relative time during the day.
        /// - It contains the ticks per second of the stopwatch, which means this time record is portable across multiple PC architectures,
        ///   and we can easily tell how accurate the original time was.
        /// Class always deals in non-UTC, this is a design decision as we are working with Eurex and we want to reduce the possibility of errors.
        /// Class is serialized using Google Protocol Buffers, so any files created by this serializer are highly compatible, and can be read by:
        /// - Future versions of .NET (ISerializable is only guaranteed to support the current version of .NET).
        /// - Other .NET languages such as Python .NET, etc.
        /// - Other non-.NET languages such as C++, Java, Python, etc.
        /// - Other hardware platforms such as Linux, Mac, etc.
        /// - Other endians (what if the platform is BigEndian or LittleEndian?).
        /// - Future versions of the struct which may add additional fields or change existing fields 
        ///   (the numbering of elements means its backwards and fowards compatible without necessarily breaking anything).
        /// </summary>
        [ProtoContract] // Serializable with Google Protocol Buffers, see protobuf-net.
        public struct MyDateTimePrecision : IEquatable<MyDateTimePrecision>
        {
            [ProtoMember(1)]
            public DateTime MyDateTime;

            // Debug: display the expiration date as a string.
            public string MyDateTimeAsString { get { return MyDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffff"); } }
            public long MyDateTimeAsTicks { get { return MyDateTime.Ticks; } }

            [ProtoMember(2)]
            public int StopwatchTicksPerSecondConstant;

            [ProtoMember(3)]
            public long StopwatchTicksSinceProgramStart;


            public MyDateTimePrecision(DateTime myDateTime, Int64 stopwatchTicksSinceProgramStart)
            {
                MyDateTime = myDateTime;

                // This is always a constant. We need to embed this metric in the timestamp so this is portable to different PC hardware in the future.
                StopwatchTicksPerSecondConstant = MyDateTimePrecisionStatic.MyGetStopwatchTicksPerSecondConstant;

                StopwatchTicksSinceProgramStart = stopwatchTicksSinceProgramStart;          
            }

            public MyDateTimePrecision(DateTime myDateTime, Int32 stopwatchTicksPerSecondConstant, Int64 stopwatchTicksSinceProgramStart)
            {
                MyDateTime = myDateTime;

                // This is always a constant. We need to embed this metric in the timestamp so this is portable to different PC hardware in the future.
                StopwatchTicksPerSecondConstant = stopwatchTicksPerSecondConstant;

                StopwatchTicksSinceProgramStart = stopwatchTicksSinceProgramStart;
            }

            /// <summary>
            /// Returns the current precision time.
            /// </summary>
            public static MyDateTimePrecision Now
            {
                get
                {
                    return new MyDateTimePrecision(
                        MyDateTimePrecisionStatic.MyGetDateTime,
                        MyDateTimePrecisionStatic.MyGetStopwatchTicksPerSecondConstant,
                        MyDateTimePrecisionStatic.MyGetStopwatchTicksSinceProgramStart);    
                }           
            }

            /// <summary>
            /// Returns the current time, in ticks, since the program has started.
            /// </summary>
            public static long NowTicksSinceProgramStart
            {
                get { return MyDateTimePrecisionStatic.MyGetStopwatchTicksSinceProgramStart; }
            }

            /// <summary>
            /// Returns the the amount of ticks per second, as a constant.
            /// </summary>
            public static long NowTicksPerSecondConstant
            {
                get { return MyDateTimePrecisionStatic.MyGetStopwatchTicksPerSecondConstant; }
            }

            /// <summary>
            /// Returns the relative time, in seconds since the class was instantiated.
            /// This method is only used to gauge the difference between two points in time, accurate to 300ns.
            /// To get the absolute time, use DateTimeUtcAbsolute.
            /// </summary>
            public double SecondsRelative
            {
                get
                {
                    return ((double)StopwatchTicksSinceProgramStart/StopwatchTicksPerSecondConstant);   
                }           
            }

            #region Google Protocol Buffers serializer.
            /// <summary>
            /// Serialize using Google Protocol Buffers.
            /// </summary>
            public byte[] SerializeUsingProtobuf()
            {
                byte[] data;
                using (var ms = new MemoryStream())
                {
                    Serializer.Serialize(ms, this);
                    data = ms.ToArray();
                }
                return data;
            }
            #endregion

            #region Google Protocol Buffers deserializer.
            /// <summary>
            /// Deserialize using Google Protocol Buffers.
            /// </summary>
            public static MyDateTimePrecision DeserializeUsingProtobuf(byte[] data)
            {
                MyDateTimePrecision result;
                using (var ms = new MemoryStream(data))
                {
                    result = Serializer.Deserialize<MyDateTimePrecision>(ms);
                }
                return result;
            }
            #endregion

            #region SerializeUsingPointers
            /// <summary>
            /// Serialize using pointers, and raw binary format.
            /// This method is blindingly fast, but not guaranteed to be compatible with anything other than the current version of the .NET runtime.
            /// </summary>
            public byte[] SerializeUsingPointers()
            {
                unsafe
                {
                    const int bufferLength = 8+4+8;
                    byte[] buffer = new byte[bufferLength];
                    fixed (byte* constPointerToBufferStart = buffer)
                    {
                        byte* pointerToBuffer = constPointerToBufferStart;

                        (*(Int64*)pointerToBuffer) = this.MyDateTime.ToBinary();
                        pointerToBuffer += sizeof(Int64);

                        (*(Int32*) pointerToBuffer) = this.StopwatchTicksPerSecondConstant;
                        pointerToBuffer += sizeof(Int32);

                        (*(Int64*)pointerToBuffer) = this.StopwatchTicksSinceProgramStart;
    #if UNITTEST
                        pointerToBuffer += sizeof(Int64);
                        if (pointerToBuffer - constPointerToBufferStart != bufferLength)
                        {
                            MyLog.LogFatalAndThrowAndExit("Error E20111004-1731. Buffer is not the expected length within SerializeUsingPointers.\n");
                        }
    #endif
                    }
                    return buffer;
                }               
            }
            #endregion

            /// <summary>
            /// Deserialize using pointers.
            /// This method is blindingly fast, but not guaranteed to be compatible with anything other than the current version of the .NET runtime.
            /// </summary>
            public static MyDateTimePrecision DeserializeUsingPointers(byte[] buffer)
            {
                MyDateTimePrecision result;
    #if UNITTEST
                const int bufferLength = 8 + 4 + 8;
    #endif
                unsafe
                {
                    fixed (byte* constPointerToBufferStart = buffer)
                    {
                        byte* pointerToBuffer = constPointerToBufferStart;

                        result.MyDateTime = DateTime.FromBinary((*(Int64*)pointerToBuffer));
                        pointerToBuffer += sizeof(Int64);

                        result.StopwatchTicksPerSecondConstant = (*(Int32*)pointerToBuffer);
                        pointerToBuffer += sizeof(Int32);

                        result.StopwatchTicksSinceProgramStart = (*(Int64*)pointerToBuffer);
    #if UNITTEST
                        pointerToBuffer += sizeof(Int64);
                        if ((pointerToBuffer - constPointerToBufferStart != buffer.Length) || (buffer.Length != bufferLength))
                        {
                            MyLog.LogFatalAndThrowAndExit("Error E20111004-1732. Buffer is not the expected length within DeserializeUsingPointers.\n");
                        }
    #endif
                    }           
                }
                return result;
            }

            /// <summary>
            /// Checksum for the data contained in this structure, based on SerializeUsingPointers.
            /// </summary>
            /// <returns>Checksum.</returns>
            public long ChecksumFromProtobuf()
            {
                return SerializeUsingProtobuf().MyToChecksum();
            }

            /// <summary>
            /// Checksum for the data contained in this structure, based on XORing the contents of this structure.
            /// </summary>
            /// <returns>Checksum.</returns>
            public long ChecksumFromXor()
            {
                return this.MyDateTime.Ticks
                       ^ this.StopwatchTicksPerSecondConstant
                       ^ this.StopwatchTicksSinceProgramStart;
            }

            /// <summary>
            /// Indicates whether the current object is equal to another object of the same type.
            /// </summary>
            /// <returns>
            /// True if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
            /// </returns>
            /// <param name="other">An object to compare with this object.</param>
            public bool Equals(MyDateTimePrecision other)
            {
                return other.MyDateTime.Equals(MyDateTime) && other.StopwatchTicksPerSecondConstant == StopwatchTicksPerSecondConstant && other.StopwatchTicksSinceProgramStart == StopwatchTicksSinceProgramStart;
            }

            /// <summary>
            /// Override operator == to compare two MyDateTimePrecision variables.
            /// </summary>
            /// <param name="c1">First MyDateTimePrecision.</param>
            /// <param name="c2">Second MyDateTimePrecision.</param>
            /// <returns>True if equal, false if not.</returns>
            public static bool operator ==(MyDateTimePrecision c1, MyDateTimePrecision c2)
            {
                return c1.Equals(c2);
            }

            /// <summary>
            /// Override operator != to compare two MyDateTimePrecision variables.
            /// </summary>
            /// <param name="c1">First MyDateTimePrecision.</param>
            /// <param name="c2">Second MyDateTimePrecision.</param>
            /// <returns>True if not equal, false if equal.</returns>
            public static bool operator !=(MyDateTimePrecision c1, MyDateTimePrecision c2)
            {
                return !c1.Equals(c2);
            }

            /// <summary>
            /// Print out both the absolute and the relative time.
            /// Useful, as the debugger in Visual Studio displays this string when you observe the variable.
            /// </summary>
            /// <returns>The string.</returns>
            public new string ToString()
            {
                return String.Format("Abs:{0:yyyy-MM-dd HH:mm:ss},Rel:{1:0.000000}sec", MyDateTime, SecondsRelative);
            }

            #region Unit test.
            /// <summary>
            /// Unit test for this entire class.
            /// </summary>
            /// <returns>False if there is no errors (false is the default for all new unit tests; saves typing).</returns>
            public static bool Unit()
            {
                // Check serialization using pointers.
                {
                    MyDateTimePrecision first = MyDateTimePrecision.Now;
                    MyDateTimePrecision second = first;
                    Debug.Assert(first == second);

                    {
                        byte[] sFirst = first.SerializeUsingPointers();
                        MyDateTimePrecision third = MyDateTimePrecision.DeserializeUsingPointers(sFirst);
                        Debug.Assert(first == third);
                        Debug.Assert(first.ChecksumFromProtobuf() == third.ChecksumFromProtobuf());                 
                    }

                    {
                        byte[] sFirst = first.SerializeUsingProtobuf();
                        MyDateTimePrecision third = MyDateTimePrecision.DeserializeUsingProtobuf(sFirst);
                        Debug.Assert(first == third);
                        Debug.Assert(first.ChecksumFromProtobuf() == third.ChecksumFromProtobuf());                 
                    }

                    {
                        try
                        {
                            byte[] sFirst = first.SerializeUsingProtobuf();
                            MyDateTimePrecision third = MyDateTimePrecision.DeserializeUsingPointers(sFirst);
                            // Program should never get to here as this should throw an error for an unknown buffer length.
                            Debug.Assert(true == false);
                        }
                        catch (Exception)
                        {   
                            // Program should get to here.
                            Debug.Assert(true);
                        }
                    }

                    {
                        MyDateTimePrecision third = MyDateTimePrecision.Now;
                        Debug.Assert(first != third);
                        Debug.Assert(first.ChecksumFromProtobuf() != third.ChecksumFromProtobuf());
                    }
                }
                return false;
            }
            #endregion

            #region Windows serializer.
            /*
            /// <summary>
            /// Serialize this object into a string.
            /// Observe that this method creates binary code that is only portable within the same version of .NET.
            /// Recommend using a faster serializer that is language, hardware, and .NET version independent, such as Google Protocol Buffers (see protobuf-net).
            /// </summary>
            /// <returns></returns>
            public string SerializeToString()
            {
                MyDateTimePrecision obj = this;
                string result;
                IFormatter formatter = new BinaryFormatter();
                using (Stream stream = new MemoryStream())
                {
                    formatter.Serialize(stream, obj);

                    result = stream.ToString();
                }
                return result;
            }

            /// <summary>
            /// Serialize this object into a byte array.
            /// Observe that this method creates binary code that is only portable within the same version of .NET.
            /// Recommend using a faster that is language, hardware, and .NET version independent, such as Google Protocol Buffers (see protobuf-net).
            /// </summary>
            /// <returns></returns>
            public byte[] SerializeToByteArray()
            {
                MyDateTimePrecision obj = this;
                byte[] bytes;
                IFormatter formatter = new BinaryFormatter();
                using (MemoryStream stream = new MemoryStream())
                {
                    formatter.Serialize(stream, obj);

                    bytes = stream.ToArray();
                }
                return bytes;
            }
            */
            #endregion

            public override bool Equals(object obj)
            {
                if (ReferenceEquals(null, obj)) return false;
                if (obj.GetType() != typeof (MyDateTimePrecision)) return false;
                return Equals((MyDateTimePrecision) obj);
            }

            public override int GetHashCode()
            {
                unchecked
                {
                    int result = MyDateTime.GetHashCode();
                    result = (result*397) ^ StopwatchTicksPerSecondConstant;
                    result = (result*397) ^ StopwatchTicksSinceProgramStart.GetHashCode();
                    return result;
                }
            }
        }       

                    /// <summary>
        /// This class starts a stopwatch when the program starts. We can query this value in MyDateTimePrecision.
        /// </summary>
        static public class MyDateTimePrecisionStatic
        {
            /// <summary>
            /// When this static class is instantiated for the first time (once on program start), start the stopwatch.
            /// This stopwatch is accurate to 300ns, unlike DateTime which is accurate to only 30ms.
            /// </summary>
            private static readonly Stopwatch stopwatchSinceClassInstantiated;
            static MyDateTimePrecisionStatic()
            {
                stopwatchSinceClassInstantiated = new Stopwatch();
                stopwatchSinceClassInstantiated.Start();
            }       

            /// <summary>
            /// Return current time, non-UTC.
            /// </summary>
            public static DateTime MyGetDateTime
            {
                get { return DateTime.Now; }
            }

            /// <summary>
            /// Return the number of ticks per second in the stopwatch.
            /// </summary>
            public static int MyGetStopwatchTicksPerSecondConstant
            {
                // We can safely downcast this to int. Typically its ~3.3 million on an Intel i7, its unlikely to get beyond int.Max on PC hardware anytime soon.
                get { return (int)Stopwatch.Frequency; }
            }

            /// <summary>
            /// Return the number of ticks since the program has started (or this static class has been instantiated).
            /// </summary>
            public static long MyGetStopwatchTicksSinceProgramStart
            {
                get { return stopwatchSinceClassInstantiated.ElapsedTicks; }
            }

            /// <summary>
            /// Return timespan since the program has started (or this static class has been instantied).
            /// </summary>
            public static TimeSpan MyGetTimespanSinceProgramStart
            {
                get { return stopwatchSinceClassInstantiated.Elapsed; }
            }
        }
    }
Contango
  • 76,540
  • 58
  • 260
  • 305
  • Typically, its between 15ms and 30ms on most modern Intel hardware. However, this could change in the future. – Contango Oct 08 '11 at 13:51
  • It can vary over time if another application uses `timeBeginPeriod`. – CodesInChaos Oct 08 '11 at 13:57
  • possible duplicate of [How frequent is DateTime.Now updated ? or is there a more precise API to get the current time?](http://stackoverflow.com/questions/307582/how-frequent-is-datetime-now-updated-or-is-there-a-more-precise-api-to-get-the) – dtb Oct 08 '11 at 14:06
  • See: http://stackoverflow.com/questions/307582/how-frequent-is-datetime-now-updated-or-is-there-a-more-precise-api-to-get-the – Contango Oct 08 '11 at 14:17
  • See: http://stackoverflow.com/questions/3920075/what-is-the-minimum-time-you-need-to-thread-sleep-to-ensure-datetime-now-diffe – Contango Oct 08 '11 at 14:18
  • 7
    Given your update I do not understand why you need the logic at all. If you have the number of stopwatch ticks already then why not *always* subtract the stopwatch ticks? Why *ever* do the math in low-precision when you have the data in high precision? – Eric Lippert Oct 08 '11 at 14:25
  • My intuition is that if your goal is precise timing, `Stopwatch` is always a better bet than `DateTime.Now`. That's what it's *for*. – Brian Oct 10 '11 at 13:39
  • 2
    @Eric Lippert What you're missing is that StopWatch will slowly drift over a period of hours, until your timing is **seconds** out. I correct for this by storing both DateTime and Stopwatch, and using a metric to return the one which is currently either the most precise, or the most accurate. This way I am precise to the microsecond when comparing times A and B that are a few seconds apart, and but never more than 20ms out when comparing times A and B that are 8 days apart. – Contango Oct 11 '11 at 17:00
  • 1
    I see -- that makes perfect sense! Thanks for the update. – Eric Lippert Oct 11 '11 at 17:54

3 Answers3

7

If you care what the level of precision is then you shouldn't be using DateTime.Now in the first place. It is useful for timing on the human scale of things, like "is it time to go home and watch Doctor Who?" and not useful for things like "is it time to resync the raster line on this CRT?"

A conservative assumption is that it is precise to no better than a quarter of a second. Whether it is accurate or not depends solely on how accurately you set your system clock, and how frequently you cross-check it against a known source of accurate time; that has nothing to do with the precision of DateTime.Now.

To actually answer your question: the precision of DateTime.Now is typically around 16 milliseconds, but that can vary from machine to machine.

Benjol
  • 63,995
  • 54
  • 186
  • 268
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Yes, I am very aware of the difference between precision and accuracy (its nice to have my reasoning confirmed, however). I've updated the question to explain the reason why I need to find the minimum quantum between two ticks in DateTime.Now. – Contango Oct 08 '11 at 14:22
4

I don't have a source for the precision of DateTime.Now, but it's known to be worse than the precision of the Stopwatch Class. If you want to measure the time span between two moments in time, you should always use the Stopwatch Class. You can determine the precision of the Stopwatch Class on your system by checking the Stopwatch.Frequency Field.

dtb
  • 213,145
  • 36
  • 401
  • 431
4

There is a .NET 4 internal method that obtains this, System.Runtime.IOThreadTimer.GetSystemTimeResolution() in the System.Runtime.DurableInstancing assembly. Nothing you could get to yourself but you can pinvoke the same winapi function it uses. GetSystemTimeAdjustment(), the lpTimeIncrement argument returns the interval between clock interrupt ticks in 100 nanosecond increments. Default is 1/64 second on most machines.

[DllImport("kernel32.dll")]
private static extern bool GetSystemTimeAdjustment(out uint lpTimeAdjustment,
   out uint lpTimeIncrement, out bool lpTimeAdjustmentDisabled);
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536