chord/vendor/github.com/urfave/cli/v3/flag_timestamp.go
2025-03-15 20:42:37 -04:00

153 lines
3.4 KiB
Go

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 &timestampValue{
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)
}
// Timestamp constructor(for internal testing only)
func newTimestamp(timestamp time.Time) *timestampValue {
return &timestampValue{timestamp: &timestamp}
}
// Below functions are to satisfy the flag.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()
// 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)
}
// Value returns the timestamp value stored in the flag
func (t *timestampValue) Value() *time.Time {
return 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{}
}