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; }
}
}
}