首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >我们如何确定DateTime.Now的“量子”,即滴答之间的最小间隔?

我们如何确定DateTime.Now的“量子”,即滴答之间的最小间隔?
EN

Stack Overflow用户
提问于 2011-10-08 13:50:37
回答 3查看 803关注 0票数 2

是否有一种方法可以确定DateTime.Now的两个滴答之间的最小运行时间?我可以用秒表来计时,但是操作系统有什么方法可以在.NET中报告这一点吗?

换句话说,DateTime.Now有多精确?

更新

这一点很重要,因为我正在编写一个DateTimePrecision类,它存储DateTime.Now、StopWatch中的滴答和秒表计时每秒常数。当计算出两个TimeSpan值之间的DateTimePrecision值时,如果差值小于DateTime.Now的量程,则使用秒表,否则使用DateTime.Now。

更新

如果您进行了两次比较,有几个人质疑为什么使用DateTime是必要的。

原因是StopWatch会在一段时间或几天内慢慢漂移,直到你的时间是

为此,我将DateTimeStopwatch存储在一个结构中,并使用一个度量来返回当前最精确或最精确的度量。

这样我就有:

  • 当比较相隔几秒钟的时间A和B时的微秒精度;
  • 当比较相隔几天的时间A和B时的毫秒精度。

在我的计时器类中,如果比较两次以计算增量,则切换点由考虑到System.Runtime.IOThreadTimer.GetSystemTimeResolution()的度量来确定。

两次A和B之间的增量的绝对误差:

  • 开始精确到微秒;
  • 在几个小时或几天内,上升到的最大 of System.Runtime.IOThreadTimer.GetSystemTimeResolution(),为几十毫秒。

同样,依赖Stopwatch本身将导致三角洲,这是秒外,如果您正在测量跨越许多天。

更新

如果您自己正在实现这一点,请确保您也将Stopwatch.Frequency存储在结构中。如果您交换硬件或更改操作系统,这种情况可能会改变。如果您持久化DateTimePrecision的实例,您将无法在不知道滴答速度的情况下在另一台机器上读取它们。

下面是我到目前为止拥有的beta代码:

代码语言:javascript
复制
    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; }
            }
        }
    }
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-10-08 14:09:44

在System.Runtime.IOThreadTimer.GetSystemTimeResolution()程序集中,有一个.NET 4内部方法可以获得这个结果。您无法获得任何东西,但您可以调用它使用的相同的winapi函数。GetSystemTimeAdjustment(),lpTimeIncrement参数返回时钟中断滴答之间的间隔(以100纳秒为单位)。在大多数机器上,缺省值为1/64秒。

代码语言:javascript
复制
[DllImport("kernel32.dll")]
private static extern bool GetSystemTimeAdjustment(out uint lpTimeAdjustment,
   out uint lpTimeIncrement, out bool lpTimeAdjustmentDisabled);
票数 4
EN

Stack Overflow用户

发布于 2011-10-08 14:12:32

--如果你关心精度的水平,那么你就不应该首先使用DateTime.Now。对于人类尺度上的计时是有用的,比如“是时候回家看医生是谁了?”对于诸如“是时候重新同步CRT上的光栅行了吗?”

保守的假设是,它是精确的,不超过四分之一秒。是否是精确的完全取决于您设置系统时钟的准确性,以及您根据已知的精确时间源交叉检查它的频率;这与DateTime.Now的精度无关。

要真正回答您的问题: DateTime.Now的精度通常在16毫秒左右,但这可能因机器而异。

票数 7
EN

Stack Overflow用户

发布于 2011-10-08 14:01:52

我没有DateTime.Now精度的来源,但它比秒表级的精度还要糟糕。如果你想测量两个时刻之间的时间跨度,你应该始终使用秒表类。您可以通过检查Stopwatch.Frequency场来确定系统中秒表类的精度。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7697232

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档