I had a need for an easy-to-use stopwatch class in a project I’m currently working on so I decided to create one and open source it.
Usage
Var watch As New StopWatch
watch.Start
// Do something that you want to time...
watch.Stop
// Easy access to watch properties:
Var ms As Double = watch.ElapsedMilliseconds
Var t As Double = watch.ElapsedTicks
Var di As DateInterval = watch.Elapsed
// StopWatch will even format the interval into a string:
Var s As String = watch.ElapsedAsString // E.g "4 minutes, 3 seconds, 89 ms"
// Query if the stopwatch is running:
If watch.IsRunning Then
// Do something
End If
// You can call `ElapsedTicks`, etc whilst the stopwatch is running too.
Limitations
If the stopwatch is left running for > 28 days, calling ElapsedMilliseconds or ElapsedTicks will raise an UnsupportedOperationException. This is because there are between 28-31 days in a month 365-366 days in a year.
If you check the source, it does use DateTime and DateInterval under the hood.
StopWatch.Elapsed() will return a DateInterval. The issue with the convenience methods StopWatch.ElapsedMilliseconds() and StopWatch.ElapsedTicks() is that I have to convert a DateInterval to milliseconds which is surprisingly difficult.
Feel free to submit a patch to the repo and I’ll integrate it. Personally, I only use StopWatch for short (seconds or milliseconds) durations so it doesn’t bother me.
You can use the DateTime.SecondsFrom1970 double wich has the epoch.nanoseconds as a double.
That can be used for your ElapsedMilliseconds, ElapsedNanoSeconds ElapsedSeconds ElapsedTicks etc
Seconds from 1970 is about 1,632,435,226 ≈ 1.6e9
MilliSeconds from 1970 is about 1,632,435,226,000 ≈ 1.6e12
MicroSeconds from 1970 is about 1,632,435,226,000,000 ≈ 1.6e15
NanoSeconds from 1970 is about 1,632,435,226,000,000,000 ≈ 1.6e18
Largest integer that double can accurately represent: 9,007,199,254,740,992 ≈ 9e15
So the nanoseconds cannot be accurately represented in a Double as a Double only has about a 15.5 bit precision (15 bit plus when the 16 bit part where the exponent is 0)
But then MicroSeconds should do fine anyway …
[and this was also a nice demonstration of why Scientific number format is quite useful - it makes it MUCH easier to compare large numbers]
If you have the habit to read them, yes.
I’d have preferred “regular” numbers so I could count the 0s rather than moving a coma from a given number of decimals. Don’t get me wrong, I’m sure it’s nice when you are accustomed to this notation, but otherwise, counting 0s (especially when grouped by 3) is simpler.
So xojo manages this for you. You can re-calculate if you want from the properties of DateInterval. No need to overflow anything or use double for (now - previous) … A stopwatch should not be used for timing as long as “9,007,199,254,740,992 ≈ 9e15” that should be a user error, you know a real life stopwatch does not time so long as well now does it ?
Unless I’m misunderstanding DateInterval.Nanoseconds, I think there is a bug in the Xojo framework.
Replace the contents of StopWatch.ElapsedMilliseconds() with this:
Return Elapsed.Nanoseconds / NS_IN_MS
Now run the app and click Start. Once the stopwatch has begun, click the “Elapsed Milliseconds” button several times. This should show the number of milliseconds that has elapsed since the stopwatch was started.
Notice how the number of elapsed milliseconds is going up and down (never exceeds 1000). This is wrong. I think DateInterval.Nanoseconds is never exceeding 100,000,000.
Yes. The fact that nanoseconds is not the whole dateinterval as nanoseconds but the nanoseconds part of it. Once a second is reached, seconds property goes up by one and nanoseconds are reset to 0.
@DerkJ, this is the reason that the stopwatch is limited to 28 days as you can see that otherwise there is no way to accurately report the time interval because you’d have to factor in leap years, computing the months between etc. Hence this code in ElapsedMilliseconds():
Var di As DateInterval = Elapsed
Return (di.Days * MS_IN_DAY) + (di.Hours * MS_IN_HOUR) + _
(di.Minutes * MS_IN_MIN) + (di.Seconds * MS_IN_SEC) + _
(di.Nanoseconds / NS_IN_MS)