tags:

views:

2906

answers:

5

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.

+6  A: 

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.

binarycoder
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?
Hermann
@Hermann: yes, as I just mentioned in my answer.
MusiGenesis
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.
Henk Holterman
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.
Hermann
A: 

See this answer. The StopWatch has high resolution, if the computer supports it.

David Basarab
A: 

If need the timestamp to perform benchmarks use StopWatch which has much better precision than DateTime.Now.

Piotr Czapla
+10  A: 

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.

MusiGenesis
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.
Hermann
YESSSS!!!! Eat my dust, Jeff Atwood!!!!!!
MusiGenesis
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
Learned this the hard way, of course. :)
MusiGenesis
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.
Hermann
<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. :)
binarycoder
@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
@Hermann: do you know about the System.TimeZone class? (just discovered it myself)
MusiGenesis
@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).
Hermann
@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.
Hermann
@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.
Hermann
@Hermann: thanks, I edited my code. I should have mentioned in my original answer that this code was not tested.
MusiGenesis
@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?
Hermann
@Hermann: that is a very interesting result. It seems to suggest that Stopwatch, while having a very high resolution, is not very accurate.
MusiGenesis
@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
@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.
MusiGenesis
+1  A: 

1). If you need high resolution absolute accuracy: you can't use DateTime.Now when it is based on a clock with 15 ms interval (unless it was possible "slide" the phase).

Instead an external source of better resolution absolute accuracy time (e.g. ntp), t1 below, could be combined with the high resolution timer (StopWatch / QueryPerformanceCounter).


2). If you just need high resolution:

Sample DateTime.Now (t1) once together with a value from the high resolution timer (StopWatch / QueryPerformanceCounter) (tt0).

If current value of the high resolution timer is tt then the current time, t, is:

t = t1 + (tt - tt0)


3). An alternative could be to disentangle absolute time and order of the financial events: one value for absolute time (15 ms resolution, possibly off-by-several-minutes) and one value for the order (e.g. incrementing a value by one each time and store that). The start value for the order can be based on some system global value or be derived from the absolute time when the application starts.

This solution would be more robust as it is not dependent on the underlying hardware implementation of the clocks/timers (that may vary betweens systems).

Peter Mortensen