1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
package cli
import (
"errors"
"fmt"
"time"
)
type TimestampFlag = FlagBase[time.Time, TimestampConfig, timestampValue]
// TimestampConfig defines the config for timestamp flags
type TimestampConfig struct {
Timezone *time.Location
// Available layouts for flag value.
//
// Note that value for formats with missing year/date will be interpreted as current year/date respectively.
//
// Read more about time layouts: https://pkg.go.dev/time#pkg-constants
Layouts []string
}
// timestampValue wrap to satisfy golang's flag interface.
type timestampValue struct {
timestamp *time.Time
hasBeenSet bool
layouts []string
location *time.Location
}
var _ ValueCreator[time.Time, TimestampConfig] = timestampValue{}
// Below functions are to satisfy the ValueCreator interface
func (t timestampValue) Create(val time.Time, p *time.Time, c TimestampConfig) Value {
*p = val
return ×tampValue{
timestamp: p,
layouts: c.Layouts,
location: c.Timezone,
}
}
func (t timestampValue) ToString(b time.Time) string {
if b.IsZero() {
return ""
}
return fmt.Sprintf("%v", b)
}
// Below functions are to satisfy the Value interface
// Parses the string value to timestamp
func (t *timestampValue) Set(value string) error {
var timestamp time.Time
var err error
if t.location == nil {
t.location = time.UTC
}
if len(t.layouts) == 0 {
return errors.New("got nil/empty layouts slice")
}
for _, layout := range t.layouts {
var locErr error
timestamp, locErr = time.ParseInLocation(layout, value, t.location)
if locErr != nil {
if err == nil {
err = locErr
continue
}
err = newMultiError(err, locErr)
continue
}
err = nil
break
}
if err != nil {
return err
}
defaultTS, _ := time.ParseInLocation(time.TimeOnly, time.TimeOnly, timestamp.Location())
n := time.Now().In(timestamp.Location())
// If format is missing date (or year only), set it explicitly to current
if timestamp.Truncate(time.Hour*24).UnixNano() == defaultTS.Truncate(time.Hour*24).UnixNano() {
timestamp = time.Date(
n.Year(),
n.Month(),
n.Day(),
timestamp.Hour(),
timestamp.Minute(),
timestamp.Second(),
timestamp.Nanosecond(),
timestamp.Location(),
)
} else if timestamp.Year() == 0 {
timestamp = time.Date(
n.Year(),
timestamp.Month(),
timestamp.Day(),
timestamp.Hour(),
timestamp.Minute(),
timestamp.Second(),
timestamp.Nanosecond(),
timestamp.Location(),
)
}
if t.timestamp != nil {
*t.timestamp = timestamp
}
t.hasBeenSet = true
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (t *timestampValue) String() string {
return fmt.Sprintf("%#v", t.timestamp)
}
// Get returns the flag structure
func (t *timestampValue) Get() any {
return *t.timestamp
}
// Timestamp gets the timestamp from a flag name
func (cmd *Command) Timestamp(name string) time.Time {
if v, ok := cmd.Value(name).(time.Time); ok {
tracef("time.Time available for flag name %[1]q with value=%[2]v (cmd=%[3]q)", name, v, cmd.Name)
return v
}
tracef("time.Time NOT available for flag name %[1]q (cmd=%[2]q)", name, cmd.Name)
return time.Time{}
}
|