package functions import ( "fmt" "regexp" "strconv" "time" ) var pattern = regexp.MustCompile(`^P((?P\d+)Y)?((?P\d+)M)?((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$`) func DateTimeAdd(args ...any) any { if len(args) < 2 { return nil } base, ok := args[0].(string) if !ok { return nil } format := time.RFC3339 if len(args) == 3 { if providedFormat, ok := args[2].(string); ok { format = convertFormat(providedFormat) } } baseTime, err := time.Parse(format, base) if err != nil { return nil } duration, err := parseISO8601(args[1].(string)) if err != nil { return nil } timeDuration := duration.timeDuration() baseTime = baseTime.Add(timeDuration) if ok { return baseTime.Format(format) } return baseTime.Format(time.RFC3339) } type Iso8601Duration struct { Y int M int W int D int // Time Component TH int TM int TS int } func parseISO8601(from string) (Iso8601Duration, error) { var match []string var d Iso8601Duration if pattern.MatchString(from) { match = pattern.FindStringSubmatch(from) } else { return d, fmt.Errorf("could not parse duration string") } for i, name := range pattern.SubexpNames() { part := match[i] if i == 0 || name == "" || part == "" { continue } val, err := strconv.Atoi(part) if err != nil { return d, err } switch name { case "year": d.Y = val case "month": d.M = val case "week": d.W = val case "day": d.D = val case "hour": d.TH = val case "minute": d.TM = val case "second": d.TS = val default: return d, fmt.Errorf("unknown field %s", name) } } return d, nil } func (d Iso8601Duration) timeDuration() time.Duration { var dur time.Duration dur += time.Duration(d.TH) * time.Hour dur += time.Duration(d.TM) * time.Minute dur += time.Duration(d.TS) * time.Second dur += time.Duration(d.D) * 24 * time.Hour dur += time.Duration(d.W) * 7 * 24 * time.Hour dur += time.Duration(d.M) * 30 * 24 * time.Hour dur += time.Duration(d.Y) * 365 * 24 * time.Hour return dur }