Open source class - `StopWatch`

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.

Repository

You can download the source from its GitHub repository.

15 Likes

Thanks for contributing to the Xojo community! :smiley:

3 Likes

Can’t you overcome the limitations by using DateTime and DateInterval under the hood?

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.

1 Like

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]

1 Like

See Markus’s reply for why that won’t work. There are too many nanoseconds between the Epoch and now to store in a 64-bit integer.

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.

http://documentation.xojo.com/api/language/dateinterval.html
has
http://documentation.xojo.com/api/language/dateinterval.html#dateinterval-nanoseconds

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.

I get this:

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.

Am I missing something here?

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.

2 Likes

Thought so, thanks @Ulrich_Bogun.

@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)
1 Like

No.

Too many for a Double - yes.

Too many for a 64bit Integer - no.

An unsigned 64bit integer can accurately represent a much larger Integer than a Double can: 9,223,372,036,854,775,807

Yep - I realised that shortly after posting it :man_facepalming:

Learned something too. Thanks Ulrich.