Low commit activity in last 3 years
High-resolution stopwatch using monotonic clock with start, stop, reset, lap timing, pause/resume support, and a class-level measure helper for block timing.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

philiprehberger-stopwatch

Tests Gem Version Last updated

Precision stopwatch with lap timing, pause/resume, and formatted output

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-stopwatch"

Or install directly:

gem install philiprehberger-stopwatch

Usage

require "philiprehberger/stopwatch"

sw = Philiprehberger::Stopwatch.new
sw.start
# ... do work ...
sw.lap('phase 1')
# ... do more work ...
sw.lap('phase 2')
sw.stop

sw.elapsed  # => 1.234 (seconds)
sw.laps     # => [{name: "phase 1", elapsed: 0.5, split: 0.5}, {name: "phase 2", elapsed: 0.734, split: 1.234}]

Pause and Resume

sw = Philiprehberger::Stopwatch.new
sw.start
sleep(0.1)
sw.pause      # alias for #stop
sw.resume     # alias for #start
sleep(0.1)
sw.stop
sw.elapsed    # => ~0.2 (paused time not counted)

Restart

sw = Philiprehberger::Stopwatch.new
sw.start
sleep(0.1)
sw.lap('phase 1')
sw.restart    # equivalent to #reset followed by #start
sleep(0.05)
sw.elapsed    # => ~0.05 (prior state cleared)

Formatted Output

sw = Philiprehberger::Stopwatch.new
sw.start
sleep(0.5)
sw.formatted_elapsed  # => "500.12ms"

sleep(65)
sw.formatted_elapsed  # => "1m 5s"

Lap Statistics

sw = Philiprehberger::Stopwatch.new
sw.start
sw.lap('setup')
sw.lap('process')
sw.lap('teardown')

sw.lap_stats
# => { count: 3, total: 0.053, avg: 0.018, min: 0.005, max: 0.031 }

sw.formatted_laps
# => [{ name: "setup", elapsed: 0.005, formatted: "5.00ms" }, ...]

Serialization

sw = Philiprehberger::Stopwatch.new
sw.start
sw.lap('setup')
sw.lap('process')
sw.stop

sw.to_h
# => { running: false, paused: true, elapsed: 0.053,
#      formatted_elapsed: "53.00ms", laps: [...], lap_stats: {...} }

Named Checkpoints

sw = Philiprehberger::Stopwatch.new
sw.start
sleep(0.1)
sw.checkpoint('after_setup')
sleep(0.2)
sw.checkpoint('after_process')

sw.elapsed_at('after_setup')    # => ~0.1 (cumulative split at that moment)
sw.elapsed_at('after_process')  # => ~0.3

sw.since('after_setup')         # => ~0.2 (time elapsed since that checkpoint)
sw.since('after_process')       # => ~0.0

sw.checkpoints
# => { "after_setup" => 0.1003, "after_process" => 0.3007 }

Block Timing

result, elapsed = Philiprehberger::Stopwatch.measure { expensive_operation }
puts "Took #{elapsed} seconds"

formatted = Philiprehberger::Stopwatch.measure_formatted { expensive_operation }
puts "Took #{formatted}"  # => "Took 12.50ms"

Sequential Tick Timing

For ad-hoc per-iteration timing in loops where naming each lap is overkill:

sw = Philiprehberger::Stopwatch.new.start

items.each do |item|
  process(item)
  puts "step took #{sw.tick} seconds"
end

Each call to #tick returns the seconds elapsed since the previous tick (or since #start for the first call). A thinner #lap for unnamed sequential measurement.

API

Method Description
Stopwatch.new Create a new stopwatch
#start Start or resume the stopwatch
#stop Pause the stopwatch
#pause Alias for #stop
#resume Alias for #start (resumes a paused stopwatch)
#reset Reset all state
#restart Reset and start the stopwatch in one call
#lap(name) Record a lap with optional name
#tick Return seconds since the previous tick (or since start on the first call)
#elapsed Total elapsed time in seconds
#elapsed_ms Total elapsed time in milliseconds
#elapsed_us Total elapsed time in microseconds
#laps Array of recorded lap data
#running? Whether the stopwatch is actively running
#paused? Whether the stopwatch is paused
#checkpoint(name) Record a named moment storing the current cumulative split time
#elapsed_at(name) Cumulative split time at the named checkpoint
#since(name) Elapsed time since the named checkpoint was recorded
#checkpoints Hash of all recorded checkpoints
#lap_stats Aggregate lap statistics (count, total, avg, min, max)
#formatted_elapsed Human-readable elapsed time string
#formatted_laps Array of laps with formatted times
#to_h Serialize full stopwatch state to a hash
Stopwatch.measure { block } Measure block execution time
Stopwatch.measure_formatted { block } Measure block and return formatted string

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT