package cal import ( "fmt" "strconv" "strings" "time" ) // parseDuration parses an RFC 5545 duration: [+-]P[nW][nD][T[nH][nM][nS]]. // A leading '-' means before the reference time, so "-PT15M" is -15m. func parseDuration(s string) (time.Duration, error) { orig := s neg := false switch { case strings.HasPrefix(s, "-"): neg, s = true, s[1:] case strings.HasPrefix(s, "+"): s = s[1:] } if !strings.HasPrefix(s, "P") { return 0, fmt.Errorf("not a duration: %q", orig) } s = s[1:] var d time.Duration inTime := false num := "" for _, r := range s { switch { case r >= '0' && r <= '9': num += string(r) continue case r == 'T': inTime = true continue } if num == "" { return 0, fmt.Errorf("unit %q with no count in %q", r, orig) } n, err := strconv.Atoi(num) if err != nil { return 0, fmt.Errorf("bad count in %q", orig) } num = "" var unit time.Duration switch r { case 'W': unit = 7 * 24 * time.Hour case 'D': unit = 24 * time.Hour case 'H': if !inTime { return 0, fmt.Errorf("H outside time part in %q", orig) } unit = time.Hour case 'M': if !inTime { return 0, fmt.Errorf("M outside time part in %q", orig) } unit = time.Minute case 'S': if !inTime { return 0, fmt.Errorf("S outside time part in %q", orig) } unit = time.Second default: return 0, fmt.Errorf("unknown unit %q in %q", r, orig) } d += time.Duration(n) * unit } if num != "" { return 0, fmt.Errorf("trailing count in %q", orig) } if neg { d = -d } return d, nil }