Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 29 additions & 3 deletions duration.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
// Package duration allows duration parsing with "d" for days (24 hours) and "w" for week (7 days).
// For output direction, it implements a more human friendly formatting of durations removing
// all 0 values and using days and weeks when applicable.
// Includes helpers for command line flag duration parsing and for date/time parsing replacement.
// See also [fortio.org/dflag] if you need dynamic flags with days/weeks support.
package duration

import (
Expand Down Expand Up @@ -95,6 +99,11 @@ func Parse(s string) (time.Duration, error) {
return d, nil
}

// Duration is like [time.Duration] but supports days (24h) and weeks (7d) in addition to the
// units supported by [time.Duration]. This is mostly for internal use for the [Flag] function
// which still returns a standard duration for compatibility. Or if you want the more compact
// formatting with days and weeks and no zeroes in [Duration.String].
//
//nolint:recvcheck // need pointer receiver obviously for Set and for String avoids pointer.
type Duration time.Duration

Expand Down Expand Up @@ -185,11 +194,28 @@ func (d *Duration) Set(s string) error {

// Flag defines a duration flag with the specified name, default value, and usage string, like
// [flag.Duration] but supporting durations in days (24 hours) and weeks (7 days)
// in addition to the other stdlib units.
// in addition to the other stdlib units. Replacement for [flag.Duration].
func Flag(name string, value time.Duration, usage string) *time.Duration {
return FlagSet(flag.CommandLine, name, value, usage)
}

// FlagSet is like [Flag] but for the specified flag set. Replacement for [flag.FlagSet.Duration].
func FlagSet(fs *flag.FlagSet, name string, value time.Duration, usage string) *time.Duration {
p := new(time.Duration)
FlagSetVar(fs, p, name, value, usage)
return p
}

// FlagVar is like [Flag] but binds the value to variable referenced. Replacement for [flag.DurationVar].
func FlagVar(p *time.Duration, name string, value time.Duration, usage string) {
FlagSetVar(flag.CommandLine, p, name, value, usage)
}

// FlagSet is like [FlagVar] but for the specified flag set. Replacement for [flag.FlagSet.DurationVar].
func FlagSetVar(fs *flag.FlagSet, p *time.Duration, name string, value time.Duration, usage string) {
d := Duration(value)
flag.Var(&d, name, usage)
return (*time.Duration)(&d)
fs.Var((*Duration)(p), name, usage)
*p = time.Duration(d)
}

// NextTime takes a partially parsed time.Time (without date) and returns the time in the future
Expand Down
28 changes: 27 additions & 1 deletion duration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ func TestFlag(t *testing.T) {
}
}

func TestFlagVar(t *testing.T) {
defaultValue, _ := duration.Parse("1d3m")
var d time.Duration
duration.FlagVar(&d, "test2", defaultValue, "test `duration`")
flag.Lookup("test2").Value.Set("1d1h")
if d != 25*time.Hour {
t.Errorf("Expected 25h but got %v", d)
}
}

func TestParseDateTime(t *testing.T) {
l, err := time.LoadLocation("America/New_York")
if err != nil {
Expand Down Expand Up @@ -147,7 +157,7 @@ func TestParseDateTimeIsLocal(t *testing.T) {
}
}

func Example() {
func ExampleParse() {
d, err := duration.Parse("1w 2d 3h 4m")
if err != nil {
fmt.Println("Error:", err)
Expand All @@ -159,3 +169,19 @@ func Example() {
// Parsed duration (std): 219h4m0s
// Parsed duration (new): 1w2d3h4m
}

func ExampleFlag() {
f := duration.Flag("duration-flag", 1*duration.Day, "test `duration`")
// and then use *f normally and the users can use days, weeks etc.

// to test/show:
flv := flag.Lookup("duration-flag").Value
fmt.Println("Default:", flv.String())
flv.Set("1d1h") // no error
fmt.Println("After set:", flv.String())
fmt.Println("After set (dereferenced):", *f)
// Output:
// Default: 1d
// After set: 1d1h
// After set (dereferenced): 25h0m0s
}
Loading