diff --git a/runtime/pkg/rilltime/rilltime.go b/runtime/pkg/rilltime/rilltime.go index 37999eb8a36e..8ce5cbb06601 100644 --- a/runtime/pkg/rilltime/rilltime.go +++ b/runtime/pkg/rilltime/rilltime.go @@ -76,6 +76,16 @@ var ( "PQ": "-1Q/Q to ref/Q", "PY": "-1Y/Y to ref/Y", } + // Mapping for our old rill- comparison offsets to ISO durations. + // Older reports/alerts may send these as the offset of a legacy comparison time range. + // "PP" (previous period) is handled separately since it depends on the main interval. + daxOffsetNotations = map[string]string{ + "PD": "P1D", + "PW": "P1W", + "PM": "P1M", + "PQ": "P3M", + "PY": "P1Y", + } grainMap = map[string]timeutil.TimeGrain{ "s": timeutil.TimeGrainSecond, "S": timeutil.TimeGrainSecond, @@ -352,7 +362,19 @@ func ParseLegacy(duration, offset string, roundToGrain timeutil.TimeGrain, parse if offset != "" { if strings.HasPrefix(offset, "rill-") { - return nil, fmt.Errorf("offset cannot have DAX notation") + dax := strings.TrimPrefix(offset, "rill-") + if dax == "PP" { + rt.Offset = &Offset{ + PreviousPeriod: &PreviousPeriod{Prefix: "-", Num: 1}, + } + return rt, nil + } + + iso, ok := daxOffsetNotations[dax] + if !ok { + return nil, fmt.Errorf("invalid DAX offset %q", offset) + } + offset = iso } offsetGrainPart, err := parseISODuration(offset) diff --git a/runtime/pkg/rilltime/rilltime_test.go b/runtime/pkg/rilltime/rilltime_test.go index bae3ff6a59e0..5860cc8fbe1d 100644 --- a/runtime/pkg/rilltime/rilltime_test.go +++ b/runtime/pkg/rilltime/rilltime_test.go @@ -495,6 +495,14 @@ func TestParseISO(t *testing.T) { {"With duration and offset no round to grain", "P7D", "P2D", timeutil.TimeGrainUnspecified, "2025-05-04T06:32:36Z", "2025-05-11T06:32:36Z", timeutil.TimeGrainUnspecified}, {"With duration, offset and round to grain", "P7D", "P2D", timeutil.TimeGrainDay, "2025-05-04T00:00:00Z", "2025-05-11T00:00:00Z", timeutil.TimeGrainUnspecified}, {"With DAX duration, offset and round to grain", "rill-PW", "P2D", timeutil.TimeGrainDay, "2025-05-03T00:00:00Z", "2025-05-10T00:00:00Z", timeutil.TimeGrainUnspecified}, + // Legacy DAX comparison offsets sent by older clients + {"With duration and DAX previous period offset", "P7D", "rill-PP", timeutil.TimeGrainUnspecified, "2025-04-29T06:32:36Z", "2025-05-06T06:32:36Z", timeutil.TimeGrainUnspecified}, + {"With duration and DAX previous day offset", "P7D", "rill-PD", timeutil.TimeGrainUnspecified, "2025-05-05T06:32:36Z", "2025-05-12T06:32:36Z", timeutil.TimeGrainUnspecified}, + {"With duration and DAX previous week offset", "P7D", "rill-PW", timeutil.TimeGrainUnspecified, "2025-04-29T06:32:36Z", "2025-05-06T06:32:36Z", timeutil.TimeGrainUnspecified}, + {"With duration and DAX previous month offset", "P7D", "rill-PM", timeutil.TimeGrainUnspecified, "2025-04-06T06:32:36Z", "2025-04-13T06:32:36Z", timeutil.TimeGrainUnspecified}, + {"With duration and DAX previous quarter offset", "P7D", "rill-PQ", timeutil.TimeGrainUnspecified, "2025-02-06T06:32:36Z", "2025-02-13T06:32:36Z", timeutil.TimeGrainUnspecified}, + {"With duration and DAX previous year offset", "P7D", "rill-PY", timeutil.TimeGrainUnspecified, "2024-05-06T06:32:36Z", "2024-05-13T06:32:36Z", timeutil.TimeGrainUnspecified}, + {"With duration, DAX previous week offset and round to grain", "P7D", "rill-PW", timeutil.TimeGrainDay, "2025-04-29T00:00:00Z", "2025-05-06T00:00:00Z", timeutil.TimeGrainUnspecified}, } nowTm := parseTestTime(t, now) @@ -522,6 +530,11 @@ func TestParseISO(t *testing.T) { } } +func TestParseISO_InvalidOffset(t *testing.T) { + _, err := ParseLegacy("P7D", "rill-PX", timeutil.TimeGrainUnspecified, ParseOptions{}) + require.ErrorContains(t, err, `invalid DAX offset "rill-PX"`) +} + func TestEval_SyntaxErrors(t *testing.T) { testCases := []struct { timeRange string