Files
clickhouse-tools/internal/chlog/time.go
T
unkin-agent 415bf0cce1
ci/woodpecker/pr/test Pipeline failed
ci/woodpecker/pr/build Pipeline was successful
ci/woodpecker/pr/pre-commit Pipeline was successful
Add chlog CLI with chcat/chtail/chgrep entrypoints
Single Go binary for the ClickHouse log store (logs.raw): chlog with
cat/tail/grep subcommands, plus chcat/chtail/chgrep argv[0]-dispatched
symlink entrypoints. Every query is time-bounded and fully parameterized;
chgrep guards wide unfiltered scans. Ships nfpm RPM with completions and
woodpecker PR/tag pipelines mirroring node-lookup.
2026-08-23 16:43:05 +10:00

39 lines
988 B
Go

package chlog
import (
"fmt"
"regexp"
"strconv"
"time"
)
var durationRe = regexp.MustCompile(`^(\d+)([smhdw])$`)
var durationUnits = map[string]time.Duration{
"s": time.Second,
"m": time.Minute,
"h": time.Hour,
"d": 24 * time.Hour,
"w": 7 * 24 * time.Hour,
}
// ParseTimeSpec accepts a relative duration (15m, 1h, 2d, 1w) meaning "that
// long before now", or an absolute RFC3339 timestamp.
func ParseTimeSpec(spec string, now time.Time) (time.Time, error) {
if spec == "" {
return time.Time{}, fmt.Errorf("empty time spec")
}
if m := durationRe.FindStringSubmatch(spec); m != nil {
n, err := strconv.ParseInt(m[1], 10, 64)
if err != nil {
return time.Time{}, fmt.Errorf("invalid duration %q: %w", spec, err)
}
return now.Add(-time.Duration(n) * durationUnits[m[2]]), nil
}
t, err := time.Parse(time.RFC3339, spec)
if err != nil {
return time.Time{}, fmt.Errorf("invalid time %q: use a duration (15m, 1h, 2d) or RFC3339", spec)
}
return t.UTC(), nil
}