extendedParseDuration is a more lenient version of parseDuration that allows for more flexible input formats and cumulative durations. It allows for some extra units: - d (days, interpreted as 24h) - y (years, interpreted as 8_760h) FIXME: handle fractional values as discussed in https://github.com
(raw string)
| 190 | // |
| 191 | // FIXME: handle fractional values as discussed in https://github.com/coder/coder/pull/15040#discussion_r1799261736 |
| 192 | func extendedParseDuration(raw string) (time.Duration, error) { |
| 193 | var d int64 |
| 194 | isPositive := true |
| 195 | |
| 196 | // handle negative durations by checking for a leading '-' |
| 197 | if strings.HasPrefix(raw, "-") { |
| 198 | raw = raw[1:] |
| 199 | isPositive = false |
| 200 | } |
| 201 | |
| 202 | if raw == "" { |
| 203 | return 0, xerrors.Errorf("invalid duration: %q", raw) |
| 204 | } |
| 205 | |
| 206 | // Regular expression to match any characters that do not match the expected duration format |
| 207 | invalidCharRe := regexp.MustCompile(`[^0-9|nsuµhdym]+`) |
| 208 | if invalidCharRe.MatchString(raw) { |
| 209 | return 0, xerrors.Errorf("invalid duration format: %q", raw) |
| 210 | } |
| 211 | |
| 212 | // Regular expression to match numbers followed by 'd', 'y', or time units |
| 213 | re := regexp.MustCompile(`(-?\d+)(ns|us|µs|ms|s|m|h|d|y)`) |
| 214 | matches := re.FindAllStringSubmatch(raw, -1) |
| 215 | |
| 216 | for _, match := range matches { |
| 217 | var num int64 |
| 218 | num, err := strconv.ParseInt(match[1], 10, 0) |
| 219 | if err != nil { |
| 220 | return 0, xerrors.Errorf("invalid duration: %q", match[1]) |
| 221 | } |
| 222 | |
| 223 | switch match[2] { |
| 224 | case "d": |
| 225 | // we want to check if d + num * int64(24*time.Hour) would overflow |
| 226 | if d > (1<<63-1)-num*int64(24*time.Hour) { |
| 227 | return 0, xerrors.Errorf("invalid duration: %q", raw) |
| 228 | } |
| 229 | d += num * int64(24*time.Hour) |
| 230 | case "y": |
| 231 | // we want to check if d + num * int64(8760*time.Hour) would overflow |
| 232 | if d > (1<<63-1)-num*int64(8760*time.Hour) { |
| 233 | return 0, xerrors.Errorf("invalid duration: %q", raw) |
| 234 | } |
| 235 | d += num * int64(8760*time.Hour) |
| 236 | case "h", "m", "s", "ns", "us", "µs", "ms": |
| 237 | partDuration, err := time.ParseDuration(match[0]) |
| 238 | if err != nil { |
| 239 | return 0, xerrors.Errorf("invalid duration: %q", match[0]) |
| 240 | } |
| 241 | if d > (1<<63-1)-int64(partDuration) { |
| 242 | return 0, xerrors.Errorf("invalid duration: %q", raw) |
| 243 | } |
| 244 | d += int64(partDuration) |
| 245 | default: |
| 246 | return 0, xerrors.Errorf("invalid duration unit: %q", match[2]) |
| 247 | } |
| 248 | } |
| 249 |