From 5dbaffe2c621e401e7ff926f8a51f152fe5c5e24 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Mon, 8 May 2023 14:14:44 -0700 Subject: [PATCH] app/{vmselect,vmctl}: move ParseTime() to lib/promutils Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4091 This is a follow-up for e2053baf326b4b75183b2b8a15e1de8c3e9e000d --- app/vmctl/utils/time.go | 78 +---------------------- app/vmselect/searchutils/searchutils.go | 74 +--------------------- docs/CHANGELOG.md | 2 +- lib/promutils/time.go | 84 +++++++++++++++++++++++++ lib/promutils/time_test.go | 52 +++++++++++++++ 5 files changed, 140 insertions(+), 150 deletions(-) create mode 100644 lib/promutils/time.go create mode 100644 lib/promutils/time_test.go diff --git a/app/vmctl/utils/time.go b/app/vmctl/utils/time.go index 541a84f45..9b3b496b4 100644 --- a/app/vmctl/utils/time.go +++ b/app/vmctl/utils/time.go @@ -2,8 +2,6 @@ package utils import ( "fmt" - "strconv" - "strings" "time" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils" @@ -15,81 +13,9 @@ const ( maxTimeMsecs = int64(1<<63-1) / 1e6 ) -func parseTime(s string) (float64, error) { - if len(s) > 0 && (s[len(s)-1] != 'Z' && s[len(s)-1] > '9' || s[0] == '-') { - // Parse duration relative to the current time - d, err := promutils.ParseDuration(s) - if err != nil { - return 0, err - } - if d > 0 { - d = -d - } - t := time.Now().Add(d) - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 4 { - // Parse YYYY - t, err := time.Parse("2006", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if !strings.Contains(s, "-") { - // Parse the timestamp in milliseconds - return strconv.ParseFloat(s, 64) - } - if len(s) == 7 { - // Parse YYYY-MM - t, err := time.Parse("2006-01", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 10 { - // Parse YYYY-MM-DD - t, err := time.Parse("2006-01-02", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 13 { - // Parse YYYY-MM-DDTHH - t, err := time.Parse("2006-01-02T15", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 16 { - // Parse YYYY-MM-DDTHH:MM - t, err := time.Parse("2006-01-02T15:04", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 19 { - // Parse YYYY-MM-DDTHH:MM:SS - t, err := time.Parse("2006-01-02T15:04:05", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - t, err := time.Parse(time.RFC3339, s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil -} - // GetTime returns time from the given string. func GetTime(s string) (time.Time, error) { - secs, err := parseTime(s) + secs, err := promutils.ParseTime(s) if err != nil { return time.Time{}, fmt.Errorf("cannot parse %s: %w", s, err) } @@ -101,5 +27,5 @@ func GetTime(s string) (time.Time, error) { msecs = maxTimeMsecs } - return time.Unix(0, msecs*int64(time.Millisecond)), nil + return time.Unix(0, msecs*int64(time.Millisecond)).UTC(), nil } diff --git a/app/vmselect/searchutils/searchutils.go b/app/vmselect/searchutils/searchutils.go index dc9a130e0..eca5f3df7 100644 --- a/app/vmselect/searchutils/searchutils.go +++ b/app/vmselect/searchutils/searchutils.go @@ -59,7 +59,7 @@ func GetTime(r *http.Request, argKey string, defaultMs int64) (int64, error) { return maxTimeMsecs, nil } // Parse argValue - secs, err := parseTime(argValue) + secs, err := promutils.ParseTime(argValue) if err != nil { return 0, fmt.Errorf("cannot parse %s=%s: %w", argKey, argValue, err) } @@ -73,78 +73,6 @@ func GetTime(r *http.Request, argKey string, defaultMs int64) (int64, error) { return msecs, nil } -func parseTime(s string) (float64, error) { - if len(s) > 0 && (s[len(s)-1] != 'Z' && s[len(s)-1] > '9' || s[0] == '-') { - // Parse duration relative to the current time - d, err := promutils.ParseDuration(s) - if err != nil { - return 0, err - } - if d > 0 { - d = -d - } - t := time.Now().Add(d) - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 4 { - // Parse YYYY - t, err := time.Parse("2006", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if !strings.Contains(s, "-") { - // Parse the timestamp in milliseconds - return strconv.ParseFloat(s, 64) - } - if len(s) == 7 { - // Parse YYYY-MM - t, err := time.Parse("2006-01", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 10 { - // Parse YYYY-MM-DD - t, err := time.Parse("2006-01-02", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 13 { - // Parse YYYY-MM-DDTHH - t, err := time.Parse("2006-01-02T15", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 16 { - // Parse YYYY-MM-DDTHH:MM - t, err := time.Parse("2006-01-02T15:04", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - if len(s) == 19 { - // Parse YYYY-MM-DDTHH:MM:SS - t, err := time.Parse("2006-01-02T15:04:05", s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil - } - t, err := time.Parse(time.RFC3339, s) - if err != nil { - return 0, err - } - return float64(t.UnixNano()) / 1e9, nil -} - var ( // These constants were obtained from https://github.com/prometheus/prometheus/blob/91d7175eaac18b00e370965f3a8186cc40bf9f55/web/api/v1/api.go#L442 // See https://github.com/prometheus/client_golang/issues/614 for details. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4e36bdb0a..898d0f320 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -32,7 +32,7 @@ The following tip changes can be tested by building VictoriaMetrics components f * FEATURE: deprecate `-bigMergeConcurrency` command-line flag, since improper configuration for this flag frequently led to uncontrolled growth of unmerged parts, which, in turn, could lead to queries slowdown and increased CPU usage. The concurrency for [background merges](https://docs.victoriametrics.com/#storage) can be controlled via `-smallMergeConcurrency` command-line flag, though it isn't recommended to do in general case. * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): support new filtering options `filter` and `node_filter` for [consul service discovery](https://docs.victoriametrics.com/sd_configs.html#consul_sd_configs). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4183) for details. * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): support new [consulagent service discovery](https://docs.victoriametrics.com/sd_configs.html#consulagent_sd_configs). See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3953) for details. -* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add support for the different time formats for `--vm-native-filter-time-start` and `--vm-native-filter-time-end` flags if the native binary protocol is used for migration. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4091). +* FEATURE: [vmctl](https://docs.victoriametrics.com/vmctl.html): add support for [different time formats](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#timestamp-formats) for `--vm-native-filter-time-start` and `--vm-native-filter-time-end` command-line flags. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4091). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): integrate WITH template playground. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3811). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add a comparison of data from the previous day with data from the current day to the `Cardinality Explorer`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3967). * FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): display histogram metrics as a heatmap in the `explore metrics` tab. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4111). diff --git a/lib/promutils/time.go b/lib/promutils/time.go new file mode 100644 index 000000000..e0490301b --- /dev/null +++ b/lib/promutils/time.go @@ -0,0 +1,84 @@ +package promutils + +import ( + "strconv" + "strings" + "time" +) + +// ParseTime parses time s in different formats. +// +// See https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#timestamp-formats +// +// It returns unix timestamp in seconds. +func ParseTime(s string) (float64, error) { + if len(s) > 0 && (s[len(s)-1] != 'Z' && s[len(s)-1] > '9' || s[0] == '-') { + // Parse duration relative to the current time + d, err := ParseDuration(s) + if err != nil { + return 0, err + } + if d > 0 { + d = -d + } + t := time.Now().Add(d) + return float64(t.UnixNano()) / 1e9, nil + } + if len(s) == 4 { + // Parse YYYY + t, err := time.Parse("2006", s) + if err != nil { + return 0, err + } + return float64(t.UnixNano()) / 1e9, nil + } + if !strings.Contains(s, "-") { + // Parse the timestamp in milliseconds + return strconv.ParseFloat(s, 64) + } + if len(s) == 7 { + // Parse YYYY-MM + t, err := time.Parse("2006-01", s) + if err != nil { + return 0, err + } + return float64(t.UnixNano()) / 1e9, nil + } + if len(s) == 10 { + // Parse YYYY-MM-DD + t, err := time.Parse("2006-01-02", s) + if err != nil { + return 0, err + } + return float64(t.UnixNano()) / 1e9, nil + } + if len(s) == 13 { + // Parse YYYY-MM-DDTHH + t, err := time.Parse("2006-01-02T15", s) + if err != nil { + return 0, err + } + return float64(t.UnixNano()) / 1e9, nil + } + if len(s) == 16 { + // Parse YYYY-MM-DDTHH:MM + t, err := time.Parse("2006-01-02T15:04", s) + if err != nil { + return 0, err + } + return float64(t.UnixNano()) / 1e9, nil + } + if len(s) == 19 { + // Parse YYYY-MM-DDTHH:MM:SS + t, err := time.Parse("2006-01-02T15:04:05", s) + if err != nil { + return 0, err + } + return float64(t.UnixNano()) / 1e9, nil + } + t, err := time.Parse(time.RFC3339, s) + if err != nil { + return 0, err + } + return float64(t.UnixNano()) / 1e9, nil +} diff --git a/lib/promutils/time_test.go b/lib/promutils/time_test.go new file mode 100644 index 000000000..e085fdc8e --- /dev/null +++ b/lib/promutils/time_test.go @@ -0,0 +1,52 @@ +package promutils + +import ( + "math" + "testing" + "time" +) + +func TestParseTimeSuccess(t *testing.T) { + f := func(s string, resultExpected float64) { + t.Helper() + result, err := ParseTime(s) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + if math.Abs(result-resultExpected) > 10 { + t.Fatalf("unexpected result; got %v; want %v", result, resultExpected) + } + } + + now := float64(time.Now().UnixNano()) / 1e9 + // duration relative to the current time + f("1h5s", now-3605) + + // negative duration relative to the current time + f("-5m", now-5*60) + + // Year + f("2023", 1.6725312e+09) + + // Year and month + f("2023-05", 1.6828992e+09) + + // Year, month and day + f("2023-05-20", 1.6845408e+09) + + // Year, month, day and hour + f("2023-05-20T04", 1.6845552e+09) + + // Year, month, day, hour and minute + f("2023-05-20T04:57", 1.68455862e+09) + + // Year, month, day, hour, minute and second + f("2023-05-20T04:57:43", 1.684558663e+09) + + // RFC3339 + f("2023-05-20T04:57:43Z", 1.684558663e+09) + f("2023-05-20T04:57:43+02:30", 1.684549663e+09) + f("2023-05-20T04:57:43-02:30", 1.684567663e+09) + f("2023-05-20T04:57:43.123Z", 1.6845586631230001e+09) + f("2023-05-20T04:57:43.123456789Z", 1.6845586631230001e+09) +}