Documentation
¶
Overview ¶
Package filter provides headless value transformations for Go callers, template engines, and other dynamic-value runtimes.
What it is: pure functions that take a runtime value and return a transformed value or an error. Strings, slices, maps, dates, and numbers.
What it is not: a SQL builder, an ORM helper, an i18n framework, an XSS sanitizer, a query DSL, or a CLI argument parser. It does no I/O. It reads no environment variables. It does not consult a global locale or timezone. Functions that need "now" use SystemClock by default and expose Clock-injected variants for deterministic callers. Random and Shuffle are non-deterministic convenience filters; RandomWithRand and ShuffleWithRand accept explicit *rand.Rand values for deterministic callers.
Errors ¶
Every public function returns *Error or wraps one. Errors are categorized by Kind: KindInvalidInput, KindNotFound, KindArithmetic, KindFormat. Match with errors.Is(err, ErrNotFound) (matches by Kind).
Stability ¶
Stable semantics include UTF-8 length, UTC default timezone, nil/false-only falsiness, Find returning *Error{Kind:KindNotFound} on miss, Date's token grammar, and Map silently substituting nil for missing keys. See SPECS/00-runtime-contract.md for the durable runtime contract.
Index ¶
- Variables
- func Abs(input any) (float64, error)
- func Append(input, toAppend string) string
- func AtLeast(input, minimum any) (float64, error)
- func AtMost(input, maximum any) (float64, error)
- func Average(input any) (float64, error)
- func Base64Decode(input string) (string, error)
- func Base64Encode(input string) string
- func Base64URLDecode(input string) (string, error)
- func Base64URLEncode(input string) string
- func Bytes(input any) (string, error)
- func Camelize(input string) string
- func Capitalize(input string) string
- func Ceil(input any) (float64, error)
- func Compact(input any, key ...string) ([]any, error)
- func Concat(input, other any) ([]any, error)
- func Dasherize(input string) string
- func Date(input any, format string) (string, error)
- func Day(input any) (int, error)
- func Divide(input, divisor any) (float64, error)
- func Escape(input string) string
- func EscapeOnce(input string) string
- func Extract(input any, key string) (any, error)
- func Find(input any, key string, value any) (any, error)
- func FindIndex(input any, key string, value any) (int, error)
- func First(input any) (any, error)
- func Floor(input any) (float64, error)
- func Has(input any, key string, value any) (bool, error)
- func HasFunc(input any, key string, predicate func(any) bool) (bool, error)
- func Index(input any, i int) (any, error)
- func Join(input any, separator string) (string, error)
- func Last(input any) (any, error)
- func Length(input string) int
- func Lower(input string) string
- func Map(input any, key string) ([]any, error)
- func Max(input any) (float64, error)
- func Min(input any) (float64, error)
- func Minus(input, subtrahend any) (float64, error)
- func Modulo(input, modulus any) (float64, error)
- func Month(input any) (int, error)
- func MonthFull(input any) (string, error)
- func Number(input any, format string) (string, error)
- func Ordinalize(number int) string
- func Pascalize(input string) string
- func Pluralize(count int, singular, plural string) string
- func Plus(input, addend any) (float64, error)
- func Prepend(input, toPrepend string) string
- func Random(input any) (any, error)
- func RandomWithRand(r *rand.Rand, input any) (any, error)
- func Reject(input any, key string, value any) ([]any, error)
- func RejectFunc(input any, key string, predicate func(any) bool) ([]any, error)
- func Remove(input, toRemove string) string
- func RemoveFirst(input, toRemove string) string
- func RemoveLast(input, toRemove string) string
- func Replace(input, old, replacement string) string
- func ReplaceFirst(input, old, replacement string) string
- func ReplaceLast(input, old, replacement string) string
- func Reverse(input any) ([]any, error)
- func Round(input, decimals any) (float64, error)
- func SeededRand(s1, s2 uint64) *rand.Rand
- func Shuffle(input any) ([]any, error)
- func ShuffleWithRand(r *rand.Rand, input any) ([]any, error)
- func Size(input any) (int, error)
- func Slice(input any, offset int, length ...int) (any, error)
- func Slugify(input string) string
- func Sort(input any, key ...string) ([]any, error)
- func SortNatural(input any, key ...string) ([]any, error)
- func Split(input, delimiter string) []string
- func StripHTML(input string) string
- func StripNewlines(input string) string
- func Sum(input any) (float64, error)
- func SumBy(input any, key string) (float64, error)
- func TimeAgo(input any) (string, error)
- func TimeAgoWithClock(clock Clock, input any) (string, error)
- func Times(input, multiplier any) (float64, error)
- func Titleize(input string) string
- func Trim(input string) string
- func TrimLeft(input string) string
- func TrimRight(input string) string
- func Truncate(input string, maxLength int, ellipsis ...string) string
- func TruncateWords(input string, maxWords int, ellipsis ...string) string
- func URLDecode(input string) (string, error)
- func URLEncode(input string) string
- func Unique(input any) ([]any, error)
- func UniqueBy(input any, key string) ([]any, error)
- func Upper(input string) string
- func Week(input any) (int, error)
- func Weekday(input any) (string, error)
- func Where(input any, key string, value any) ([]any, error)
- func WhereFunc(input any, key string, predicate func(any) bool) ([]any, error)
- func Year(input any) (int, error)
- type Clock
- type Error
- type ErrorKind
- type FixedClock
- type SystemClock
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrInvalidInput error = kindError(KindInvalidInput) ErrNotFound error = kindError(KindNotFound) ErrArithmetic error = kindError(KindArithmetic) ErrFormat error = kindError(KindFormat) )
Sentinels classify errors by kind. Their dynamic value is immutable, so callers cannot corrupt process-wide errors.Is behavior.
Functions ¶
func Abs ¶
Abs returns the absolute value of input.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result, _ := filter.Abs(-5)
fmt.Println(result)
}
Output: 5
func Average ¶
Average returns the mean of numeric elements. Empty slice returns *Error{Kind:KindInvalidInput} (no defined mean).
func Base64Decode ¶ added in v0.4.15
Base64Decode decodes a standard Base64 string.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result, _ := filter.Base64Decode("aGVsbG8gd29ybGQ=")
fmt.Println(result)
}
Output: hello world
func Base64Encode ¶ added in v0.4.15
Base64Encode encodes a string with standard Base64 (RFC 4648).
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Base64Encode("hello world"))
}
Output: aGVsbG8gd29ybGQ=
func Base64URLDecode ¶ added in v0.5.0
Base64URLDecode decodes a URL-safe Base64 string.
func Base64URLEncode ¶ added in v0.5.0
Base64URLEncode encodes a string with URL-safe Base64 (RFC 4648 §5).
func Bytes ¶
Bytes formats a non-negative whole-number byte count using SI/decimal units (e.g. 1024 → "1.0 KB", 1048576 → "1.0 MB"). Negative, fractional, non-finite, and overflowing inputs return *Error{Kind: KindInvalidInput}.
For binary (KiB / MiB) units, callers can compose humanize.BinaryBytes themselves; this helper owns only the SI form.
func Camelize ¶
Camelize converts input to camelCase.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Camelize("hello_world"))
fmt.Println(filter.Camelize("foo-bar-baz"))
}
Output: helloWorld fooBarBaz
func Capitalize ¶
Capitalize returns input with the first rune title-cased and the rest lowercased. It uppercases the first rune and lowercases the rest.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Capitalize("hELLO"))
fmt.Println(filter.Capitalize("hello world"))
}
Output: Hello Hello world
func Compact ¶ added in v0.4.15
Compact removes nil elements. If key is provided, removes items where the property is nil or unreachable.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result, _ := filter.Compact([]any{"a", nil, "b", nil, "c"})
fmt.Println(result)
}
Output: [a b c]
func Concat ¶ added in v0.4.15
Concat returns a new slice containing the elements of input followed by the elements of other. Both must be slices or arrays.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result, _ := filter.Concat([]any{"a", "b"}, []any{"c", "d"})
fmt.Println(result)
}
Output: [a b c d]
func Dasherize ¶
Dasherize converts input to lowercase words joined by dashes.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Dasherize("helloWorld"))
fmt.Println(filter.Dasherize("FooBar"))
}
Output: hello-world foo-bar
func Date ¶
Date formats input using this package's date format tokens. An empty format returns the canonical "2006-01-02 15:04:05" representation.
Supported tokens:
Y year (2024) y year 2-digit (24) m month 2-digit (03) n month (3) M month abbr (Mar) F month full (March) d day 2-digit (05) j day (5) D weekday abbr (Sat) l weekday full (Saturday) N ISO weekday 1-7 S ordinal suffix (st/nd/rd/th) w weekday 0-6 (Sun=0) z day of year 0-365 W ISO week (1-53) o ISO week-numbering year t days in month L leap year flag (0/1) H hour 24h 2-digit (07) G hour 24h (7) h hour 12h 2-digit (07) g hour 12h (7) i minute (07) s second (07) u microseconds v milliseconds A AM/PM uppercase a am/pm lowercase U Unix timestamp O zone offset (+0800) P zone offset (+08:00) p P but UTC as Z T zone abbreviation (UTC) e zone identifier I daylight-saving flag (0/1) Z zone offset seconds c ISO 8601 date r RFC 2822 date B Swatch Internet time
Unknown letters pass through as literals. A backslash escapes the next byte.
func Divide ¶
Divide divides input by divisor. Returns *Error{Kind: KindArithmetic} when divisor is zero.
func Escape ¶ added in v0.4.15
Escape escapes HTML special characters in input.
This is not an XSS defense; XSS protection requires the context-aware escaper in html/template.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Escape("<p>Hello & World</p>"))
}
Output: <p>Hello & World</p>
func EscapeOnce ¶ added in v0.4.15
EscapeOnce escapes HTML special characters without double-escaping entities.
Same caveat as Escape: not an XSS defense.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.EscapeOnce("<p>already escaped</p>"))
fmt.Println(filter.EscapeOnce("1 < 2 & 3"))
}
Output: <p>already escaped</p> 1 < 2 & 3
func Extract ¶
Extract returns the value at key from input using dot-separated paths.
Keys can traverse maps, arrays, slices, structs, pointers, and interfaces. Failure modes:
- nil input → *Error{Kind: KindInvalidInput}
- empty key, missing path step, or out-of-range index → *Error{Kind: KindNotFound}
- path step incompatible with the current value → *Error{Kind: KindInvalidInput}
Extract intentionally owns this tiny path language instead of outsourcing it to a broader query DSL. It is a core dynamic-value transformation.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
data := map[string]any{
"user": map[string]any{
"name": "Alice",
"age": 30,
},
}
name, _ := filter.Extract(data, "user.name")
fmt.Println(name)
}
Output: Alice
func Find ¶ added in v0.4.15
Find returns the first item whose property at key equals value, or *Error{Kind: KindNotFound} if none matches.
func FindIndex ¶ added in v0.4.15
FindIndex returns the 0-based index of the first matching item, or -1.
func First ¶
First returns the first element of a slice. Empty slices return *Error{Kind: KindNotFound}.
func HasFunc ¶ added in v0.6.3
HasFunc reports whether any item's property at key satisfies predicate.
func Join ¶
Join concatenates the elements of input into a single string with separator between each element. Elements are stringified with fmt.Sprint.
An empty separator is allowed and equivalent to no separator.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result, _ := filter.Join([]string{"a", "b", "c"}, ", ")
fmt.Println(result)
}
Output: a, b, c
func Last ¶
Last returns the last element of a slice. Empty slices return *Error{Kind: KindNotFound}.
func Map ¶
Map returns the values at key from each item in input. Items where the key is missing or unreachable contribute nil so the output preserves input cardinality.
func Modulo ¶
Modulo returns the remainder of input divided by modulus. Returns *Error{Kind: KindArithmetic} when modulus is zero.
func Number ¶
Number formats input according to a `#,###.##`-style format string.
The format mirrors the convention historically used by `humanize.FormatFloat` in github.com/dustin/go-humanize so that templates such as `{{ value | number: '#,###.##' }}` keep producing identical output:
- The number of `#` (or any character) after the `.` controls the decimal precision. Without `.`, integers render without decimals and non-integers keep their natural precision.
- A `,` anywhere in the integer part inserts thousands separators.
Examples:
Number(1234567.89, "#,###.##") → "1,234,567.89" Number(1234567, "#,###.") → "1,234,567" Number(1234.5, "#.#") → "1234.5" Number(1234, "#") → "1234"
Returns *Error{Kind: KindInvalidInput} for non-numeric input and *Error{Kind: KindFormat} for unparseable numeric strings.
func Ordinalize ¶
Ordinalize converts a numeric input to its ordinal English version.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Ordinalize(1))
fmt.Println(filter.Ordinalize(2))
fmt.Println(filter.Ordinalize(3))
fmt.Println(filter.Ordinalize(11))
}
Output: 1st 2nd 3rd 11th
func Pascalize ¶
Pascalize converts input to PascalCase.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Pascalize("hello_world"))
fmt.Println(filter.Pascalize("foo-bar"))
}
Output: HelloWorld FooBar
func Pluralize ¶
Pluralize selects the plural or singular form based on count.
It performs no string interpolation. Callers that need to embed the count must format it themselves (e.g. fmt.Sprintf("%d %s", n, Pluralize(n, "x", "xs"))).
When singular is empty it is derived from plural via inflection; when plural is empty it is derived from singular. English inflection is heuristic and best-effort.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Pluralize(1, "item", "items"))
fmt.Println(filter.Pluralize(5, "item", "items"))
}
Output: item items
func Random ¶
Random returns one element chosen uniformly at random from input. Empty input returns *Error{Kind: KindNotFound}.
Random uses math/rand/v2's package-level generator. Call RandomWithRand when tests or callers need reproducible output.
func RandomWithRand ¶ added in v0.5.0
RandomWithRand returns one element chosen by r. Passing nil returns *Error{Kind: KindInvalidInput}.
func RejectFunc ¶ added in v0.6.3
RejectFunc returns items whose property at key does not satisfy predicate.
func RemoveFirst ¶ added in v0.4.15
RemoveFirst removes the first occurrence of a substring.
func RemoveLast ¶ added in v0.4.15
RemoveLast removes the last occurrence of a substring.
func ReplaceFirst ¶ added in v0.4.15
ReplaceFirst replaces the first occurrence of old with replacement.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.ReplaceFirst("hello hello hello", "hello", "hi"))
}
Output: hi hello hello
func ReplaceLast ¶ added in v0.4.15
ReplaceLast replaces the last occurrence of old with replacement.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.ReplaceLast("hello hello hello", "hello", "hi"))
}
Output: hello hello hi
func Round ¶
Round rounds input to the given number of decimal places.
decimals accepts any numeric type (int, float, or numeric string) so callers from template runtimes do not need to coerce first.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result, _ := filter.Round(3.14159, 2)
fmt.Println(result)
}
Output: 3.14
func SeededRand ¶ added in v0.5.0
SeededRand returns a deterministic *rand.Rand for callers that need reproducible RandomWithRand or ShuffleWithRand output.
r := filter.SeededRand(1, 2)
got, _ := filter.ShuffleWithRand(r, []int{1, 2, 3, 4})
// got is the same on every run.
The returned *rand.Rand follows math/rand/v2's usual rule: it is intended for one goroutine at a time. Use the package-level Random and Shuffle helpers when callers only need non-deterministic, concurrent-safe behavior.
func Shuffle ¶
Shuffle returns a new slice with elements rearranged in random order. Shuffle uses math/rand/v2's package-level generator. Call ShuffleWithRand when tests or callers need reproducible output.
func ShuffleWithRand ¶ added in v0.5.0
ShuffleWithRand returns a new slice with elements rearranged by r. Passing nil returns *Error{Kind: KindInvalidInput}.
func Size ¶
Size returns the length of a collection (slice, array, or map). Strings return their UTF-8 rune count, matching Length.
func Slice ¶ added in v0.4.15
Slice extracts a substring or sub-slice. For strings the offset is in runes; for slices and arrays it is in elements. Negative offsets count from the end. length defaults to 1; out-of-range returns an empty result.
Inputs that are neither string nor slice/array return *Error{Kind: KindInvalidInput}.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
str, _ := filter.Slice("hello", 1, 3)
fmt.Println(str)
arr, _ := filter.Slice([]any{1, 2, 3, 4}, 1, 2)
fmt.Println(arr)
}
Output: ell [2 3]
func Slugify ¶
Slugify converts input to a URL-friendly slug.
Note: Slugify is intentionally opinionated: transliteration and word boundary choices are policy, not universal truth.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result := filter.Slugify("Hello World!")
fmt.Println(result)
}
Output: hello-world
func Sort ¶ added in v0.4.15
Sort sorts the slice in ascending order. If key is provided, items are sorted by that property. Numeric values use numeric comparison; otherwise values are compared as strings.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result, _ := filter.Sort([]any{"banana", "apple", "cherry"})
fmt.Println(result)
}
Output: [apple banana cherry]
func SortNatural ¶ added in v0.4.15
SortNatural sorts case-insensitively. If key is provided, sorts by that property case-insensitively.
func StripHTML ¶ added in v0.4.15
StripHTML removes tags, script blocks, style blocks, and comments from input on a best-effort basis.
This is not a sanitizer and not a security boundary. For untrusted HTML, use a real parser-based sanitizer (e.g. bluemonday). Provided here because it preserves the long-standing behavior of this package's StripHTML helper.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.StripHTML("<p>Hello <b>World</b></p>"))
}
Output: Hello World
func StripNewlines ¶ added in v0.4.15
StripNewlines removes newline characters from input.
func SumBy ¶ added in v0.5.0
SumBy returns the sum of numeric values extracted at key from every element. Missing keys and non-numeric extracted values return errors.
func TimeAgo ¶
TimeAgo returns the difference between input and the current wall time in human-readable form ("3 hours ago", "in 5 minutes").
func TimeAgoWithClock ¶ added in v0.5.0
TimeAgoWithClock returns the difference between input and clock.Now() in human-readable form. Tests should pass FixedClock for deterministic output.
func Trim ¶
Trim strips leading and trailing whitespace from a string.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result := filter.Trim(" hello ")
fmt.Println(result)
}
Output: hello
func TrimLeft ¶ added in v0.4.15
TrimLeft removes leading whitespace from input.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.TrimLeft(" hello "))
}
Output: hello
func TrimRight ¶ added in v0.4.15
TrimRight removes trailing whitespace from input.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.TrimRight(" hello "))
}
Output: hello
func Truncate ¶
Truncate shortens input to maxLength runes. Default ellipsis is "...".
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.Truncate("Hello, World!", 8))
fmt.Println(filter.Truncate("Hi", 5))
fmt.Println(filter.Truncate("Hello, World!", 10, "--"))
}
Output: Hello... Hi Hello, W--
func TruncateWords ¶
TruncateWords shortens input to maxWords words. Default ellipsis is "...".
func URLDecode ¶ added in v0.4.15
URLDecode decodes a percent-encoded string. Parse failure returns *Error{Kind: KindFormat}.
func URLEncode ¶ added in v0.4.15
URLEncode percent-encodes a string for use in a URL. Equivalent to url.QueryEscape.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
fmt.Println(filter.URLEncode("hello world"))
}
Output: hello+world
func Unique ¶
Unique removes duplicate elements while preserving first-seen order.
All elements must be Comparable. Non-comparable elements (slices, maps, functions, structs containing them) return *Error{Kind: KindInvalidInput} — use UniqueBy when records should be deduplicated by a property.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
result, _ := filter.Unique([]any{1, 2, 2, 3, 3, 3})
fmt.Println(result)
}
Output: [1 2 3]
func UniqueBy ¶ added in v0.5.0
UniqueBy removes duplicate elements by the value at key, preserving first-seen order. Missing or unreachable keys return an error.
func Where ¶ added in v0.4.15
Where returns items whose property at key equals value.
Example ¶
package main
import (
"fmt"
"github.com/kaptinlin/filter"
)
func main() {
products := []any{
map[string]any{"name": "Shoes", "available": true},
map[string]any{"name": "Shirt", "available": false},
map[string]any{"name": "Pants", "available": true},
}
result, _ := filter.Where(products, "available", true)
for _, p := range result {
m := p.(map[string]any)
fmt.Println(m["name"])
}
}
Output: Shoes Pants
Types ¶
type Clock ¶ added in v0.5.0
Clock is the source of "now" for filters that depend on the current time.
Production code should use SystemClock{}; tests should inject a FixedClock to keep results deterministic.
type Error ¶ added in v0.5.0
type Error struct {
// contains filtered or unexported fields
}
Error is the only error type this package returns.
Callers use errors.As to extract it and switch on Kind. The four package-level sentinels (ErrInvalidInput, ErrNotFound, ErrArithmetic, ErrFormat) participate in errors.Is by Kind only — Op, Path, and Cause are diagnostic context, not part of identity.
func (*Error) Error ¶ added in v0.5.0
Error renders a stable message: "filter.<Op>: <kind>[: <path>][: <cause>]".
type ErrorKind ¶ added in v0.5.0
type ErrorKind uint8
ErrorKind classifies failures returned by this package and its subpackages.
Kinds are deliberately coarse: four buckets cover every failure mode a pure value-transformation library can produce. Callers branch on Kind to map filter errors onto their own exit codes / HTTP status / log levels without maintaining a sentinel translation table.
const ( // KindInvalidInput is returned when the caller passed a value of the // wrong type, the wrong shape, or out of range. KindInvalidInput ErrorKind = iota + 1 // KindNotFound is returned when a path, key, or index does not exist // in the input. KindNotFound // KindArithmetic is returned for division by zero, modulus by zero, // and similar numeric-domain failures. KindArithmetic // KindFormat is returned when parsing fails (date layout, base64, // URL escape, etc.). KindFormat )
type FixedClock ¶ added in v0.5.0
FixedClock always returns the same instant. Use it in tests.