How to get timestamp of tick precision in .NET / C#?

56,860

Solution 1

The value of the system clock that DateTime.Now reads is only updated every 15 ms or so (or 10 ms on some systems), which is why the times are quantized around those intervals. There is an additional quantization effect that results from the fact that your code is running in a multithreaded OS, and thus there are stretches where your application is not "alive" and is thus not measuring the real current time.

Since you're looking for an ultra-accurate time stamp value (as opposed to just timing an arbitrary duration), the Stopwatch class by itself will not do what you need. I think you would have to do this yourself with a sort of DateTime/Stopwatch hybrid. When your application starts, you would store the current DateTime.UtcNow value (i.e. the crude-resolution time when your application starts) and then also start a Stopwatch object, like this:

DateTime _starttime = DateTime.UtcNow;
Stopwatch _stopwatch = Stopwatch.StartNew();

Then, whenever you need a high-resolution DateTime value, you would get it like this:

DateTime highresDT = _starttime.AddTicks(_stopwatch.Elapsed.Ticks);

You also may want to periodically reset _starttime and _stopwatch, to keep the resulting time from getting too far out of sync with the system time (although I'm not sure this would actually happen, and it would take a long time to happen anyway).

Update: since it appears that Stopwatch does get out of sync with the system time (by as much as half a second per hour), I think it makes sense to reset the hybrid DateTime class based on the amount of time that passes between calls to check the time:

public class HiResDateTime
{
    private static DateTime _startTime;
    private static Stopwatch _stopWatch = null;
    private static TimeSpan _maxIdle = 
        TimeSpan.FromSeconds(10);

    public static DateTime UtcNow
    {
        get
        {
            if ((_stopWatch == null) || 
                (_startTime.Add(_maxIdle) < DateTime.UtcNow))
            {
                Reset();
            }
            return _startTime.AddTicks(_stopWatch.Elapsed.Ticks);
        }
    }

    private static void Reset()
    {
        _startTime = DateTime.UtcNow;
        _stopWatch = Stopwatch.StartNew();
    }
}

If you reset the hybrid timer at some regular interval (say every hour or something), you run the risk of setting the time back before the last read time, kind of like a miniature Daylight Savings Time problem.

Solution 2

To get a high-resolution tick-count, please, use the static Stopwatch.GetTimestamp()-method:

long tickCount = System.Diagnostics.Stopwatch.GetTimestamp();
DateTime highResDateTime = new DateTime(tickCount);

just take a look at the .NET Source Code:

    public static long GetTimestamp() {
        if(IsHighResolution) {
            long timestamp = 0;    
            SafeNativeMethods.QueryPerformanceCounter(out timestamp);
            return timestamp;
        }
        else {
            return DateTime.UtcNow.Ticks;
        }   
    }

Source Code here: http://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Stopwatch.cs,69c6c3137e12dab4

Solution 3

[The accepted answer does not appear to be thread safe, and by its own admission can go backwards in time causing duplicate timestamps, hence this alternative answer]

If what you really care about (per your comment) is in fact, a unique timestamp that is allocated in strict ascending order and which corresponds as closely as possible to the system time, you could try this alternative approach:

public class HiResDateTime
{
   private static long lastTimeStamp = DateTime.UtcNow.Ticks;
   public static long UtcNowTicks
   {
       get
       {
           long orig, newval;
           do
           {
               orig = lastTimeStamp;
               long now = DateTime.UtcNow.Ticks;
               newval = Math.Max(now, orig + 1);
           } while (Interlocked.CompareExchange
                        (ref lastTimeStamp, newval, orig) != orig);

           return newval;
       }
   }
}

Solution 4

These suggestions all look too hard! If you're on Windows 8 or Server 2012 or higher, use GetSystemTimePreciseAsFileTime as follows:

[DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)]
static extern void GetSystemTimePreciseAsFileTime(out long filetime);

public DateTimeOffset GetNow()
{
    long fileTime;
    GetSystemTimePreciseAsFileTime(out fileTime);
    return DateTimeOffset.FromFileTime(fileTime);
}

This has much, much better accuracy than DateTime.Now without any effort.

See MSDN for more info: http://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx

Solution 5

It does return the most accurate date and time known to the operating system.

The operating system also provides higher resolution timing through QueryPerformanceCounter and QueryPerformanceFrequency (.NET Stopwatch class). These let you time an interval but do not give you date and time of day. You might argue that these would be able to give you a very accurate time and day, but I am not sure how badly they skew over a long interval.

Share:
56,860
Admin
Author by

Admin

Updated on October 16, 2021

Comments

  • Admin
    Admin over 2 years

    Up until now I used DateTime.Now for getting timestamps, but I noticed that if you print DateTime.Now in a loop you will see that it increments in descrete jumps of approx. 15 ms. But for certain scenarios in my application I need to get the most accurate timestamp possible, preferably with tick (=100 ns) precision. Any ideas?

    Update:

    Apparently, StopWatch / QueryPerformanceCounter is the way to go, but it can only be used to measure time, so I was thinking about calling DateTime.Now when the application starts up and then just have StopWatch run and then just add the elapsed time from StopWatch to the initial value returned from DateTime.Now. At least that should give me accurate relative timestamps, right? What do you think about that (hack)?

    NOTE:

    StopWatch.ElapsedTicks is different from StopWatch.Elapsed.Ticks! I used the former assuming 1 tick = 100 ns, but in this case 1 tick = 1 / StopWatch.Frequency. So to get ticks equivalent to DateTime use StopWatch.Elapsed.Ticks. I just learned this the hard way.

    NOTE 2:

    Using the StopWatch approach, I noticed it gets out of sync with the real time. After about 10 hours, it was ahead by 5 seconds. So I guess one would have to resync it every X or so where X could be 1 hour, 30 min, 15 min, etc. I am not sure what the optimal timespan for resyncing would be since every resync will change the offset which can be up to 20 ms.

  • Admin
    Admin almost 15 years
    Would it make sense to call DateTime.Now when the application starts up and then just have StopWatch run and then just add the elapsed tiem from StopWatch to the initial value returned from DateTime.Now to get a more accurate timestamp?
  • Henk Holterman
    Henk Holterman almost 15 years
    Hermann, the difference in the values will be accurate, but the base (DateTime.Now) will still suffer the ~20ms accuracy. So will the absolute values you will get.
  • Admin
    Admin almost 15 years
    Yeah, this is what I came up with also when I read the other responses about StopWatch/QueryPerformanceCounter. Thanks for providing a code sample. I am sure, others will benefit from it also.
  • MusiGenesis
    MusiGenesis almost 15 years
    By the way, using DateTime.UtcNow instead of DateTime.Now is actually important for timestamping. Otherwise, daylight savings time will hose you every Fall by suddenly throwing the timestamp values back by an hour.
  • MusiGenesis
    MusiGenesis almost 15 years
    -1 because the 15ms quantization is based on the Windows scheduler. The ~55Hz timer resolution is from an older, crude timer mechanism that fires off 18 events per second.
  • Admin
    Admin almost 15 years
    Yes, but considering that your system time is probably not synchronized perfectly anyway that offset wouldn't make much of a difference. At least that way the relative timestamps are more accurate.
  • Admin
    Admin almost 15 years
    Thanks. I already had to implement my own 'Date', 'Time' and 'DateAndTime' class to work around all the shortcomings of DateTime (e.g. no intrinsic support for time zones). It does seem extreme to reinvent the wheel with regards to Date/Time, but I am actually kind of suprised how bad DateTime was designed and implemented. You would think with all the resources and knowledge they have, they could have done way better.
  • Admin
    Admin almost 15 years
    The resolution I am getting on my system is about 2.8 ticks (2.79365114840015E-07), which is pretty good. Especially compared to DateTime.NowUtc, it's 50,000 times more accurate.
  • Jason Kresowaty
    Jason Kresowaty almost 15 years
    <Yeah, this is what I came up with when I read> I wouldn't underestimate my previous comment about skew. Especially when the documentation for QueryPerformanceCounter talks about bugs in the BIOS and HAL. :)
  • Henk Holterman
    Henk Holterman almost 15 years
    Musical Genius, 1/55 = 18 ms, so what's the diff?
  • MusiGenesis
    MusiGenesis almost 15 years
    @Henk: ouch ("Musical Genius"). :)
  • MusiGenesis
    MusiGenesis almost 15 years
    @Henk: sorry, I misread your original answer as saying the 15 ms was based on the Windows 55 ms timer resolution (which happened to be the resolution of the old-school 18 Hz timer). My bad.
  • MusiGenesis
    MusiGenesis almost 15 years
    @binary: as I said in my answer, I'm not entirely sure my DateTime/Stopwatch hybrid actually would get out of sync with just an ordinary DateTime, since I think they're ultimately driven by the same underlying mechanism. DateTime is quantized to 15 ms because that's how often the system time is updated, but the underlying mechanism doing the updating is the same mechanism underlying Stopwatch </THEORY>.
  • MusiGenesis
    MusiGenesis almost 15 years
    @Hermann: do you know about the System.TimeZone class? (just discovered it myself)
  • Admin
    Admin almost 15 years
    @MusiGenesis: I am using TimeZoneInfo class internally. MSDN says to use this one instead of TimeZone. TimeZoneInfo supports conversion across all time zones while TimeZone supports only Local and UTC (not any different from DateTime).
  • Admin
    Admin almost 15 years
    @MusiGenesis: I just discovered a bug in my code (and your code sample). Please see my "NOTE" in my updated question. You might want to updated your code sample. Thanks.
  • Admin
    Admin almost 15 years
    @binarycoder: I'll find out if it gets out of sync (my application is going to run for at least 10 hours) and post it here.
  • MusiGenesis
    MusiGenesis almost 15 years
    @Hermann: thanks, I edited my code. I should have mentioned in my original answer that this code was not tested.
  • Admin
    Admin almost 15 years
    @binarycoder: You were right. It does get out of sync. I noticed it was about 5 seconds ahead after 10 hours. I guess I'll have to resync it every X or so, I am not sure what to use for X. 1 hour? 30 min? 1 min?
  • MusiGenesis
    MusiGenesis almost 15 years
    @Hermann: that is a very interesting result. It seems to suggest that Stopwatch, while having a very high resolution, is not very accurate.
  • MusiGenesis
    MusiGenesis almost 15 years
    @Hermann: I'd go with resetting it whenever it's been idle for more than a given length of time (see my update). I don't know if 10 seconds is exactly the best idle time, but I don't think it hurts to reset it that frequently.
  • MusiGenesis
    MusiGenesis almost 15 years
    @Hermann: I've been running a similar test for the last two hours (without the reset correction), and the Stopwatch-based timer is only running about 400 ms ahead after 2 hours, so the skew itself appears to be variable (but still pretty severe). I'm pretty surprised that the skew is this bad; I guess this is why Stopwatch is in System.Diagnostics.
  • Lee Grissom
    Lee Grissom about 11 years
    +1. See stackoverflow.com/questions/5608980/… for my thoughts.
  • Frank Hileman
    Frank Hileman about 11 years
    If you need a thread-safe version, see the answer by Ian Mercer below.
  • MusiGenesis
    MusiGenesis about 10 years
    For the record, I didn't admit that my method can go backwards in time. I wrote: "If you reset the hybrid timer at some regular interval (say every hour or something), you run the risk of setting the time back before the last read time", which was an explanation of why I chose not to reset the timer at some regular interval. My solution was definitely not thread safe, however, and I like the non-duplication feature of your solution.
  • ToolmakerSteve
    ToolmakerSteve almost 9 years
    The limitation of this solution is that its resolution is limited by DateTime to 1ms or 16ms (depending on hardware). While the timestamps will be unique, they will not measure ticks precisely. @MusiGenesis approach will give more precise measurements.
  • ToolmakerSteve
    ToolmakerSteve almost 9 years
    Of course all those answers were written before Windows 8 / Server 2012 even existed. And even today, to assume customers have 8 / 2012 is not a good assumption to make, for most code.
  • ToolmakerSteve
    ToolmakerSteve almost 9 years
    MusiGenesis's approach is superior, as long as (1) Stopwatch's drift is small (which it is), and (2) there are gaps in the requests, long enough to perform the reset. The one feature he is missing is the "auto-increment" from Ian's (which could easily be added). Under no circumstances should one use Ian's by itself, if you care about tick accuracy: DateTime is only accurate to 16 ms.
  • ToolmakerSteve
    ToolmakerSteve almost 9 years
    If (2) is a problem, because there aren't enough gaps, then MG's solution needs to be adjusted to do "continuous correction": every call should check whether Now has changed from the previous call. If it has, then reset the timer, but use Ian's "Max(Now, lasttimestamp+1) to make sure you always increase.
  • ToolmakerSteve
    ToolmakerSteve almost 9 years
    While this does give trade order, it does not give sub-ms accuracy to timestamps, as requested by OP.
  • ToolmakerSteve
    ToolmakerSteve almost 9 years
    @FrankHileman: as long as readers understand that Ian's answer has accuracy limited to 16 ms by Datetime. It would be better to modify this answer to be thread-safe.
  • ToolmakerSteve
    ToolmakerSteve almost 9 years
    An interesting enhancement would be to modify continuously. On a given system, track the skew of Stopwatch vs DateTime. So rather than making sudden jumps to reset after an idle period, adjust every returned value based on a multiplier (that will be very slightly above or below 1, depending on the system's drift; e.g. 1.0000001). Instead of doing "reset" after idle, which could cause up to 16 ms "jump" in your time, poll datetime periodically, making a microscopic change to your multiplier each time (and set a time-offset, so that new multiplier only applies to time after the change).
  • ToolmakerSteve
    ToolmakerSteve almost 9 years
    Or as an alternative, have a separate thread periodically do the reset. This other thread must be asleep most of the time, then wake up and poll DateTime fairly rapidly, to find out (more accurately than to the nearest 16ms) when DateTime changes to a new value. When it sees DateTime change, it reads the stopwatch value. Now it has a correspondence between an "edge" when DateTime transitions, and a stopwatch time. So can calculate an accurate offset for future time results.
  • antiduh
    antiduh almost 8 years
    @MusiGenesis - Your UtcNow <-> Stopwatch synchronization approach isn't correct. See this thread: stackoverflow.com/a/38127282/344638
  • MaYaN
    MaYaN almost 8 years
    This guy seems to have fixed all the threading issues in this answer and has come up with an even more accurate clock, nimaara.com/2016/07/06/high-resolution-clock-in-net
  • Marcus.D
    Marcus.D over 7 years
    Thank you for your comment. I always trying to find the shortest way to solve a problem by using the available features of the .NET framework. And share it.
  • Arek
    Arek about 5 years
    It doesn't seem to work on .NET 4.5.2 and Win 10 x64. Datetimes have year of 0001.
  • Jason D
    Jason D over 2 years
    how does this answer compare/contrast with the other 10 answers here? Can you please elaborate why this might be better?
  • Vladimir Kramar
    Vladimir Kramar over 2 years
    Jason, first of all: 1.) it should work faster 2.) it does not contain any error-prone logic as the so-called green answer does (like recalling DateTime.UtcNow can get us back in the past and other issues regarding the multithreading environment). 3.) this way of implementing will be more supportable. 4.) and the last one: I did not really test other answers, but I suspect they will not be working in some possible corner cases at all.
  • Jason D
    Jason D over 2 years
    That's good Vladimir. I only suggest that it is good to state these ideas when you post your solution. It will be helpful for others who come across your answer.
  • Vladimir Kramar
    Vladimir Kramar over 2 years
    I am sorry if my answer is not obvious. So to be clear my exact point is to use this DateTimeOffset.Now.ToUnixTimeMilliseconds(); instead of this HiResDateTime.TimestampNow; Thank you!