mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
app/{vmselect,vmctl}: move ParseTime() to lib/promutils
Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4091
This is a follow-up for e2053baf32
This commit is contained in:
parent
6601fa3e9d
commit
80946f06c2
5 changed files with 140 additions and 150 deletions
|
@ -2,8 +2,6 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
|
||||||
|
@ -15,81 +13,9 @@ const (
|
||||||
maxTimeMsecs = int64(1<<63-1) / 1e6
|
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.
|
// GetTime returns time from the given string.
|
||||||
func GetTime(s string) (time.Time, error) {
|
func GetTime(s string) (time.Time, error) {
|
||||||
secs, err := parseTime(s)
|
secs, err := promutils.ParseTime(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, fmt.Errorf("cannot parse %s: %w", s, err)
|
return time.Time{}, fmt.Errorf("cannot parse %s: %w", s, err)
|
||||||
}
|
}
|
||||||
|
@ -101,5 +27,5 @@ func GetTime(s string) (time.Time, error) {
|
||||||
msecs = maxTimeMsecs
|
msecs = maxTimeMsecs
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Unix(0, msecs*int64(time.Millisecond)), nil
|
return time.Unix(0, msecs*int64(time.Millisecond)).UTC(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ func GetTime(r *http.Request, argKey string, defaultMs int64) (int64, error) {
|
||||||
return maxTimeMsecs, nil
|
return maxTimeMsecs, nil
|
||||||
}
|
}
|
||||||
// Parse argValue
|
// Parse argValue
|
||||||
secs, err := parseTime(argValue)
|
secs, err := promutils.ParseTime(argValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("cannot parse %s=%s: %w", argKey, argValue, err)
|
return 0, fmt.Errorf("cannot parse %s=%s: %w", argKey, argValue, err)
|
||||||
}
|
}
|
||||||
|
@ -71,78 +71,6 @@ func GetTime(r *http.Request, argKey string, defaultMs int64) (int64, error) {
|
||||||
return msecs, nil
|
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 (
|
var (
|
||||||
// These constants were obtained from https://github.com/prometheus/prometheus/blob/91d7175eaac18b00e370965f3a8186cc40bf9f55/web/api/v1/api.go#L442
|
// 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.
|
// See https://github.com/prometheus/client_golang/issues/614 for details.
|
||||||
|
|
|
@ -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: 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 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: [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): 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): 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).
|
* 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).
|
||||||
|
|
84
lib/promutils/time.go
Normal file
84
lib/promutils/time.go
Normal file
|
@ -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
|
||||||
|
}
|
52
lib/promutils/time_test.go
Normal file
52
lib/promutils/time_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
Loading…
Reference in a new issue