Commit 56687a08 by Agnès Toulet Committed by GitHub

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>
parent 69bdcccd
package gtime
import (
"fmt"
"regexp"
"strconv"
"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.
func ParseInterval(interval string) (time.Duration, error) {
......@@ -18,14 +19,18 @@ func ParseInterval(interval string) (time.Duration, error) {
num, _ := strconv.Atoi(string(result[1]))
period := string(result[2])
if period == `d` {
return time.Hour * 24 * time.Duration(num), nil
}
if period == `w` {
return time.Hour * 24 * 7 * time.Duration(num), nil
now := time.Now()
switch period {
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
}
return time.Hour * 24 * 7 * 365 * time.Duration(num), nil
return 0, fmt.Errorf("ParseInterval: invalid duration %q", interval)
}
package gtime
import (
"errors"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestParseInterval(t *testing.T) {
now := time.Now()
tcs := []struct {
interval string
duration time.Duration
err error
err string
}{
{interval: "1d", duration: time.Hour * 24},
{interval: "1w", duration: time.Hour * 24 * 7},
{interval: "2w", duration: time.Hour * 24 * 7 * 2},
{interval: "1y", duration: time.Hour * 24 * 7 * 365},
{interval: "5y", duration: time.Hour * 24 * 7 * 365 * 5},
{interval: "1M", err: errors.New("time: unknown unit M in duration 1M")},
{interval: "invalid-duration", err: errors.New("time: invalid duration invalid-duration")},
{interval: "1d", duration: now.Sub(now.AddDate(0, 0, -1))},
{interval: "1w", duration: now.Sub(now.AddDate(0, 0, -7))},
{interval: "2w", duration: now.Sub(now.AddDate(0, 0, -14))},
{interval: "1M", duration: now.Sub(now.AddDate(0, -1, 0))},
{interval: "1y", duration: now.Sub(now.AddDate(-1, 0, 0))},
{interval: "5y", duration: now.Sub(now.AddDate(-5, 0, 0))},
{interval: "invalid-duration", err: "time: invalid duration invalid-duration"},
}
for i, tc := range tcs {
t.Run(fmt.Sprintf("testcase %d", i), func(t *testing.T) {
res, err := ParseInterval(tc.interval)
if err != nil && err.Error() != tc.err.Error() {
t.Fatalf("expected '%v' got '%v'", tc.err, err)
}
if res != tc.duration {
t.Errorf("expected %v got %v", tc.duration, res)
if tc.err == "" {
require.NoError(t, err, "interval %q", tc.interval)
require.Equal(t, tc.duration, res, "interval %q", tc.interval)
} else {
require.EqualError(t, err, tc.err, "interval %q", tc.interval)
}
})
}
......
package tsdb
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/timberio/go-datemath"
)
func NewTimeRange(from, to string) *TimeRange {
......@@ -79,38 +80,26 @@ func tryParseUnixMsEpoch(val string) (time.Time, bool) {
}
func (tr *TimeRange) ParseFrom() (time.Time, error) {
if res, ok := tryParseUnixMsEpoch(tr.From); ok {
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
return parse(tr.From, tr.now, false)
}
func (tr *TimeRange) ParseTo() (time.Time, error) {
if tr.To == "now" {
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 parse(tr.To, tr.now, true)
}
return tr.now.Add(diff), nil
func parse(s string, now time.Time, withRoundUp bool) (time.Time, error) {
if res, ok := tryParseUnixMsEpoch(s); ok {
return res, nil
}
if res, ok := tryParseUnixMsEpoch(tr.To); ok {
return res, nil
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 time.Time{}, fmt.Errorf("cannot parse to value %s", tr.To)
return now.Add(diff), nil
}
// EpochPrecisionToMs converts epoch precision to millisecond, if needed.
......
package tsdb
import (
"strconv"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
......@@ -20,7 +22,8 @@ func TestTimeRange(t *testing.T) {
}
Convey("5m ago ", func() {
fiveMinAgo, _ := time.ParseDuration("-5m")
fiveMinAgo, err := time.ParseDuration("-5m")
So(err, ShouldBeNil)
expected := now.Add(fiveMinAgo)
res, err := tr.ParseFrom()
......@@ -43,7 +46,8 @@ func TestTimeRange(t *testing.T) {
}
Convey("5h ago ", func() {
fiveHourAgo, _ := time.ParseDuration("-5h")
fiveHourAgo, err := time.ParseDuration("-5h")
So(err, ShouldBeNil)
expected := now.Add(fiveHourAgo)
res, err := tr.ParseFrom()
......@@ -52,7 +56,8 @@ func TestTimeRange(t *testing.T) {
})
Convey("now-10m ", func() {
tenMinAgo, _ := time.ParseDuration("-10m")
tenMinAgo, err := time.ParseDuration("-10m")
So(err, ShouldBeNil)
expected := now.Add(tenMinAgo)
res, err := tr.ParseTo()
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
tr := TimeRange{
From: "1474973725473",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment