timeutil

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 2 Imported by: 0

README

timeutil

import "github.com/brpaz/lib-go/timeutil"

Package timeutil provides utilities for working with time.

Clock

Inject Clock into any struct that needs the current time:

type Cache struct {
    clock timeutil.Clock
}

func NewCache(clock timeutil.Clock) *Cache {
    return &Cache{clock: clock}
}

func (c *Cache) IsExpired(expiresAt time.Time) bool {
    return c.clock.Now().After(expiresAt)
}

In production, pass RealClock:

cache := NewCache(timeutil.RealClock{})

In tests, pass FixedClock to control time precisely:

fixed := time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
cache := NewCache(timeutil.NewFixedClock(fixed))

assert.True(t, cache.IsExpired(fixed.Add(-time.Second)))
assert.False(t, cache.IsExpired(fixed.Add(time.Second)))
Sleeper

Inject Sleeper into any struct that pauses execution — retry/backoff, polling, throttling — so tests run instantly instead of waiting on real time:

type Retrier struct {
    sleeper timeutil.Sleeper
}

func (r *Retrier) Do(fn func() error) error {
    backoff := 100 * time.Millisecond
    for i := 0; i < 5; i++ {
        if err := fn(); err == nil {
            return nil
        }
        r.sleeper.Sleep(backoff)
        backoff *= 2
    }
    return errors.New("max retries exceeded")
}

In production, pass RealSleeper:

r := &Retrier{sleeper: timeutil.RealSleeper{}}

In tests, pass FakeSleeper to assert on the schedule without waiting:

fake := timeutil.NewFakeSleeper()
r    := &Retrier{sleeper: fake}

_ = r.Do(failingFn)
assert.Equal(t, []time.Duration{100 * time.Millisecond, 200 * time.Millisecond}, fake.Calls())
Ticker

Inject Ticker into any struct that runs periodic work — pollers, schedulers, heartbeats — so tests can drive ticks manually instead of waiting on real intervals:

type Poller struct {
    ticker timeutil.Ticker
}

func (p *Poller) Run(ctx context.Context, fn func()) {
    defer p.ticker.Stop()
    for {
        select {
        case <-ctx.Done():
            return
        case <-p.ticker.C():
            fn()
        }
    }
}

In production, pass RealTicker:

p := &Poller{ticker: timeutil.NewRealTicker(time.Minute)}

In tests, pass FakeTicker to trigger ticks on demand:

fake := timeutil.NewFakeTicker()
p    := &Poller{ticker: fake}

go p.Run(ctx, fn)
fake.Tick(time.Now())
fake.Tick(time.Now())

Index

type Clock

Clock provides the current time. Inject it into structs that need time so tests can control it.

type Clock interface {
    Now() time.Time
}

type FakeSleeper

FakeSleeper records every requested duration instead of sleeping. Use it in tests to assert on retry/backoff schedules without waiting for real time to pass. Safe for concurrent use.

type FakeSleeper struct {
    Slept []time.Duration
    // contains filtered or unexported fields
}

func NewFakeSleeper
func NewFakeSleeper() *FakeSleeper

NewFakeSleeper creates an empty FakeSleeper.

func (*FakeSleeper) Calls
func (s *FakeSleeper) Calls() []time.Duration

Calls returns a copy of every duration recorded so far, in call order.

func (*FakeSleeper) Sleep
func (s *FakeSleeper) Sleep(d time.Duration)

Sleep records d instead of sleeping.

func (*FakeSleeper) Total
func (s *FakeSleeper) Total() time.Duration

Total returns the sum of every duration recorded so far.

type FakeTicker

FakeTicker delivers ticks only when told to via Tick. Use it in tests to drive periodic code deterministically without waiting on real time. Safe for concurrent use.

type FakeTicker struct {
    // contains filtered or unexported fields
}

func NewFakeTicker
func NewFakeTicker() *FakeTicker

NewFakeTicker creates a FakeTicker with an unbuffered channel.

func (*FakeTicker) C
func (t *FakeTicker) C() <-chan time.Time

func (*FakeTicker) Stop
func (t *FakeTicker) Stop()

Stop marks the ticker as stopped. Safe to call multiple times.

func (*FakeTicker) Stopped
func (t *FakeTicker) Stopped() bool

Stopped reports whether Stop has been called.

func (*FakeTicker) Tick
func (t *FakeTicker) Tick(tm time.Time)

Tick sends tm on the ticker's channel, simulating an elapsed interval. It blocks until a receiver reads it.

type FixedClock

FixedClock always returns the same time. Use in tests.

type FixedClock struct {
    T time.Time
}

func NewFixedClock
func NewFixedClock(t time.Time) FixedClock

NewFixedClock creates a FixedClock at the given time.

func (FixedClock) Now
func (c FixedClock) Now() time.Time

type RealClock

RealClock returns the actual current time.

type RealClock struct{}

func (RealClock) Now
func (RealClock) Now() time.Time

type RealSleeper

RealSleeper sleeps for real, delegating to time.Sleep.

type RealSleeper struct{}

func (RealSleeper) Sleep
func (RealSleeper) Sleep(d time.Duration)

type RealTicker

RealTicker ticks for real, delegating to time.Ticker.

type RealTicker struct {
    // contains filtered or unexported fields
}

func NewRealTicker
func NewRealTicker(d time.Duration) *RealTicker

NewRealTicker creates a RealTicker that ticks every d.

func (*RealTicker) C
func (t *RealTicker) C() <-chan time.Time

func (*RealTicker) Stop
func (t *RealTicker) Stop()

type Sleeper

Sleeper pauses execution for a duration. Inject it into structs that sleep (retry/backoff, polling, throttling) so tests can run instantly instead of waiting on real time.

type Sleeper interface {
    Sleep(d time.Duration)
}

type Ticker

Ticker delivers the current time on a channel at regular intervals. Inject it into structs that run periodic work — pollers, schedulers, heartbeats — so tests can drive ticks manually instead of waiting on real intervals.

type Ticker interface {
    // C returns the channel on which ticks are delivered.
    C() <-chan time.Time

    // Stop turns off the ticker. No more ticks are sent after Stop returns.
    Stop()
}

Generated by gomarkdoc

Documentation

Overview

Package timeutil provides utilities for working with time.

Clock

Inject Clock into any struct that needs the current time:

type Cache struct {
    clock timeutil.Clock
}

func NewCache(clock timeutil.Clock) *Cache {
    return &Cache{clock: clock}
}

func (c *Cache) IsExpired(expiresAt time.Time) bool {
    return c.clock.Now().After(expiresAt)
}

In production, pass RealClock:

cache := NewCache(timeutil.RealClock{})

In tests, pass FixedClock to control time precisely:

fixed := time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
cache := NewCache(timeutil.NewFixedClock(fixed))

assert.True(t, cache.IsExpired(fixed.Add(-time.Second)))
assert.False(t, cache.IsExpired(fixed.Add(time.Second)))

Sleeper

Inject Sleeper into any struct that pauses execution — retry/backoff, polling, throttling — so tests run instantly instead of waiting on real time:

type Retrier struct {
    sleeper timeutil.Sleeper
}

func (r *Retrier) Do(fn func() error) error {
    backoff := 100 * time.Millisecond
    for i := 0; i < 5; i++ {
        if err := fn(); err == nil {
            return nil
        }
        r.sleeper.Sleep(backoff)
        backoff *= 2
    }
    return errors.New("max retries exceeded")
}

In production, pass RealSleeper:

r := &Retrier{sleeper: timeutil.RealSleeper{}}

In tests, pass FakeSleeper to assert on the schedule without waiting:

fake := timeutil.NewFakeSleeper()
r    := &Retrier{sleeper: fake}

_ = r.Do(failingFn)
assert.Equal(t, []time.Duration{100 * time.Millisecond, 200 * time.Millisecond}, fake.Calls())

Ticker

Inject Ticker into any struct that runs periodic work — pollers, schedulers, heartbeats — so tests can drive ticks manually instead of waiting on real intervals:

type Poller struct {
    ticker timeutil.Ticker
}

func (p *Poller) Run(ctx context.Context, fn func()) {
    defer p.ticker.Stop()
    for {
        select {
        case <-ctx.Done():
            return
        case <-p.ticker.C():
            fn()
        }
    }
}

In production, pass RealTicker:

p := &Poller{ticker: timeutil.NewRealTicker(time.Minute)}

In tests, pass FakeTicker to trigger ticks on demand:

fake := timeutil.NewFakeTicker()
p    := &Poller{ticker: fake}

go p.Run(ctx, fn)
fake.Tick(time.Now())
fake.Tick(time.Now())

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Clock

type Clock interface {
	Now() time.Time
}

Clock provides the current time. Inject it into structs that need time so tests can control it.

type FakeSleeper

type FakeSleeper struct {
	Slept []time.Duration
	// contains filtered or unexported fields
}

FakeSleeper records every requested duration instead of sleeping. Use it in tests to assert on retry/backoff schedules without waiting for real time to pass. Safe for concurrent use.

func NewFakeSleeper

func NewFakeSleeper() *FakeSleeper

NewFakeSleeper creates an empty FakeSleeper.

func (*FakeSleeper) Calls

func (s *FakeSleeper) Calls() []time.Duration

Calls returns a copy of every duration recorded so far, in call order.

func (*FakeSleeper) Sleep

func (s *FakeSleeper) Sleep(d time.Duration)

Sleep records d instead of sleeping.

func (*FakeSleeper) Total

func (s *FakeSleeper) Total() time.Duration

Total returns the sum of every duration recorded so far.

type FakeTicker

type FakeTicker struct {
	// contains filtered or unexported fields
}

FakeTicker delivers ticks only when told to via Tick. Use it in tests to drive periodic code deterministically without waiting on real time. Safe for concurrent use.

func NewFakeTicker

func NewFakeTicker() *FakeTicker

NewFakeTicker creates a FakeTicker with an unbuffered channel.

func (*FakeTicker) C

func (t *FakeTicker) C() <-chan time.Time

func (*FakeTicker) Stop

func (t *FakeTicker) Stop()

Stop marks the ticker as stopped. Safe to call multiple times.

func (*FakeTicker) Stopped

func (t *FakeTicker) Stopped() bool

Stopped reports whether Stop has been called.

func (*FakeTicker) Tick

func (t *FakeTicker) Tick(tm time.Time)

Tick sends tm on the ticker's channel, simulating an elapsed interval. It blocks until a receiver reads it.

type FixedClock

type FixedClock struct {
	T time.Time
}

FixedClock always returns the same time. Use in tests.

func NewFixedClock

func NewFixedClock(t time.Time) FixedClock

NewFixedClock creates a FixedClock at the given time.

func (FixedClock) Now

func (c FixedClock) Now() time.Time

type RealClock

type RealClock struct{}

RealClock returns the actual current time.

func (RealClock) Now

func (RealClock) Now() time.Time

type RealSleeper

type RealSleeper struct{}

RealSleeper sleeps for real, delegating to time.Sleep.

func (RealSleeper) Sleep

func (RealSleeper) Sleep(d time.Duration)

type RealTicker

type RealTicker struct {
	// contains filtered or unexported fields
}

RealTicker ticks for real, delegating to time.Ticker.

func NewRealTicker

func NewRealTicker(d time.Duration) *RealTicker

NewRealTicker creates a RealTicker that ticks every d.

func (*RealTicker) C

func (t *RealTicker) C() <-chan time.Time

func (*RealTicker) Stop

func (t *RealTicker) Stop()

type Sleeper

type Sleeper interface {
	Sleep(d time.Duration)
}

Sleeper pauses execution for a duration. Inject it into structs that sleep (retry/backoff, polling, throttling) so tests can run instantly instead of waiting on real time.

type Ticker

type Ticker interface {
	// C returns the channel on which ticks are delivered.
	C() <-chan time.Time

	// Stop turns off the ticker. No more ticks are sent after Stop returns.
	Stop()
}

Ticker delivers the current time on a channel at regular intervals. Inject it into structs that run periodic work — pollers, schedulers, heartbeats — so tests can drive ticks manually instead of waiting on real intervals.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL