diff --git a/CHANGELOG.md b/CHANGELOG.md index b524115..2c6d19c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [5.21.0] - 2023-06-30 +### Added +- Instant type to make working with monotonically increasing times more convenient. + ## [5.20.0] - 2023-06-17 ### Added - Expanded Option type SQL Value support to handle value custom types and honour the `driver.Valuer` interface. @@ -62,7 +66,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added `timext.NanoTime` for fast low level monotonic time with nanosecond precision. -[Unreleased]: https://github.com/go-playground/pkg/compare/v5.20.0...HEAD +[Unreleased]: https://github.com/go-playground/pkg/compare/v5.21.0...HEAD +[5.21.0]: https://github.com/go-playground/pkg/compare/v5.20.0..v5.21.0 [5.20.0]: https://github.com/go-playground/pkg/compare/v5.19.0..v5.20.0 [5.19.0]: https://github.com/go-playground/pkg/compare/v5.18.0..v5.19.0 [5.18.0]: https://github.com/go-playground/pkg/compare/v5.17.2..v5.18.0 diff --git a/README.md b/README.md index b0e92f1..b7ee92a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pkg -![Project status](https://img.shields.io/badge/version-5.20.0-green.svg) +![Project status](https://img.shields.io/badge/version-5.21.0-green.svg) [![Lint & Test](https://github.com/go-playground/pkg/actions/workflows/go.yml/badge.svg)](https://github.com/go-playground/pkg/actions/workflows/go.yml) [![Coverage Status](https://coveralls.io/repos/github/go-playground/pkg/badge.svg?branch=master)](https://coveralls.io/github/go-playground/pkg?branch=master) [![GoDoc](https://godoc.org/github.com/go-playground/pkg?status.svg)](https://pkg.go.dev/mod/github.com/go-playground/pkg/v5) diff --git a/time/instant.go b/time/instant.go new file mode 100644 index 0000000..218f58a --- /dev/null +++ b/time/instant.go @@ -0,0 +1,31 @@ +//go:build go1.18 +// +build go1.18 + +package timeext + +import "time" + +// Instant represents a monotonic instant in time. +// +// Instants are opaque types that can only be compared with one another and allows measuring of duration. +type Instant struct { + monotonic int64 +} + +// NewInstant returns a new Instant. +func NewInstant() Instant { + return Instant{monotonic: NanoTime()} +} + +// Elapsed returns the duration since the instant was created. +func (i Instant) Elapsed() time.Duration { + return time.Duration(NanoTime() - i.monotonic) +} + +// Since returns the duration elapsed from another Instant, or zero is that Instant is later than this one. +func (i Instant) Since(instant Instant) time.Duration { + if instant.monotonic > i.monotonic { + return 0 + } + return time.Duration(i.monotonic - instant.monotonic) +} diff --git a/time/instant_test.go b/time/instant_test.go new file mode 100644 index 0000000..4a26b02 --- /dev/null +++ b/time/instant_test.go @@ -0,0 +1,22 @@ +//go:build go1.18 +// +build go1.18 + +package timeext + +import ( + "testing" +) + +func TestInstant(t *testing.T) { + i := NewInstant() + if i.Elapsed() < 0 { + t.Fatalf("elapsed time should be always be monotonically increasing") + } + i2 := NewInstant() + if i2.Since(i) < 0 { + t.Fatalf("time since instant should always be after") + } + if i.Since(i2) != 0 { + t.Fatalf("time since instant should be zero") + } +}