有没有办法确定 DateTime.Now 的两次滴答之间经过的最小时间?我可以用秒表计时,但是否有某种方法可以让操作系统在 .NET 中报告这一点?
换句话说 - DateTime.Now 有多准确?
更新
这很重要,因为我正在编写一个 DateTimePrecision 类,它存储 DateTime.Now、StopWatch 的滴答声和 Stopwatch 每秒滴答声常量。在计算两个 DateTimePrecision 值之间的 TimeSpan 时,如果差值小于 DateTime.Now 的量程,则使用 Stopwatch,否则使用 DateTime.Now。
更新
有几个人质疑为什么有必要使用 DateTime
根本没有,如果你比较两次的话。
原因是StopWatch
会在数小时或数天内缓慢漂移,直到您的时间秒结束。
我通过存储 DateTime
来纠正这个问题和 Stopwatch
在结构中,并使用度量返回当前最精确或最准确的度量。
这样我有:
- 比较相隔几秒的时间 A 和 B 时的微秒级精度;
- 比较相隔数天的时间 A 和 B 时的毫秒精度。
在我的计时器类中,如果比较两次以计算增量,则切换点由采用 System.Runtime.IOThreadTimer.GetSystemTimeResolution()
的度量确定。考虑在内。
两个时间A和B之间的delta的绝对误差:
- 开始时精确到微秒;
- 在几个小时或几天内上升到最大值
System.Runtime.IOThreadTimer.GetSystemTimeResolution()
这是几十毫秒。
再次依赖Stopwatch
如果您跨多天进行测量,则单独使用会导致秒级的增量。
更新
如果您自己实现,请确保您存储了 Stopwatch.Frequency
在结构上也是如此。如果您交换硬件或更改操作系统,这可能会改变。如果您保留 DateTimePrecision 的实例,您将无法在不知道滴答进来的速度的情况下在另一台机器上读回它们。
这是我目前拥有的测试代码:
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; }
}
}
}
最佳答案
如果您关心精度级别是多少,那么您不应该首先使用 DateTime.Now。它对于事物的人类尺度的计时很有用,例如“是吗?是时候回家看神秘博士了?”对于诸如“是时候重新同步此 CRT 上的光栅线了吗?”之类的事情没有用。
保守的假设是精确不超过四分之一秒。它是否准确完全取决于您设置系统时钟的准确程度,以及您将其与已知准确时间来源进行交叉检查的频率;这与 DateTime.Now 的精度无关。
实际回答您的问题:DateTime.Now 的精度通常在 16 毫秒左右,但这可能因机器而异。
关于c# - 我们如何确定 DateTime.Now 的 "quantum",即刻度之间的最小间隔?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7697232/