package cli import ( "fmt" "os" "regexp" "strconv" "strings" "time" ) type FlagStringArray []string func (array *FlagStringArray) String() string { return strings.Join(*array, ", ") } func (array *FlagStringArray) Set(s string) error { *array = append(*array, s) return nil } type FileList FlagStringArray func (fileList FileList) Strings() []string { return ([]string)(fileList) } func (fileList *FileList) String() string { return (*FlagStringArray)(fileList).String() } func (fileList *FileList) Set(s string) error { if _, err := os.Stat(s); os.IsNotExist(err) { return fmt.Errorf("file does not exist: %s", s) } return (*FlagStringArray)(fileList).Set(s) } type Period struct { Start time.Time Stop time.Time } func (period Period) Empty() bool { return period.Stop.Sub(period.Start) == 0 } func (period *Period) String() string { return fmt.Sprintf("%s..%s", period.Start, period.Stop) } func (period *Period) Set(s string) error { ss := strings.Split(s, "..") if err := period.setStartStop(ss[0]); err != nil { return err } if len(ss) != 2 { } else if err := period.setStop(ss[1]); err != nil { return err } return nil } func (period *Period) setStartStop(s string) error { stop, err := period.setT(time.Now(), s, &period.Start) period.Stop = stop return err } func (period *Period) setStop(s string) error { _, err := period.setT(time.Now(), s, &period.Stop) return err } func (*Period) setT(now time.Time, s string, t *time.Time) (time.Time, error) { if s == "" { *t = time.Unix(0, 0) return time.Now().AddDate(100, 0, 0), nil } if submatches := regexp.MustCompile(`[+-]?(?P[0-9]+y)?(?P[0-9]+mo)?(?P[0-9]+w)?(?P[0-9]+d)?([0-9]+[a-z])?`).FindStringSubmatch(s); len(submatches) > 0 { s2 := s result := now scalar := time.Duration(1) if strings.HasPrefix(s2, "-") { scalar = -1 } for i, submatch := range submatches[1 : len(submatches)-1] { if len(submatch) == 0 { continue } unit := submatch[len(submatch)-1] if submatch[len(submatch)-1] == 'o' { // mo submatch = submatch[:len(submatch)-1] } n, err := strconv.Atoi(submatch[:len(submatch)-1]) if err != nil { return time.Time{}, err } for i := 0; i < n; i++ { switch unit { case 'y': result = result.AddDate(int(scalar*1), 0, 0) case 'o': result = result.AddDate(0, int(scalar*1), 0) case 'w': result = result.AddDate(0, 0, int(scalar*7)) case 'd': result = result.AddDate(0, 0, int(scalar*1)) } } s2 = strings.ReplaceAll(s2, submatches[i+1], "") } if stdDuration := strings.TrimLeft(s2, "+-"); stdDuration != "" { if d, err := time.ParseDuration(stdDuration); err == nil { result = result.Add(scalar * d) } } if result != now { *t = result return result.AddDate(100, 0, 0), nil } } if result, err := time.Parse("2006", s); err == nil { *t = result return result.AddDate(1, 0, 0).Add(-1 * time.Minute), nil } if result, err := time.Parse("2006-01", s); err == nil { *t = result return result.AddDate(0, 1, 0).Add(-1 * time.Minute), nil } if result, err := time.Parse("2006-01-02", s); err == nil { *t = result return result.AddDate(0, 0, 1).Add(-1 * time.Minute), nil } return time.Time{}, fmt.Errorf("unimplemented format: %s", s) }