File: flag_timestamp.go

package info (click to toggle)
golang-github-urfave-cli-v3 3.3.8-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 12,676 kB
  • sloc: sh: 26; makefile: 16
file content (142 lines) | stat: -rw-r--r-- 3,197 bytes parent folder | download
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 &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)
}

// 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{}
}