/chrono-go

Primary LanguageGoMIT LicenseMIT

GoDoc Build Status Coverage Status Report Card

go.mway.dev/chrono

chrono is a small collection of useful time-based types and helpers. It provides:

  • go.mway.dev/chrono
  • go.mway.dev/chrono/clock
    • A common Clock interface shared by all clocks
    • Monotonic and wall Clocks
    • A ThrottledClock to provide configurable time memoization to reduce time-based syscalls
    • A FakeClock implementation, to support mocking time
    • A lightweight Stopwatch for trivially (and continuously) measuring elapsed time
  • go.mway.dev/periodic
    • A [Handle][periodic-handler-doc] to manage functions that are run periodically via Start
  • go.mway.dev/chrono/rate
    • A Recorder that performs simple, empirical rate calculation, optionally against a custom clock
    • A lightweight Rate type that provides simple translation of an absolute rate into different denominations

These packages are intended for general use, and as a replacement for the long-archived github.com/andres-erbsen/clock package.

Getting Started

To start using a Clock, first determine whether the goal is to tell time, or to measure time:

  • When telling time, wall clocks are necessary. Use NewWallClock.
  • When measuring time, monotonic clocks are preferable, though in most cases a wall clock can be used as well. Use NewMonotonicClock.

The only difference between a wall clock and a monotonic clock is the time source being used, which in this implementation is either a TimeFunc or a NanotimeFunc.

After selecting a type of clock, simply construct and use it:

package main

import (
  "time"
  
  "go.mway.dev/chrono/clock"
)

func main() {
  var (
    clk       = clock.NewWallClock()
    now       = clk.Now()       // time.Time
    nanos     = clk.Nanotime()  // int64
    stopwatch = clk.NewStopwatch()
    ticker    = clk.NewTicker(time.Second)
    timer     = clk.NewTimer(3*time.Second)
  )

  fmt.Printf("It is now: %s (~%d)\n", now, nanos)

  func() {
    for {
      select {
      case <-ticker.C:
        fmt.Println("Tick!")
      case <-timer.C:
        fmt.Println("Done!")
        return
      }
    }
  }()

  fmt.Println("Ticker/timer took", stopwatch.Elapsed())

  // etc.
}

The goal of Clock is to be comparable to using the standard library time, i.e. any common time-related functions should be provided as part of the Clock API.

Examples

TODO

Throttling

In some cases, it may be desirable to limit the number of underlying time-based syscalls being made, for example when needing to attribute time within a tight loop. Rather than needing to throttle or debounce such calls themselves, users can use a ThrottledClock, which does this at a configurable resolution:

// Use monotonic time that only updates once per second
clk := clock.NewThrottledMonotonicClock(time.Second)
defer clk.Stop() // free resources

// Issue an arbitrary number of time-based calls
for i := 0; i < 1_000_000; i++ {
  clk.Nanotime()
}

A background routine updates the clock's time from the configured source at the specified interval, and time-based calls on a ThrottledClock use the internally cached time.

Contributions & Feedback

Pull requests are welcome, and all feedback is appreciated!