diff options
Diffstat (limited to 'internal/calendar/datespec.go')
| -rw-r--r-- | internal/calendar/datespec.go | 52 |
1 files changed, 52 insertions, 0 deletions
diff --git a/internal/calendar/datespec.go b/internal/calendar/datespec.go new file mode 100644 index 0000000..d0040cc --- /dev/null +++ b/internal/calendar/datespec.go @@ -0,0 +1,52 @@ +package calendar + +import ( + "strconv" + "strings" + "time" +) + +// resolveDate returns the date (UTC midnight) that spec names in year, or +// ok=false if the spec is unparseable. +func resolveDate(spec DateSpec, year int, easter time.Time) (time.Time, bool) { + s := strings.TrimSpace(string(spec)) + switch { + case len(s) == 5 && s[2] == '-': // MM-DD + mo, err1 := strconv.Atoi(s[0:2]) + da, err2 := strconv.Atoi(s[3:5]) + if err1 != nil || err2 != nil { + return time.Time{}, false + } + return time.Date(year, time.Month(mo), da, 0, 0, 0, 0, time.UTC), true + case strings.HasPrefix(s, "easter"): + return offset(easter, s[len("easter"):]) + case strings.HasPrefix(s, "christmas"): + xmas := time.Date(year, 12, 25, 0, 0, 0, 0, time.UTC) + return offset(xmas, s[len("christmas"):]) + case strings.HasPrefix(s, "sunday-after "): + base, ok := resolveDate(DateSpec(strings.TrimPrefix(s, "sunday-after ")), year, easter) + if !ok { + return time.Time{}, false + } + return nextWeekday(base.AddDate(0, 0, 1), time.Sunday), true + } + return time.Time{}, false +} + +// offset parses "±N" and adds N days to base. +func offset(base time.Time, pm string) (time.Time, bool) { + if pm == "" { + return base, true + } + n, err := strconv.Atoi(pm) // strconv.Atoi handles a leading '+' and '-' + if err != nil { + return time.Time{}, false + } + return base.AddDate(0, 0, n), true +} + +// nextWeekday returns the first day >= from whose weekday is wd. +func nextWeekday(from time.Time, wd time.Weekday) time.Time { + delta := (int(wd) - int(from.Weekday()) + 7) % 7 + return from.AddDate(0, 0, delta) +} |
