mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Core: use go-datemath in timerange utility (#23120)
* Core: use go-datemath in time_range * Core: update timerange for negative epoch * Core: update gtime component * Core: clean up time_range tests * Update pkg/components/gtime/gtime.go Co-Authored-By: Arve Knudsen <arve.knudsen@gmail.com> * Core: clean gtime tests * components/gtime: Fix test Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
parent
69bdcccd10
commit
56687a08f9
@ -1,12 +1,13 @@
|
|||||||
package gtime
|
package gtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var dateUnitPattern = regexp.MustCompile(`(\d+)([wdy])`)
|
var dateUnitPattern = regexp.MustCompile(`^(\d+)([dwMy])$`)
|
||||||
|
|
||||||
// ParseInterval parses an interval with support for all units that Grafana uses.
|
// ParseInterval parses an interval with support for all units that Grafana uses.
|
||||||
func ParseInterval(interval string) (time.Duration, error) {
|
func ParseInterval(interval string) (time.Duration, error) {
|
||||||
@ -18,14 +19,18 @@ func ParseInterval(interval string) (time.Duration, error) {
|
|||||||
|
|
||||||
num, _ := strconv.Atoi(string(result[1]))
|
num, _ := strconv.Atoi(string(result[1]))
|
||||||
period := string(result[2])
|
period := string(result[2])
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
if period == `d` {
|
switch period {
|
||||||
return time.Hour * 24 * time.Duration(num), nil
|
case "d":
|
||||||
|
return now.Sub(now.AddDate(0, 0, -num)), nil
|
||||||
|
case "w":
|
||||||
|
return now.Sub(now.AddDate(0, 0, -num*7)), nil
|
||||||
|
case "M":
|
||||||
|
return now.Sub(now.AddDate(0, -num, 0)), nil
|
||||||
|
case "y":
|
||||||
|
return now.Sub(now.AddDate(-num, 0, 0)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if period == `w` {
|
return 0, fmt.Errorf("ParseInterval: invalid duration %q", interval)
|
||||||
return time.Hour * 24 * 7 * time.Duration(num), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return time.Hour * 24 * 7 * 365 * time.Duration(num), nil
|
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,38 @@
|
|||||||
package gtime
|
package gtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParseInterval(t *testing.T) {
|
func TestParseInterval(t *testing.T) {
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
tcs := []struct {
|
tcs := []struct {
|
||||||
interval string
|
interval string
|
||||||
duration time.Duration
|
duration time.Duration
|
||||||
err error
|
err string
|
||||||
}{
|
}{
|
||||||
{interval: "1d", duration: time.Hour * 24},
|
{interval: "1d", duration: now.Sub(now.AddDate(0, 0, -1))},
|
||||||
{interval: "1w", duration: time.Hour * 24 * 7},
|
{interval: "1w", duration: now.Sub(now.AddDate(0, 0, -7))},
|
||||||
{interval: "2w", duration: time.Hour * 24 * 7 * 2},
|
{interval: "2w", duration: now.Sub(now.AddDate(0, 0, -14))},
|
||||||
{interval: "1y", duration: time.Hour * 24 * 7 * 365},
|
{interval: "1M", duration: now.Sub(now.AddDate(0, -1, 0))},
|
||||||
{interval: "5y", duration: time.Hour * 24 * 7 * 365 * 5},
|
{interval: "1y", duration: now.Sub(now.AddDate(-1, 0, 0))},
|
||||||
{interval: "1M", err: errors.New("time: unknown unit M in duration 1M")},
|
{interval: "5y", duration: now.Sub(now.AddDate(-5, 0, 0))},
|
||||||
{interval: "invalid-duration", err: errors.New("time: invalid duration invalid-duration")},
|
{interval: "invalid-duration", err: "time: invalid duration invalid-duration"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range tcs {
|
for i, tc := range tcs {
|
||||||
t.Run(fmt.Sprintf("testcase %d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("testcase %d", i), func(t *testing.T) {
|
||||||
res, err := ParseInterval(tc.interval)
|
res, err := ParseInterval(tc.interval)
|
||||||
if err != nil && err.Error() != tc.err.Error() {
|
if tc.err == "" {
|
||||||
t.Fatalf("expected '%v' got '%v'", tc.err, err)
|
require.NoError(t, err, "interval %q", tc.interval)
|
||||||
}
|
require.Equal(t, tc.duration, res, "interval %q", tc.interval)
|
||||||
if res != tc.duration {
|
} else {
|
||||||
t.Errorf("expected %v got %v", tc.duration, res)
|
require.EqualError(t, err, tc.err, "interval %q", tc.interval)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package tsdb
|
package tsdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/timberio/go-datemath"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewTimeRange(from, to string) *TimeRange {
|
func NewTimeRange(from, to string) *TimeRange {
|
||||||
@ -79,38 +80,26 @@ func tryParseUnixMsEpoch(val string) (time.Time, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TimeRange) ParseFrom() (time.Time, error) {
|
func (tr *TimeRange) ParseFrom() (time.Time, error) {
|
||||||
if res, ok := tryParseUnixMsEpoch(tr.From); ok {
|
return parse(tr.From, tr.now, false)
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
fromRaw := strings.Replace(tr.From, "now-", "", 1)
|
|
||||||
diff, err := time.ParseDuration("-" + fromRaw)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tr.now.Add(diff), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TimeRange) ParseTo() (time.Time, error) {
|
func (tr *TimeRange) ParseTo() (time.Time, error) {
|
||||||
if tr.To == "now" {
|
return parse(tr.To, tr.now, true)
|
||||||
return tr.now, nil
|
|
||||||
} else if strings.HasPrefix(tr.To, "now-") {
|
|
||||||
withoutNow := strings.Replace(tr.To, "now-", "", 1)
|
|
||||||
|
|
||||||
diff, err := time.ParseDuration("-" + withoutNow)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return tr.now.Add(diff), nil
|
func parse(s string, now time.Time, withRoundUp bool) (time.Time, error) {
|
||||||
}
|
if res, ok := tryParseUnixMsEpoch(s); ok {
|
||||||
|
|
||||||
if res, ok := tryParseUnixMsEpoch(tr.To); ok {
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return time.Time{}, fmt.Errorf("cannot parse to value %s", tr.To)
|
withoutNow := strings.Replace(s, "now-", "", 1)
|
||||||
|
|
||||||
|
diff, err := time.ParseDuration("-" + withoutNow)
|
||||||
|
if err != nil {
|
||||||
|
return datemath.ParseAndEvaluate(s, datemath.WithNow(now), datemath.WithRoundUp(withRoundUp))
|
||||||
|
}
|
||||||
|
|
||||||
|
return now.Add(diff), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EpochPrecisionToMs converts epoch precision to millisecond, if needed.
|
// EpochPrecisionToMs converts epoch precision to millisecond, if needed.
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package tsdb
|
package tsdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
@ -20,7 +22,8 @@ func TestTimeRange(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Convey("5m ago ", func() {
|
Convey("5m ago ", func() {
|
||||||
fiveMinAgo, _ := time.ParseDuration("-5m")
|
fiveMinAgo, err := time.ParseDuration("-5m")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
expected := now.Add(fiveMinAgo)
|
expected := now.Add(fiveMinAgo)
|
||||||
|
|
||||||
res, err := tr.ParseFrom()
|
res, err := tr.ParseFrom()
|
||||||
@ -43,7 +46,8 @@ func TestTimeRange(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Convey("5h ago ", func() {
|
Convey("5h ago ", func() {
|
||||||
fiveHourAgo, _ := time.ParseDuration("-5h")
|
fiveHourAgo, err := time.ParseDuration("-5h")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
expected := now.Add(fiveHourAgo)
|
expected := now.Add(fiveHourAgo)
|
||||||
|
|
||||||
res, err := tr.ParseFrom()
|
res, err := tr.ParseFrom()
|
||||||
@ -52,7 +56,8 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("now-10m ", func() {
|
Convey("now-10m ", func() {
|
||||||
tenMinAgo, _ := time.ParseDuration("-10m")
|
tenMinAgo, err := time.ParseDuration("-10m")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
expected := now.Add(tenMinAgo)
|
expected := now.Add(tenMinAgo)
|
||||||
res, err := tr.ParseTo()
|
res, err := tr.ParseTo()
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
@ -60,7 +65,101 @@ func TestTimeRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("can parse unix epocs", func() {
|
now, err := time.Parse(time.RFC3339Nano, "2020-03-26T15:12:56.000Z")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
Convey("Can parse now-1M/M, now-1M/M", func() {
|
||||||
|
tr := TimeRange{
|
||||||
|
From: "now-1M/M",
|
||||||
|
To: "now-1M/M",
|
||||||
|
now: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
Convey("from now-1M/M ", func() {
|
||||||
|
expected, err := time.Parse(time.RFC3339Nano, "2020-02-01T00:00:00.000Z")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
res, err := tr.ParseFrom()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldEqual, expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("to now-1M/M ", func() {
|
||||||
|
expected, err := time.Parse(time.RFC3339Nano, "2020-02-29T23:59:59.999Z")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
res, err := tr.ParseTo()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldEqual, expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Can parse now-3d, now+3w", func() {
|
||||||
|
tr := TimeRange{
|
||||||
|
From: "now-3d",
|
||||||
|
To: "now+3w",
|
||||||
|
now: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
Convey("now-3d ", func() {
|
||||||
|
expected, err := time.Parse(time.RFC3339Nano, "2020-03-23T15:12:56.000Z")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
res, err := tr.ParseFrom()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldEqual, expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("now+3w ", func() {
|
||||||
|
expected, err := time.Parse(time.RFC3339Nano, "2020-04-16T15:12:56.000Z")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
res, err := tr.ParseTo()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldEqual, expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Can parse 1960-02-01T07:00:00.000Z, 1965-02-03T08:00:00.000Z", func() {
|
||||||
|
tr := TimeRange{
|
||||||
|
From: "1960-02-01T07:00:00.000Z",
|
||||||
|
To: "1965-02-03T08:00:00.000Z",
|
||||||
|
now: now,
|
||||||
|
}
|
||||||
|
|
||||||
|
Convey("1960-02-01T07:00:00.000Z ", func() {
|
||||||
|
expected, err := time.Parse(time.RFC3339Nano, "1960-02-01T07:00:00.000Z")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
res, err := tr.ParseFrom()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldEqual, expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("1965-02-03T08:00:00.000Z ", func() {
|
||||||
|
expected, err := time.Parse(time.RFC3339Nano, "1965-02-03T08:00:00.000Z")
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
res, err := tr.ParseTo()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldEqual, expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Can parse negative unix epochs", func() {
|
||||||
|
from := time.Date(1960, 2, 1, 7, 0, 0, 0, time.UTC)
|
||||||
|
to := time.Date(1965, 2, 3, 8, 0, 0, 0, time.UTC)
|
||||||
|
tr := NewTimeRange(strconv.FormatInt(from.UnixNano()/int64(time.Millisecond), 10), strconv.FormatInt(to.UnixNano()/int64(time.Millisecond), 10))
|
||||||
|
|
||||||
|
res, err := tr.ParseFrom()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldEqual, from)
|
||||||
|
|
||||||
|
res, err = tr.ParseTo()
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
So(res, ShouldEqual, to)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("can parse unix epochs", func() {
|
||||||
var err error
|
var err error
|
||||||
tr := TimeRange{
|
tr := TimeRange{
|
||||||
From: "1474973725473",
|
From: "1474973725473",
|
||||||
|
Loading…
Reference in New Issue
Block a user