是否有可用于 CLR 的高分辨率(微秒、纳秒)DateTime 对象?

Posted

技术标签:

【中文标题】是否有可用于 CLR 的高分辨率(微秒、纳秒)DateTime 对象?【英文标题】:Is there a high resolution (microsecond, nanosecond) DateTime object available for the CLR? 【发布时间】:2011-07-18 13:09:54 【问题描述】:

我有一个存储微秒级时间戳的仪器,我需要存储这些时间戳作为从仪器收集信息的一部分。请注意,我确实不需要需要generate timestamps;这些时间戳由仪器本身使用高分辨率实时操作系统预先生成。解析出这些值不是问题——它们使用标准格式存储在 UTC 时间。本来我想用C# DateTime structure 只能存储毫秒分辨率的时间戳。

.NET 或支持微秒和(理想情况下)纳秒分辨率时间戳的通用 C# 库是否提供了另一个对象,还是我必须自己推出?

【问题讨论】:

System.DateTime 的分辨率为 100 纳秒(如您提供的链接中所述)。这是 1/10 微秒,所以似乎已经满足您的要求,不是吗? 我多年前从事检测代码工作。我发现有必要使用仪器自己的时间信号来处理真正高分辨率的东西。如果测量时间确实需要精确到不到一微秒,您可能需要一个仪器来显示测量时间 - 采集样本的时间 - 以及测量本身。如果你不这样做,你会从上下文切换、缓存未命中和其他非硬实时操作系统垃圾中得到各种令人讨厌的抖动。 @Ollie Jones:是的,这正是正在发生的事情。时间戳是仪器自己生成的,仪器有非常高精度的计时硬件。一旦我有了那个时间戳,我只需要找出将它放入什么数据结构! 【参考方案1】:

毕竟你也许可以使用DateTimeDateTime.Ticks' 分辨率为 100 纳秒。您可以使用DateTime.AddTicks 设置刻度。

【讨论】:

是的,除了 AddTicks 需要很长时间(向下滚动我的答案)并且单个滴答声只有 0.1 微秒,所以如果他需要比这更严格的粒度,它将无法工作。 是的,但他明确表示他需要微秒,理想情况下纳秒。这就是为什么我用“可能”来表达它。 ~ 是的,我知道这一点;)~ 我只是想确保有人把这些拼写出来。 我今天试了 DateTime.Ticks,只得到了大约 16 毫秒的分辨率。 @PeterMortensen - 你试过什么?我的猜测是您已经尝试过 Sleep(1),它只会为您提供大约 16 毫的分辨率。但是,Ticks 的分辨率为每秒 10,000,000。【参考方案2】:

查看答案和 DateTime.Ticks 属性,可以根据给定值计算微秒和纳秒。于是,我把这个扩展方法类放在一起来做。 (遗憾的是,考虑到其他一些要求,我认为我无法使用它,但其他人可能会觉得它很有用。)

/// <summary>
/// Extension methods for accessing Microseconds and Nanoseconds of a
/// DateTime object.
/// </summary>
public static class DateTimeExtensionMethods

   /// <summary>
   /// The number of ticks per microsecond.
   /// </summary>
   public const int TicksPerMicrosecond = 10;
   /// <summary>
   /// The number of ticks per Nanosecond.
   /// </summary>
   public const int NanosecondsPerTick = 100;

   /// <summary>
   /// Gets the microsecond fraction of a DateTime.
   /// </summary>
   /// <param name="self"></param>
   /// <returns></returns>
   public static int Microseconds(this DateTime self)
   
      return (int)Math.Floor(
         (self.Ticks 
         % TimeSpan.TicksPerMillisecond )
         / (double)TicksPerMicrosecond);
   
   /// <summary>
   /// Gets the Nanosecond fraction of a DateTime.  Note that the DateTime
   /// object can only store nanoseconds at resolution of 100 nanoseconds.
   /// </summary>
   /// <param name="self">The DateTime object.</param>
   /// <returns>the number of Nanoseconds.</returns>
   public static int Nanoseconds(this DateTime self)
   
      return (int)(self.Ticks % TimeSpan.TicksPerMillisecond % TicksPerMicrosecond)
         * NanosecondsPerTick;
   
   /// <summary>
   /// Adds a number of microseconds to this DateTime object.
   /// </summary>
   /// <param name="self">The DateTime object.</param>
   /// <param name="microseconds">The number of milliseconds to add.</param>
   public static DateTime AddMicroseconds(this DateTime self, int microseconds)
   
      return self.AddTicks(microseconds * TicksPerMicrosecond);
   
   /// <summary>
   /// Adds a number of nanoseconds to this DateTime object.  Note: this
   /// object only stores nanoseconds of resolutions of 100 seconds.
   /// Any nanoseconds passed in lower than that will be rounded using
   /// the default rounding algorithm in Math.Round().
   /// </summary>
   /// <param name="self">The DateTime object.</param>
   /// <param name="nanoseconds">The number of nanoseconds to add.</param>
   public static DateTime AddNanoseconds(this DateTime self, int nanoseconds)
   
      return self.AddTicks((int)Math.Round(nanoseconds / (double)NanosecondsPerTick));
   

这仍然不允许您在创建时设置微秒或纳秒,但可以在不久之后添加它们。它也不能提供比 DateTime 更好的分辨率(例如,1/10 微秒,也就是 100 纳秒分辨率。)

DateTime time = new DateTime(year, month, day, hour, min, sec, msec);
time = time.AddMicroseconds(microseconds);
time = time.AddNanoseconds(nanoseconds); # note: rounds if not enough added

希望这对其他人有用!

【讨论】:

也有可能做类似time += TimeSpan.FromTicks(ticksFromMicroseconds(microseconds)) 的事情,但这将是一个偏好问题,因为我猜他们基本上做同样的事情......【参考方案3】:

如果我真的需要比 DateTime 提供的 100 ns 分辨率更高的精度,我会考虑创建一个包含 DateTime 和整数值的结构:

public struct HiResDateTime

    public HiResDateTime(DateTime dateTime, int nanoseconds)
    
        if (nanoSeconds < 0 || nanoSeconds > 99) 
            throw new ArgumentOutOfRangeException(...);
        DateTime = dateTime;
        Nanoseconds = nanoseconds;
    

    ... possibly other constructors including one that takes a timestamp parameter
    ... in the format provided by the instruments.

    public DateTime DateTime  get; private set; 
    public int Nanoseconds  get; private set; 

    ... implementation ...

然后实现需要的任何东西,例如:

比较(首先是DateTime,然后是NanosecondsToString() 例如将 DateTime 格式设置为 100 ns 精度,然后附加纳秒。 与DateTime 之间的转换 加/减(可能需要类似的HiResTimeSpan) ……等等……

【讨论】:

【参考方案4】:

如果您想要在很短的微秒内运行的东西,那么不。您要求的东西不作为标准库的一部分存在,但是对于您的要求,为什么需要这个?听起来您确实需要两个组件,一个字符串(可变长度,保留几乎任何可能的值)和一个 DateTime,用于您本机获得的 UTC 标准格式日期/时间。

微/纳米级秒计时不在“正常”计算范围内,因此在“正常”.NET 库中未提供。

您将如何处理这些时间戳?你会比较它们吗?添加/减去它们?我建议为基本的 DateTime 对象运行反射器(实际上我想我也会很快做到这一点)

为了您的利益,这里是标准 DateTime 对象的 .NET Reflector 反汇编的简单版本(因为在此编辑时的另一个答案也暗示了 TimeSpan 元素)

[Serializable]
public struct DateTime : IComparable, IFormattable, IConvertible, ISerializable, IComparable<DateTime>, IEquatable<DateTime>

    // Fields
    private ulong dateData;
    private const string DateDataField = "dateData";
    private const int DatePartDay = 3;
    private const int DatePartDayOfYear = 1;
    private const int DatePartMonth = 2;
    private const int DatePartYear = 0;
    private const int DaysPer100Years = 0x8eac;
    private const int DaysPer400Years = 0x23ab1;
    private const int DaysPer4Years = 0x5b5;
    private const int DaysPerYear = 0x16d;
    private const int DaysTo10000 = 0x37b9db;
    private const int DaysTo1601 = 0x8eac4;
    private const int DaysTo1899 = 0xa9559;
    private static readonly int[] DaysToMonth365;
    private static readonly int[] DaysToMonth366;
    private const long DoubleDateOffset = 0x85103c0cb83c000L;
    private const long FileTimeOffset = 0x701ce1722770000L;
    private const ulong FlagsMask = 13835058055282163712L;
    private const ulong KindLocal = 9223372036854775808L;
    private const ulong KindLocalAmbiguousDst = 13835058055282163712L;
    private const int KindShift = 0x3e;
    private const ulong KindUnspecified = 0L;
    private const ulong KindUtc = 0x4000000000000000L;
    private const ulong LocalMask = 9223372036854775808L;
    private const long MaxMillis = 0x11efae44cb400L;
    internal const long MaxTicks = 0x2bca2875f4373fffL;
    public static readonly DateTime MaxValue;
    private const int MillisPerDay = 0x5265c00;
    private const int MillisPerHour = 0x36ee80;
    private const int MillisPerMinute = 0xea60;
    private const int MillisPerSecond = 0x3e8;
    internal const long MinTicks = 0L;
    public static readonly DateTime MinValue;
    private const double OADateMaxAsDouble = 2958466.0;
    private const double OADateMinAsDouble = -657435.0;
    private const long OADateMinAsTicks = 0x6efdddaec64000L;
    private const long TicksCeiling = 0x4000000000000000L;
    private const string TicksField = "ticks";
    private const ulong TicksMask = 0x3fffffffffffffffL;
    private const long TicksPerDay = 0xc92a69c000L;
    private const long TicksPerHour = 0x861c46800L;
    private const long TicksPerMillisecond = 0x2710L;
    private const long TicksPerMinute = 0x23c34600L;
    private const long TicksPerSecond = 0x989680L;

    // Methods
    static DateTime();
    public DateTime(long ticks);
    private DateTime(ulong dateData);
    public DateTime(long ticks, DateTimeKind kind);
    private DateTime(SerializationInfo info, StreamingContext context);
    public DateTime(int year, int month, int day);
    internal DateTime(long ticks, DateTimeKind kind, bool isAmbiguousDst);
    public DateTime(int year, int month, int day, Calendar calendar);
    public DateTime(int year, int month, int day, int hour, int minute, int second);
    public DateTime(int year, int month, int day, int hour, int minute, int second, DateTimeKind kind);
    public DateTime(int year, int month, int day, int hour, int minute, int second, Calendar calendar);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, DateTimeKind kind);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, Calendar calendar, DateTimeKind kind);
    public DateTime Add(TimeSpan value);
    private DateTime Add(double value, int scale);
    public DateTime AddDays(double value);
    public DateTime AddHours(double value);
    public DateTime AddMilliseconds(double value);
    public DateTime AddMinutes(double value);
    public DateTime AddMonths(int months);
    public DateTime AddSeconds(double value);
    public DateTime AddTicks(long value);
    public DateTime AddYears(int value);
    public static int Compare(DateTime t1, DateTime t2);
    public int CompareTo(DateTime value);
    public int CompareTo(object value);
    private static long DateToTicks(int year, int month, int day);
    public static int DaysInMonth(int year, int month);
    internal static long DoubleDateToTicks(double value);
    public bool Equals(DateTime value);
    public override bool Equals(object value);
    public static bool Equals(DateTime t1, DateTime t2);
    public static DateTime FromBinary(long dateData);
    internal static DateTime FromBinaryRaw(long dateData);
    public static DateTime FromFileTime(long fileTime);
    public static DateTime FromFileTimeUtc(long fileTime);
    public static DateTime FromOADate(double d);
    private int GetDatePart(int part);
    public string[] GetDateTimeFormats();
    public string[] GetDateTimeFormats(char format);
    public string[] GetDateTimeFormats(IFormatProvider provider);
    public string[] GetDateTimeFormats(char format, IFormatProvider provider);
    public override int GetHashCode();
    [MethodImpl(MethodImplOptions.InternalCall)]
    internal static extern long GetSystemTimeAsFileTime();
    public TypeCode GetTypeCode();
    internal bool IsAmbiguousDaylightSavingTime();
    public bool IsDaylightSavingTime();
    public static bool IsLeapYear(int year);
    public static DateTime operator +(DateTime d, TimeSpan t);
    public static bool operator ==(DateTime d1, DateTime d2);
    public static bool operator >(DateTime t1, DateTime t2);
    public static bool operator >=(DateTime t1, DateTime t2);
    public static bool operator !=(DateTime d1, DateTime d2);
    public static bool operator <(DateTime t1, DateTime t2);
    public static bool operator <=(DateTime t1, DateTime t2);
    public static TimeSpan operator -(DateTime d1, DateTime d2);
    public static DateTime operator -(DateTime d, TimeSpan t);
    public static DateTime Parse(string s);
    public static DateTime Parse(string s, IFormatProvider provider);
    public static DateTime Parse(string s, IFormatProvider provider, DateTimeStyles styles);
    public static DateTime ParseExact(string s, string format, IFormatProvider provider);
    public static DateTime ParseExact(string s, string format, IFormatProvider provider, DateTimeStyles style);
    public static DateTime ParseExact(string s, string[] formats, IFormatProvider provider, DateTimeStyles style);
    public static DateTime SpecifyKind(DateTime value, DateTimeKind kind);
    public TimeSpan Subtract(DateTime value);
    public DateTime Subtract(TimeSpan value);
    bool IConvertible.ToBoolean(IFormatProvider provider);
    byte IConvertible.ToByte(IFormatProvider provider);
    char IConvertible.ToChar(IFormatProvider provider);
    DateTime IConvertible.ToDateTime(IFormatProvider provider);
    decimal IConvertible.ToDecimal(IFormatProvider provider);
    double IConvertible.ToDouble(IFormatProvider provider);
    short IConvertible.ToInt16(IFormatProvider provider);
    int IConvertible.ToInt32(IFormatProvider provider);
    long IConvertible.ToInt64(IFormatProvider provider);
    sbyte IConvertible.ToSByte(IFormatProvider provider);
    float IConvertible.ToSingle(IFormatProvider provider);
    object IConvertible.ToType(Type type, IFormatProvider provider);
    ushort IConvertible.ToUInt16(IFormatProvider provider);
    uint IConvertible.ToUInt32(IFormatProvider provider);
    ulong IConvertible.ToUInt64(IFormatProvider provider);
    [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)]
    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context);
    private static double TicksToOADate(long value);
    private static long TimeToTicks(int hour, int minute, int second);
    public long ToBinary();
    internal long ToBinaryRaw();
    public long ToFileTime();
    public long ToFileTimeUtc();
    public DateTime ToLocalTime();
    public string ToLongDateString();
    public string ToLongTimeString();
    public double ToOADate();
    public string ToShortDateString();
    public string ToShortTimeString();
    public override string ToString();
    public string ToString(IFormatProvider provider);
    public string ToString(string format);
    public string ToString(string format, IFormatProvider provider);
    public DateTime ToUniversalTime();
    internal static bool TryCreate(int year, int month, int day, int hour, int minute, int second, int millisecond, out DateTime result);
    public static bool TryParse(string s, out DateTime result);
    public static bool TryParse(string s, IFormatProvider provider, DateTimeStyles styles, out DateTime result);
    public static bool TryParseExact(string s, string[] formats, IFormatProvider provider, DateTimeStyles style, out DateTime result);
    public static bool TryParseExact(string s, string format, IFormatProvider provider, DateTimeStyles style, out DateTime result);

    // Properties
    public DateTime Date  get; 
    public int Day  get; 
    public DayOfWeek DayOfWeek  get; 
    public int DayOfYear  get; 
    public int Hour  get; 
    private ulong InternalKind  get; 
    private long InternalTicks  get; 
    public DateTimeKind Kind  get; 
    public int Millisecond  get; 
    public int Minute  get; 
    public int Month  get; 
    public static DateTime Now  get; 
    public int Second  get; 
    public long Ticks  get; 
    public TimeSpan TimeOfDay  get; 
    public static DateTime Today  get; 
    public static DateTime UtcNow  get; 
    public int Year  get; 


[Serializable, StructLayout(LayoutKind.Sequential), ComVisible(true)]
public struct TimeSpan : IComparable, IComparable<TimeSpan>, IEquatable<TimeSpan>

    public const long TicksPerMillisecond = 0x2710L;
    private const double MillisecondsPerTick = 0.0001;
    public const long TicksPerSecond = 0x989680L;
    private const double SecondsPerTick = 1E-07;
    public const long TicksPerMinute = 0x23c34600L;
    private const double MinutesPerTick = 1.6666666666666667E-09;
    public const long TicksPerHour = 0x861c46800L;
    private const double HoursPerTick = 2.7777777777777777E-11;
    public const long TicksPerDay = 0xc92a69c000L;
    private const double DaysPerTick = 1.1574074074074074E-12;
    private const int MillisPerSecond = 0x3e8;
    private const int MillisPerMinute = 0xea60;
    private const int MillisPerHour = 0x36ee80;
    private const int MillisPerDay = 0x5265c00;
    private const long MaxSeconds = 0xd6bf94d5e5L;
    private const long MinSeconds = -922337203685L;
    private const long MaxMilliSeconds = 0x346dc5d638865L;
    private const long MinMilliSeconds = -922337203685477L;
    public static readonly TimeSpan Zero;
    public static readonly TimeSpan MaxValue;
    public static readonly TimeSpan MinValue;
    internal long _ticks;
    public TimeSpan(long ticks);
    public TimeSpan(int hours, int minutes, int seconds);
    public TimeSpan(int days, int hours, int minutes, int seconds);
    public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds);
    public long Ticks  get; 
    public int Days  get; 
    public int Hours  get; 
    public int Milliseconds  get; 
    public int Minutes  get; 
    public int Seconds  get; 
    public double TotalDays  get; 
    public double TotalHours  get; 
    public double TotalMilliseconds  get; 
    public double TotalMinutes  get; 
    public double TotalSeconds  get; 
    public TimeSpan Add(TimeSpan ts);
    public static int Compare(TimeSpan t1, TimeSpan t2);
    public int CompareTo(object value);
    public int CompareTo(TimeSpan value);
    public static TimeSpan FromDays(double value);
    public TimeSpan Duration();
    public override bool Equals(object value);
    public bool Equals(TimeSpan obj);
    public static bool Equals(TimeSpan t1, TimeSpan t2);
    public override int GetHashCode();
    public static TimeSpan FromHours(double value);
    private static TimeSpan Interval(double value, int scale);
    public static TimeSpan FromMilliseconds(double value);
    public static TimeSpan FromMinutes(double value);
    public TimeSpan Negate();
    public static TimeSpan Parse(string s);
    public static bool TryParse(string s, out TimeSpan result);
    public static TimeSpan FromSeconds(double value);
    public TimeSpan Subtract(TimeSpan ts);
    public static TimeSpan FromTicks(long value);
    internal static long TimeToTicks(int hour, int minute, int second);
    private string IntToString(int n, int digits);
    public override string ToString();
    public static TimeSpan operator -(TimeSpan t);
    public static TimeSpan operator -(TimeSpan t1, TimeSpan t2);
    public static TimeSpan operator +(TimeSpan t);
    public static TimeSpan operator +(TimeSpan t1, TimeSpan t2);
    public static bool operator ==(TimeSpan t1, TimeSpan t2);
    public static bool operator !=(TimeSpan t1, TimeSpan t2);
    public static bool operator <(TimeSpan t1, TimeSpan t2);
    public static bool operator <=(TimeSpan t1, TimeSpan t2);
    public static bool operator >(TimeSpan t1, TimeSpan t2);
    public static bool operator >=(TimeSpan t1, TimeSpan t2);
    static TimeSpan();
    // Nested Types
    [StructLayout(LayoutKind.Sequential)]
    private struct StringParser
    
        private string str;
        private char ch;
        private int pos;
        private int len;
        private ParseError error;
        internal void NextChar();
        internal char NextNonDigit();
        internal long Parse(string s);
        internal bool TryParse(string s, out long value);
        internal bool ParseInt(int max, out int i);
        internal bool ParseTime(out long time);
        internal void SkipBlanks();
        // Nested Types
        private enum ParseError
        
            ArgumentNull = 4,
            Format = 1,
            Overflow = 2,
            OverflowHoursMinutesSeconds = 3
        
    

【讨论】:

这些时间戳将用于多种用途;有些,只是为了知道它何时发生(“应用程序”级别的用法),但更重要的原因是要知道盒子上发生的事件的时间:“时差”。这些事件可能非常非常短暂,因此精度必须非常高 - 因此是“微”和“纳米”第二分辨率。 @RobertP 然后我会考虑将值存储为字符串并进行字符串比较以确定“在此之前”。这同样有效,并且整体上更容易阅读,并消除了一定程度的透明度。 idk,只是我的两分钱。 如果你告诉我硬件供应商还没有现成的东西可以让你使用设备来解释数据,我真的很惊讶,除非他们只是想让你使用字符串 @drachenstern:如果我说我的公司制造了这个设备,我正在实现那个库,对你有帮助吗? :) 如果只需要相对时间,则字符串选项会很有用,但我们需要知道并访问确切的数字。 @RobertP ~ 确实会改变一点点;) @RobertP 我认为这听起来是一件非常有趣的事情,我建议以某种方式重新实现 DateTime。我对你正在做的事情并不完全陌生,但我自己从来没有写过这样的东西。最重要的是考虑如何最好地存储数据,这取决于您如何取回数据。如果数据变得如此详细,我会说将 DateTime 扩展为“subticks”。坦率地说,任何比兆赫更快的东西都让我难以置信,所以我必须把所有东西的数学都摆在我面前。【参考方案5】:

我正在努力解决同样的问题,因为我有一个项目,其中我有皮秒分辨率时间戳。我的源数据采用“time_t”格式,即自纪元以来的第二秒 + 皮秒 + UTC 偏移量。

我发现的最佳解决方案是在内部使用“UTC 纪元以来的十进制秒数”作为我的时间格式,并且仅使用 DateTime 作为漂亮的打印对象,以提取高达 1 秒分辨率的语言环境/格式,然后手动操作字符串以包含小数秒。

【讨论】:

【参考方案6】:

您可能会以各种方式自己滚动。

1.) 创建一个使用 System.Decimal 字段作为“后备存储”的结构。

2.) 创建一个使用 System.Numerics.BigInteger 作为支持的结构。

包装方法以使用 System.DateTime 方便的“部件”(年、月、日...) 包括你自己的纳秒皮秒飞秒等,属性和方法。

仅供参考:

DateTime.MaxValue.Ticks : 3,155,378,975,999,999,999

Decimal.MaxValue : 79,228,162,514,264,337,593,543,950,335

BigInteger : 任意大!

【讨论】:

以上是关于是否有可用于 CLR 的高分辨率(微秒、纳秒)DateTime 对象?的主要内容,如果未能解决你的问题,请参考以下文章

秒的换算:ms(毫秒),μs(微秒),ns(纳秒),ps(皮秒)

微秒、纳秒、秒,1 千 兆 的换算?

如何获得 MySQL 毫秒,微秒

一微妙等于多少秒?

Java日期时间API系列35-----Jdk8中java.time包中的新的日期时间API类应用,微秒和纳秒等更精确的时间格式化和解析。

请问1秒=100毫秒=1000纳秒吗,即1S=100MS=1000NS