I'm using timestamps to temporally order concurrent changes in my program, and require that each timestamp of a change be unique. However, I've discovered that simply calling DateTime.Now
is insufficient, as it will often return the same value if called in quick succession.
I have some thoughts, but nothing strikes me as the "best" solution to this. Is there a method I can write that will guarantee each successive call produces a unique DateTime
?
Should I perhaps be using a different type for this, maybe a long int? DateTime
has the obvious advantage of being easily interpretable as a real time, unlike, say, an incremental counter.
Update: Here's what I ended up coding as a simple compromise solution that still allows me to use DateTime
as my temporal key, while ensuring uniqueness each time the method is called:
private static long _lastTime; // records the 64-bit tick value of the last time private static object _timeLock = new object(); internal static DateTime GetCurrentTime() { lock ( _timeLock ) { // prevent concurrent access to ensure uniqueness DateTime result = DateTime.UtcNow; if ( result.Ticks <= _lastTime ) result = new DateTime( _lastTime + 1 ); _lastTime = result.Ticks; return result; } }
Because each tick value is only one 10-millionth of a second, this method only introduces noticeable clock skew when called on the order of 10 million times per second (which, by the way, it is efficient enough to execute at), meaning it's perfectly acceptable for my purposes.
Here is some test code:
DateTime start = DateTime.UtcNow; DateTime prev = Kernel.GetCurrentTime(); Debug.WriteLine( "Start time : " + start.TimeOfDay ); Debug.WriteLine( "Start value: " + prev.TimeOfDay ); for ( int i = 0; i < 10000000; i++ ) { var now = Kernel.GetCurrentTime(); Debug.Assert( now > prev ); // no failures here! prev = now; } DateTime end = DateTime.UtcNow; Debug.WriteLine( "End time: " + end.TimeOfDay ); Debug.WriteLine( "End value: " + prev.TimeOfDay ); Debug.WriteLine( "Skew: " + ( prev - end ) ); Debug.WriteLine( "GetCurrentTime test completed in: " + ( end - start ) );
...and the results:
Start time: 15:44:07.3405024 Start value: 15:44:07.3405024 End time: 15:44:07.8355307 End value: 15:44:08.3417124 Skew: 00:00:00.5061817 GetCurrentTime test completed in: 00:00:00.4950283
So in other words, in half a second it generated 10 million unique timestamps, and the final result was only pushed ahead by half a second. In real-world applications the skew would be unnoticeable.
timestamp is a data type that exposes automatically generated binary numbers, which are guaranteed to be unique within a database.
Basically all you need to do is use the atomic counter value to add addition fixed width precision to your timestamp.
Of course, if you create integer timestamps by less than millisecond interval, then they can't all be unique. Note that the absolute value nanoTime() is arbitrary. If you want absolute time, calibrate it somehow, i.e. compare it to currentTimeMillis() when starting.
This uses the DateTime. Now. Ticks property, which is “the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001”. It will therefore always be unique, unless the id is generated in a threaded scenario.
One way to get a strictly ascending sequence of timestamps with no duplicates is the following code.
Compared to the other answers here this one has the following benefits:
lock
statements.public class HiResDateTime { private static long lastTimeStamp = DateTime.UtcNow.Ticks; public static long UtcNowTicks { get { long original, newValue; do { original = lastTimeStamp; long now = DateTime.UtcNow.Ticks; newValue = Math.Max(now, original + 1); } while (Interlocked.CompareExchange (ref lastTimeStamp, newValue, original) != original); return newValue; } } }
Er, the answer to your question is that "you can't," since if two operations occur at the same time (which they will in multi-core processors), they will have the same timestamp, no matter what precision you manage to gather.
That said, it sounds like what you want is some kind of auto-incrementing thread-safe counter. To implement this (presumably as a global service, perhaps in a static class), you would use the Interlocked.Increment
method, and if you decided you needed more than int.MaxValue
possible versions, also Interlocked.Read
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With