jsonduration

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 6, 2026 License: BSD-3-Clause Imports: 11 Imported by: 0

README

jsonduration

Go Reference CI

JSON for time.Duration in Go's encoding/json/v2, in the format you choose.

encoding/json/v2 has no default representation for time.Duration (golang/go#71631): marshaling one fails with an error. The format struct tag that used to pick one was removed in Go 1.27 (golang/go#79071). This package offers two ways to choose a format instead:

  • One option per call that covers every time.Duration in it, so your types stay as they are.
  • One wrapper type per format, to choose per field without any options.

It is a small package for encoding/json/v2 only. encoding/json (v1), other JSON libraries, and YAML or TOML are out of scope.

go get github.com/pscheid92/jsonduration

Requires Go 1.27 or later. No dependencies outside the standard library. Until v1.0.0 the API may still change.

One format for a whole call

import (
	"encoding/json/v2"
	"time"

	"github.com/pscheid92/jsonduration"
)

type Config struct {
	Timeout time.Duration   `json:"timeout"`
	Retry   []time.Duration `json:"retry"`
}

opts := jsonduration.With(jsonduration.ISO8601)

b, err := json.Marshal(Config{Timeout: 90 * time.Second}, opts)
// {"timeout":"PT1M30S","retry":[]}

var cfg Config
err = json.Unmarshal([]byte(`{"timeout":"PT2M"}`), &cfg, opts)

The format applies to struct fields, pointers, slices, arrays, map keys and values. The options are safe for concurrent use, so create them once and share them.

One format per field

type Config struct {
	Timeout jsonduration.ISO8601Duration `json:"timeout"`
	Backoff jsonduration.SecDuration     `json:"backoff"`
}

b, err := json.Marshal(Config{
	Timeout: jsonduration.ISO8601Duration{Duration: 90 * time.Second},
	Backoff: jsonduration.SecDuration{Duration: 1500 * time.Millisecond},
})
// {"timeout":"PT1M30S","backoff":1.5}

var cfg Config
err = json.Unmarshal(b, &cfg)
cfg.Timeout.Minutes()          // the methods of time.Duration still work
time.Sleep(cfg.Backoff.Duration)

Wrapper types embed time.Duration and implement json.MarshalerTo and json.UnmarshalerFrom. With does not change their format.

Formats

Format Name Wrapper type JSON type 1h30m0.5s
Units units UnitsDuration string "1h30m0.5s"
Sec sec SecDuration number 5400.5
Milli milli MilliDuration number 5400500
Micro micro MicroDuration number 5400500000
Nano nano NanoDuration number 5400500000000
ISO8601 iso8601 ISO8601Duration string "PT1H30M0.5S"
Base60 base60 Base60Duration string "1:30:00.5"
ProtoJSON protojson ProtoJSONDuration string "5400.500s"
  • The first six keep the names and the exact output of the formats encoding/json/v2 offered through format before Go 1.27, so they can be carried over without changing any JSON.
  • Nano is byte-for-byte what encoding/json (v1) writes.
  • Sec, Milli and Micro are exact decimal numbers. They never go through float64.
  • ProtoJSON is the protobuf JSON mapping of google.protobuf.Duration.
  • Numeric formats become JSON strings where JSON needs one (object names) and when json.StringifyNumbers is set.

The codecs are also available without JSON:

jsonduration.ProtoJSON.Format(1500 * time.Millisecond) // "1.500s"
d, err := jsonduration.Base60.Parse("1:30:00.5")         // 1h30m0.5s

A Format can be chosen at run time. It implements MarshalText and UnmarshalText, so a format in a JSON configuration is checked while it is decoded, and it works with flag.TextVar. The names in the table must match exactly (iso8601, not ISO8601), and anything else is rejected with an error that lists them:

var format jsonduration.Format
flag.TextVar(&format, "duration-format", jsonduration.ISO8601, "how durations are written")
flag.Parse()
opts := jsonduration.With(format)

A Format converted from a plain string is not checked, and With panics on an invalid one. Check it with Valid first:

format := jsonduration.Format(os.Getenv("DURATION_FORMAT"))
if !format.Valid() {
	return fmt.Errorf("unknown duration format %q", format)
}

Migrating between formats

Write one format, but read others too:

// Stored data holds encoding/json (v1) nanoseconds; new data is ISO 8601.
opts := jsonduration.With(jsonduration.ISO8601, jsonduration.AlsoAccept(jsonduration.Nano))

{"timeout":90000000000} and {"timeout":"PT1M30S"} both decode to 1m30s, and everything is written as ISO 8601. Old and new data can even be mixed in one document.

At most one numeric format can be involved, because a number such as 90 could be seconds or nanoseconds. With panics if you name two. String formats never conflict: no text is valid in two of them with different values. Where two accept the same text, such as "1.5s" in Units and ProtoJSON, they agree on it. A fuzz test checks exactly this.

Decoding is strict

  • Only the configured formats are accepted. There is no guessing.
  • ISO8601 rejects years, months, weeks and days with ErrNominalUnits, since they have no fixed length.
  • Where a producer means exactly 24 hours by a day, as Java's Duration.parse does, the AcceptDays() option to With reads "P1DT12H" as 36 hours and a week as 7 days. Years and months are still rejected, output never uses days, and Parse and the wrapper types stay strict.
  • Digits below one nanosecond are truncated toward zero, computed exactly in every format. Units accepts exactly what time.ParseDuration accepts (a fuzz test compares the two), without going through float64.
  • Errors wrap strconv.ErrSyntax, strconv.ErrRange or ErrNominalUnits.
  • null sets a duration to zero, as encoding/json/v2 does for other types.

Good to know

  • Named types such as type TTL time.Duration are different types and are not affected by With.
  • A wrapper type reads and writes exactly its own format. To accept several formats on input, use With with AlsoAccept.
  • Use omitzero, not omitempty, to leave out zero durations. In v2, omitempty only omits values that encode as empty JSON.
  • If you pass your own json.WithMarshalers, combine it with jsonduration.Marshalers through json.JoinMarshalers (the same goes for unmarshalers). A second WithMarshalers option replaces the first one.
  • The string tag option quotes numeric formats, like json.StringifyNumbers, and has no effect on string formats.

Alternatives

  • The json.FormatDurationAsNano option from encoding/json, passed to v2: nanoseconds only, as encoding/json (v1) writes them.
  • github.com/go-json-experiment/json with ExperimentalSupportFormatTag: the old format struct tag, as an option that is explicitly temporary.
  • tailscale.com/types/jsonformat: wrapper types for nanoseconds, Go strings and ISO 8601. It is part of the tailscale.com module and says it may be deleted once Go supports formats directly.
  • github.com/MarkRosemaker/jsonutil: marshal functions for many types, among them durations as whole seconds or Go strings.

This package differs in offering both ways to choose (per call and per field), all eight formats, exact decoding, and migration between formats, with only the standard library as a dependency.

Future

Go may add its own way to choose a format for a type: see golang/go#71664 and golang/go#81013, both still open proposals. If that lands, prefer it. This package will then point to it in a final release. The format names here are the ones Go used, so the move is meant to be little more than a rename.

Development

Needs just and golangci-lint.

just check         # gofmt, vet, golangci-lint, tests with -race, govulncheck
just fuzz          # every fuzz target for 30s; `just fuzz 2m FuzzParse` for one
just bench
just generate      # after changing gen_wrapper_types.go

The wrapper types in wrapper_types.go are generated from the table in gen_wrapper_types.go. CI fails if the two are out of sync.

CI runs the same checks on Linux, macOS and Windows, fuzzes every push for a short time and for longer once a week, and runs govulncheck weekly. Renovate keeps the pinned GitHub Actions up to date. Pushing a tag such as v0.1.0 creates a GitHub release and announces the version to the Go module proxy, so pkg.go.dev picks it up.

License

BSD 3-Clause, see LICENSE. Parts of the codecs are adapted from the Go standard library, whose notice is included there.

Documentation

Overview

Package jsonduration gives time.Duration a JSON representation in encoding/json/v2.

encoding/json/v2 has no default representation for time.Duration (https://go.dev/issue/71631): marshaling or unmarshaling one returns an error. The `format` struct tag that used to select one was removed in Go 1.27 (https://go.dev/issue/79071), and its replacement depends on typed struct tags (https://go.dev/issue/74472). This package fills the gap in two ways. It is for encoding/json/v2 only.

One format for a whole call

With returns options that apply one format to every time.Duration in a call: struct fields, pointers, slices, arrays, map keys and values, and values held in an any when marshaling. Your types stay unchanged.

opts := jsonduration.With(jsonduration.ISO8601)
b, err := json.Marshal(v, opts)  // {"ttl":"PT1H30M"}
err = json.Unmarshal(b, &v, opts)

One format per field

Each format has a wrapper type, such as ISO8601Duration or SecDuration, that embeds a time.Duration and encodes itself through the encoding/json/v2 interfaces MarshalerTo and UnmarshalerFrom. Wrapper types need no options, and With does not change their format.

type Config struct {
	Timeout jsonduration.ISO8601Duration `json:"timeout"`
	Backoff jsonduration.SecDuration     `json:"backoff"`
}

Formats

The six formats encoding/json/v2 offered through `format` are supported with the same names and identical output: Units, Sec, Milli, Micro, Nano and ISO8601. Nano is byte-for-byte compatible with encoding/json (v1). Two more are supported: Base60 from the original v2 proposal, and ProtoJSON, the protobuf JSON mapping of google.protobuf.Duration.

A Format can come from a JSON configuration or from flags: it implements encoding.TextUnmarshaler and rejects anything but the exact names. Check a Format converted from a plain string with Format.Valid.

Strictness

Decoding accepts only the configured format. To read data written in other formats as well, for example while migrating, name them with AlsoAccept. ISO8601 input with nominal units (years, months, weeks, days) is rejected with ErrNominalUnits, since those have no fixed length; where a producer means 24 hours by a day, AcceptDays reads days and weeks. Fractional digits below one nanosecond are truncated toward zero, exactly, in every format. Errors wrap strconv.ErrSyntax, strconv.ErrRange or ErrNominalUnits.

JSON null sets a duration to zero, as encoding/json/v2 does for other types.

Good to know

Named types such as `type TTL time.Duration` are distinct types and are not affected by With. To omit zero durations, use the `omitzero` tag option: in v2, `omitempty` only omits values that encode as empty JSON. The `string` tag option quotes numeric formats, like json.StringifyNumbers, and has no effect on string formats.

Example
package main

import (
	"encoding/json/v2"
	"fmt"
	"time"

	"github.com/pscheid92/jsonduration"
)

func main() {
	type Config struct {
		Timeout time.Duration   `json:"timeout"`
		Retry   []time.Duration `json:"retry"`
		Grace   *time.Duration  `json:"grace,omitzero"`
	}
	opts := jsonduration.With(jsonduration.ISO8601)

	b, err := json.Marshal(Config{
		Timeout: 90 * time.Second,
		Retry:   []time.Duration{time.Second, 2500 * time.Millisecond},
	}, opts)
	fmt.Println(string(b), err)

	var c Config
	err = json.Unmarshal([]byte(`{"timeout":"PT2M","retry":["PT0.5S"]}`), &c, opts)
	fmt.Println(c.Timeout, c.Retry, err)

}
Output:
{"timeout":"PT1M30S","retry":["PT1S","PT2.5S"]} <nil>
2m0s [500ms] <nil>

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrNominalUnits = errors.New("nominal year, month, week, or day units")

ErrNominalUnits reports an ISO 8601 duration that uses the year, month, week or day designators. Their length in time is not fixed, so they cannot be converted to a time.Duration exactly. AcceptDays allows days and weeks by fixing their length.

Functions

func Marshalers

func Marshalers(f Format) *json.Marshalers

Marshalers returns the marshaler for time.Duration in format f. It panics if f is not valid.

Example

Combine the duration marshaler with your own through json.JoinMarshalers, because a second json.WithMarshalers option would replace the first.

package main

import (
	"encoding/json/jsontext"
	"encoding/json/v2"
	"fmt"
	"time"

	"github.com/pscheid92/jsonduration"
)

func main() {
	type UserID int
	ids := json.MarshalToFunc(func(enc *jsontext.Encoder, id UserID) error {
		return enc.WriteToken(jsontext.String(fmt.Sprintf("user-%d", id)))
	})
	opts := json.WithMarshalers(json.JoinMarshalers(ids, jsonduration.Marshalers(jsonduration.Units)))

	b, err := json.Marshal(struct {
		ID  UserID
		TTL time.Duration
	}{42, 90 * time.Minute}, opts)
	fmt.Println(string(b), err)

}
Output:
{"ID":"user-42","TTL":"1h30m0s"} <nil>

func Unmarshalers

func Unmarshalers(f Format, opts ...DecodeOption) *json.Unmarshalers

Unmarshalers returns the unmarshaler for time.Duration in format f and in any format added with AlsoAccept. It panics if a format is not valid or if two numeric formats are involved.

Decoding is strict: only the accepted formats are tried, and a numeric format accepts a JSON string only where it would also write one (object names, or with json.StringifyNumbers). JSON null sets the duration to zero, as encoding/json/v2 does for other types.

func With

func With(f Format, opts ...DecodeOption) json.Options

With returns options for encoding/json/v2 that marshal and unmarshal every time.Duration using format f: struct fields, pointers, slice and array elements, map keys and values, and values held in an any when marshaling. Decoding accepts only f, unless AlsoAccept adds formats. With panics under the same conditions as Unmarshalers.

The options are safe for concurrent use, so they can be created once, for example in a package-level variable, and shared:

var jsonOpts = jsonduration.With(jsonduration.ISO8601)

b, err := json.Marshal(v, jsonOpts)
err = json.Unmarshal(b, &v, jsonOpts)

If you also pass your own json.WithMarshalers or json.WithUnmarshalers, combine them with Marshalers and Unmarshalers through json.JoinMarshalers and json.JoinUnmarshalers instead, because a later WithMarshalers option replaces an earlier one.

Types

type Base60Duration

type Base60Duration struct{ time.Duration }

Base60Duration is a time.Duration that encodes itself in format Base60 in encoding/json/v2, without any options: as a JSON string such as "1:30:00.5" for 1h30m0.5s.

func (Base60Duration) MarshalJSONTo

func (d Base60Duration) MarshalJSONTo(enc *jsontext.Encoder) error

MarshalJSONTo implements encoding/json/v2.MarshalerTo.

func (*Base60Duration) UnmarshalJSONFrom

func (d *Base60Duration) UnmarshalJSONFrom(dec *jsontext.Decoder) error

UnmarshalJSONFrom implements encoding/json/v2.UnmarshalerFrom.

type DecodeOption

type DecodeOption interface {
	// contains filtered or unexported methods
}

A DecodeOption changes how With and Unmarshalers decode. Encoding always uses the single format passed to them.

func AcceptDays

func AcceptDays() DecodeOption

AcceptDays makes ISO8601 decoding through With and Unmarshalers read days as exactly 24 hours and weeks as exactly 7 days, as Java's Duration.parse does for days, so that "P1D" is 24h and "P1DT12H" is 36h. Years and months are still rejected with ErrNominalUnits, and days and weeks take no fraction. Encoding never writes days. AcceptDays has no effect on the other formats, and Format.Parse and ISO8601Duration stay strict.

Only use it where the producer means 24 hours by a day: a calendar day can be 23 or 25 hours long across a daylight saving change.

Example

Read days from a service that means 24 hours by one, as Java's Duration.parse does.

package main

import (
	"encoding/json/v2"
	"errors"
	"fmt"
	"time"

	"github.com/pscheid92/jsonduration"
)

func main() {
	opts := jsonduration.With(jsonduration.ISO8601, jsonduration.AcceptDays())

	var d time.Duration
	err := json.Unmarshal([]byte(`"P1DT12H"`), &d, opts)
	fmt.Println(d, err)

	// Years and months still have no fixed length.
	err = json.Unmarshal([]byte(`"P1M"`), &d, opts)
	fmt.Println(errors.Is(err, jsonduration.ErrNominalUnits))

}
Output:
36h0m0s <nil>
true

func AlsoAccept

func AlsoAccept(formats ...Format) DecodeOption

AlsoAccept makes decoding accept the given formats in addition to the one that is written. Use it to read data written in an older format while writing a new one:

// Stored data holds v1 nanoseconds; new data is written as ISO 8601.
opts := jsonduration.With(jsonduration.ISO8601, jsonduration.AlsoAccept(jsonduration.Nano))

String formats never conflict: no text is valid in two of them with different values. Where two accept the same text, such as "1.5s" in Units and ProtoJSON, they agree on its value. Numeric formats do conflict, since a JSON number such as 90 could be seconds or nanoseconds, so at most one numeric format may be involved. With and Unmarshalers panic if there are two, or if a format is not valid.

Example

Read data written by encoding/json (v1), which stores nanoseconds, while writing ISO 8601 from now on.

package main

import (
	"encoding/json/v2"
	"fmt"
	"time"

	"github.com/pscheid92/jsonduration"
)

func main() {
	type Job struct {
		Timeout time.Duration `json:"timeout"`
	}
	opts := jsonduration.With(jsonduration.ISO8601, jsonduration.AlsoAccept(jsonduration.Nano))

	for _, stored := range []string{`{"timeout":90000000000}`, `{"timeout":"PT1M30S"}`} {
		var j Job
		err := json.Unmarshal([]byte(stored), &j, opts)
		fmt.Println(j.Timeout, err)
	}

	b, err := json.Marshal(Job{Timeout: 90 * time.Second}, opts)
	fmt.Println(string(b), err)

}
Output:
1m30s <nil>
1m30s <nil>
{"timeout":"PT1M30S"} <nil>

type Format

type Format string

Format names a JSON representation of a time.Duration. Its value is the name of the format, such as "iso8601", and names match exactly, case included, as they did in the `format` struct tag option of encoding/json/v2.

A Format converted from a string, as in Format(s), is not checked. Check such a value with Format.Valid before passing it to With, which panics on an invalid format. Format.UnmarshalText checks for itself.

const (
	// Units is a JSON string produced by [time.Duration.String], e.g.
	// "1h30m0.5s". Like the standard library, durations under one
	// millisecond use "µs". Input is what [time.ParseDuration] accepts,
	// such as "1h30m" or "1.5us", but fractions are computed exactly
	// instead of through float64, and errors wrap [strconv.ErrSyntax] or
	// [strconv.ErrRange] like those of the other formats.
	Units Format = "units"

	// Sec is a JSON number of seconds with up to 9 fractional digits,
	// e.g. 5400.5. It is exact: the value is never routed through float64.
	Sec Format = "sec"

	// Milli is a JSON number of milliseconds, e.g. 5400500.
	Milli Format = "milli"

	// Micro is a JSON number of microseconds, e.g. 5400500000.
	Micro Format = "micro"

	// Nano is a JSON integer of nanoseconds, e.g. 5400500000000.
	// This is the representation of encoding/json (v1).
	Nano Format = "nano"

	// ISO8601 is a JSON string with the restricted ISO 8601 profile of
	// encoding/json/v2, e.g. "PT1H30M0.5S". Output only uses the accurate
	// hour, minute and second designators. Input follows the grammar of
	// JavaScript's Temporal.Duration; unlike the standard library, input
	// with the nominal designators year, month, week or day is rejected,
	// unless [AcceptDays] allows days and weeks.
	ISO8601 Format = "iso8601"

	// Base60 is a JSON string in H:MM:SS.SSSSSSSSS notation, e.g. "1:30:00.5".
	// It was part of the original encoding/json/v2 proposal and was removed
	// after the vote in https://go.dev/issue/71631.
	Base60 Format = "base60"

	// ProtoJSON is the JSON string of google.protobuf.Duration:
	// seconds followed by "s", e.g. "5400.500s". Output has 0, 3, 6 or 9
	// fractional digits as the protobuf JSON mapping requires; input
	// accepts 0 to 9 fractional digits.
	ProtoJSON Format = "protojson"
)

func Formats

func Formats() []Format

Formats returns every supported format, in a new slice on each call.

func (Format) Append

func (f Format) Append(b []byte, d time.Duration) []byte

Append appends the representation of d in format f to b, without JSON quoting. It panics if f is not valid.

func (Format) Format

func (f Format) Format(d time.Duration) string

Format returns the representation of d in format f, without JSON quoting. It panics if f is not valid.

Example
package main

import (
	"fmt"
	"time"

	"github.com/pscheid92/jsonduration"
)

func main() {
	d := 90*time.Minute + 500*time.Millisecond
	for _, f := range jsonduration.Formats() {
		fmt.Printf("%-9s %s\n", f, f.Format(d))
	}
}
Output:
units     1h30m0.5s
sec       5400.5
milli     5400500
micro     5400500000
nano      5400500000000
iso8601   PT1H30M0.5S
base60    1:30:00.5
protojson 5400.500s

func (Format) IsNumeric

func (f Format) IsNumeric() bool

IsNumeric reports whether f encodes as a JSON number. Numeric formats are written as JSON strings when used as an object name or when json.StringifyNumbers is set.

Example

Numeric formats are written as JSON strings where JSON requires one, such as object names, and wherever json.StringifyNumbers asks for it.

package main

import (
	"encoding/json/v2"
	"fmt"
	"time"

	"github.com/pscheid92/jsonduration"
)

func main() {
	m := map[time.Duration]time.Duration{time.Minute: time.Hour}

	b, _ := json.Marshal(m, jsonduration.With(jsonduration.Sec))
	fmt.Println(string(b))

	b, _ = json.Marshal(m, jsonduration.With(jsonduration.Sec), json.StringifyNumbers(true))
	fmt.Println(string(b))

}
Output:
{"60":3600}
{"60":"3600"}

func (Format) MarshalText

func (f Format) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler, so that a format can be written to configuration. It returns an error if f is not valid.

func (Format) Parse

func (f Format) Parse(s string) (time.Duration, error)

Parse parses s, which must be in format f and not JSON quoted. Only format f is accepted; there is no fallback to other formats. Fractional digits below one nanosecond are truncated toward zero. Errors wrap strconv.ErrSyntax, strconv.ErrRange or ErrNominalUnits.

Example
package main

import (
	"errors"
	"fmt"

	"github.com/pscheid92/jsonduration"
)

func main() {
	d, err := jsonduration.ISO8601.Parse("PT1H30M")
	fmt.Println(d, err)

	// Days have no fixed length, so they are rejected.
	_, err = jsonduration.ISO8601.Parse("P1D")
	fmt.Println(errors.Is(err, jsonduration.ErrNominalUnits))

	// Only the configured format is accepted.
	_, err = jsonduration.ISO8601.Parse("1h30m")
	fmt.Println(err)

}
Output:
1h30m0s <nil>
true
invalid ISO 8601 duration "1h30m": invalid syntax

func (*Format) UnmarshalText

func (f *Format) UnmarshalText(b []byte) error

UnmarshalText implements encoding.TextUnmarshaler, so that a format in a JSON configuration is checked while encoding/json/v2 decodes it, and so that a format can come from a flag (see flag.TextVar). It accepts exactly the names of the formats, such as "iso8601", and rejects anything else, including other spellings such as "ISO8601", with an error that lists the names.

Example

Choose the format at run time, from a flag. The same works for a format in a JSON configuration. Names must match exactly, and anything else is rejected right away.

package main

import (
	"encoding/json/v2"
	"flag"
	"fmt"
	"io"
	"time"

	"github.com/pscheid92/jsonduration"
)

func main() {
	fs := flag.NewFlagSet("server", flag.ContinueOnError)
	fs.SetOutput(io.Discard) // keep the usage message out of the example
	var format jsonduration.Format
	fs.TextVar(&format, "duration-format", jsonduration.ISO8601, "how durations are written")

	_ = fs.Parse([]string{"-duration-format=sec"})
	b, err := json.Marshal(90*time.Second, jsonduration.With(format))
	fmt.Println(string(b), err)

	fmt.Println(fs.Parse([]string{"-duration-format=weeks"}) != nil)

}
Output:
90 <nil>
true

func (Format) Valid

func (f Format) Valid() bool

Valid reports whether f is exactly one of the supported formats, so that, for example, "ISO8601" is not valid. Use it to check a Format converted from a string before passing it to With.

type ISO8601Duration

type ISO8601Duration struct{ time.Duration }

ISO8601Duration is a time.Duration that encodes itself in format ISO8601 in encoding/json/v2, without any options: as a JSON string such as "PT1H30M0.5S" for 1h30m0.5s.

Example

Wrapper types choose the format per field and need no options. The methods of time.Duration stay available.

package main

import (
	"encoding/json/v2"
	"fmt"
	"time"

	"github.com/pscheid92/jsonduration"
)

func main() {
	type Config struct {
		Timeout jsonduration.ISO8601Duration `json:"timeout"`
		Backoff jsonduration.SecDuration     `json:"backoff"`
	}
	b, err := json.Marshal(Config{
		Timeout: jsonduration.ISO8601Duration{Duration: 90 * time.Second},
		Backoff: jsonduration.SecDuration{Duration: 1500 * time.Millisecond},
	})
	fmt.Println(string(b), err)

	var c Config
	err = json.Unmarshal([]byte(`{"timeout":"PT2M","backoff":0.25}`), &c)
	fmt.Println(c.Timeout.Minutes(), c.Backoff, err)

}
Output:
{"timeout":"PT1M30S","backoff":1.5} <nil>
2 250ms <nil>

func (ISO8601Duration) MarshalJSONTo

func (d ISO8601Duration) MarshalJSONTo(enc *jsontext.Encoder) error

MarshalJSONTo implements encoding/json/v2.MarshalerTo.

func (*ISO8601Duration) UnmarshalJSONFrom

func (d *ISO8601Duration) UnmarshalJSONFrom(dec *jsontext.Decoder) error

UnmarshalJSONFrom implements encoding/json/v2.UnmarshalerFrom.

type MicroDuration

type MicroDuration struct{ time.Duration }

MicroDuration is a time.Duration that encodes itself in format Micro in encoding/json/v2, without any options: as a JSON number such as 5400500000 for 1h30m0.5s.

func (MicroDuration) MarshalJSONTo

func (d MicroDuration) MarshalJSONTo(enc *jsontext.Encoder) error

MarshalJSONTo implements encoding/json/v2.MarshalerTo.

func (*MicroDuration) UnmarshalJSONFrom

func (d *MicroDuration) UnmarshalJSONFrom(dec *jsontext.Decoder) error

UnmarshalJSONFrom implements encoding/json/v2.UnmarshalerFrom.

type MilliDuration

type MilliDuration struct{ time.Duration }

MilliDuration is a time.Duration that encodes itself in format Milli in encoding/json/v2, without any options: as a JSON number such as 5400500 for 1h30m0.5s.

func (MilliDuration) MarshalJSONTo

func (d MilliDuration) MarshalJSONTo(enc *jsontext.Encoder) error

MarshalJSONTo implements encoding/json/v2.MarshalerTo.

func (*MilliDuration) UnmarshalJSONFrom

func (d *MilliDuration) UnmarshalJSONFrom(dec *jsontext.Decoder) error

UnmarshalJSONFrom implements encoding/json/v2.UnmarshalerFrom.

type NanoDuration

type NanoDuration struct{ time.Duration }

NanoDuration is a time.Duration that encodes itself in format Nano in encoding/json/v2, without any options: as a JSON number such as 5400500000000 for 1h30m0.5s.

func (NanoDuration) MarshalJSONTo

func (d NanoDuration) MarshalJSONTo(enc *jsontext.Encoder) error

MarshalJSONTo implements encoding/json/v2.MarshalerTo.

func (*NanoDuration) UnmarshalJSONFrom

func (d *NanoDuration) UnmarshalJSONFrom(dec *jsontext.Decoder) error

UnmarshalJSONFrom implements encoding/json/v2.UnmarshalerFrom.

type ProtoJSONDuration

type ProtoJSONDuration struct{ time.Duration }

ProtoJSONDuration is a time.Duration that encodes itself in format ProtoJSON in encoding/json/v2, without any options: as a JSON string such as "5400.500s" for 1h30m0.5s.

func (ProtoJSONDuration) MarshalJSONTo

func (d ProtoJSONDuration) MarshalJSONTo(enc *jsontext.Encoder) error

MarshalJSONTo implements encoding/json/v2.MarshalerTo.

func (*ProtoJSONDuration) UnmarshalJSONFrom

func (d *ProtoJSONDuration) UnmarshalJSONFrom(dec *jsontext.Decoder) error

UnmarshalJSONFrom implements encoding/json/v2.UnmarshalerFrom.

type SecDuration

type SecDuration struct{ time.Duration }

SecDuration is a time.Duration that encodes itself in format Sec in encoding/json/v2, without any options: as a JSON number such as 5400.5 for 1h30m0.5s.

func (SecDuration) MarshalJSONTo

func (d SecDuration) MarshalJSONTo(enc *jsontext.Encoder) error

MarshalJSONTo implements encoding/json/v2.MarshalerTo.

func (*SecDuration) UnmarshalJSONFrom

func (d *SecDuration) UnmarshalJSONFrom(dec *jsontext.Decoder) error

UnmarshalJSONFrom implements encoding/json/v2.UnmarshalerFrom.

type UnitsDuration

type UnitsDuration struct{ time.Duration }

UnitsDuration is a time.Duration that encodes itself in format Units in encoding/json/v2, without any options: as a JSON string such as "1h30m0.5s" for 1h30m0.5s.

func (UnitsDuration) MarshalJSONTo

func (d UnitsDuration) MarshalJSONTo(enc *jsontext.Encoder) error

MarshalJSONTo implements encoding/json/v2.MarshalerTo.

func (*UnitsDuration) UnmarshalJSONFrom

func (d *UnitsDuration) UnmarshalJSONFrom(dec *jsontext.Decoder) error

UnmarshalJSONFrom implements encoding/json/v2.UnmarshalerFrom.

Jump to

Keyboard shortcuts

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