package runexpr import ( "fmt" "log" "reflect" "regexp" "time" "github.com/lestrrat-go/strftime" ) var ( // matchDuration is a regex which matches ES durations, which // supports days (in contrast to time.Duration which does not) matchDuration = regexp.MustCompile(`(\d+)([ydhms])`) timeFormats = []string{ time.RFC3339, time.RFC1123Z, time.RFC1123, time.RFC850, time.RFC822Z, time.RFC822, time.RubyDate, time.UnixDate, time.DateTime, time.DateOnly, time.TimeOnly, "02.01.2006 15:04:05", // german "02/01/2006 15:04:05", "20060102", "02.01.2006", // german "20060102150405", "15:04:05", "15:04", } ) // setDefault# either returns the first element if item if set or // replace. Types must match expect. func setDefault(item []any, replace any, expect reflect.Type) any { if reflect.TypeOf(replace) != expect { log.Fatal("setDefault(): invalid types called") } if len(item) == 1 { return item[0] } else { return replace } } // strfTime formats a time.Time. Args are any,any to be able to use it in a pipe. // // eg: // // now | strftime("%D") // strftime(now, "%D") // "%d.%m" | strftime(now) func strfTime(fst any, snd ...any) string { var ts time.Time var format string switch firstVal := fst.(type) { case string: format = firstVal ts = setDefault(snd, time.Now(), reflect.TypeFor[time.Time]()).(time.Time) case time.Time: ts = firstVal format = setDefault(snd, "%D", reflect.TypeFor[string]()).(string) } if format == "" || ts.IsZero() { log.Fatal("strftime(): invalid args") } formatted, err := strftime.Format(format, ts) if err != nil { log.Fatal(err) } return formatted } // timeDiff returns the time.Duration diff between time.Time a and // b. It syncs timezones first and exchanges a+b if a>b func timeDiff(a, b time.Time) time.Duration { if a.Location() != b.Location() { b = b.In(a.Location()) } if a.After(b) { a, b = b, a } return b.Sub(a) } // newTime returns a new tim.Time object if ts matches one of the // predefined formats. If format is set, use this. func newTime(ts string, format ...string) time.Time { if len(format) > 0 { t, err := time.Parse(format[0], ts) if err == nil { return t } } for _, format := range timeFormats { t, err := time.Parse(format, ts) if err == nil { return t } } log.Fatal("new(): failed to parse timestamp") return time.Time{} } // toClock returns the HH:MM:SS time part of a time.Time func toClock(t time.Time) string { h, m, s := t.Clock() return fmt.Sprintf("%02d:%02d:%02d", h, m, s) } // timeAdd adds d to t func timeAdd(t time.Time, d time.Duration) time.Time { return t.Add(d) } // toUnix returns the epoch of t func toUnix(t time.Time) int64 { return t.Unix() } // toTZ converts t to given time zone func toTZ(t time.Time, tz any) time.Time { var z *time.Location var err error switch val := tz.(type) { case string: z, err = time.LoadLocation(val) if err != nil { log.Fatal(err) } case int: f := fmt.Sprintf("UTC+%d", val) if val < 0 { f = fmt.Sprintf("UTC%d", val) } z = time.FixedZone(f, val*60*60) } return t.In(z) }