Ver código fonte

Use existing RoundToStartOfWeek instead of adding RoundToStartOfWeekMonday

Per review feedback, fold weekly step-boundary alignment onto the
existing timeutil.RoundToStartOfWeek helper (preceding Sunday 00:00 UTC)
rather than introducing a new Monday-based helper. The behavior change
is the same in spirit: callers with step=1w get calendar-week buckets
instead of arbitrary Tue-to-Tue or similar buckets driven by the
request's window start.

Removes:
  - timeutil.RoundToStartOfWeekMonday and its tests.

Updates:
  - alignStepStart now calls timeutil.RoundToStartOfWeek for step=1w.
  - TestAlignStepStart reworked to cover the Sunday-based expectations,
    including Monday-starts (which now round back one day), Sunday
    unchanged, Tuesday mid-day rounded to preceding Sunday midnight, and
    the unchanged non-weekly step cases.

Signed-off-by: Cursor Agent <cursor@opencost.io>

Co-authored-by: Alex Meijer <ameijer@users.noreply.github.com>
Cursor Agent 4 semanas atrás
pai
commit
0707c0d4c1

+ 0 - 12
core/pkg/util/timeutil/timeutil.go

@@ -323,18 +323,6 @@ func RoundToStartOfFollowingWeek(t time.Time) time.Time {
 	return date.Add(time.Duration(daysFromSunday) * Day)
 }
 
-// RoundToStartOfWeekMonday creates a new time.Time for the preceding Monday 00:00
-// in the given time's timezone. If the given time is already Monday at 00:00,
-// it is returned unchanged.
-func RoundToStartOfWeekMonday(t time.Time) time.Time {
-	date := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
-	// time.Weekday: Sunday=0, Monday=1, ..., Saturday=6
-	// Days to subtract to reach Monday:
-	//   Mon -> 0, Tue -> 1, Wed -> 2, ..., Sun -> 6
-	daysFromMonday := (int(date.Weekday()) + 6) % 7
-	return date.Add(-1 * time.Duration(daysFromMonday) * Day)
-}
-
 // JobTicker is a ticker used to synchronize the next run of a repeating
 // process. The designated use-case is for infinitely-looping selects,
 // where a timeout or an exit channel might cancel the process, but otherwise

+ 0 - 63
core/pkg/util/timeutil/timeutil_test.go

@@ -416,66 +416,3 @@ func TestRoundToStartOfFollowingWeek(t *testing.T) {
 		t.Errorf("expected date to be rounded to the same sunday, got: %d, %s", roundedFromTuesday.Day(), roundedFromTuesday.Weekday().String())
 	}
 }
-
-func TestRoundToStartOfWeekMonday(t *testing.T) {
-	testCases := map[string]struct {
-		input    time.Time
-		expected time.Time
-	}{
-		"monday at midnight UTC is unchanged": {
-			input:    time.Date(2026, time.April, 13, 0, 0, 0, 0, time.UTC),
-			expected: time.Date(2026, time.April, 13, 0, 0, 0, 0, time.UTC),
-		},
-		"monday mid-day rounds to start of same day": {
-			input:    time.Date(2026, time.April, 13, 12, 34, 56, 0, time.UTC),
-			expected: time.Date(2026, time.April, 13, 0, 0, 0, 0, time.UTC),
-		},
-		"tuesday rounds back to preceding monday": {
-			input:    time.Date(2026, time.April, 7, 0, 0, 0, 0, time.UTC),
-			expected: time.Date(2026, time.April, 6, 0, 0, 0, 0, time.UTC),
-		},
-		"wednesday rounds back to preceding monday": {
-			input:    time.Date(2026, time.April, 22, 23, 59, 59, 0, time.UTC),
-			expected: time.Date(2026, time.April, 20, 0, 0, 0, 0, time.UTC),
-		},
-		"saturday rounds back to preceding monday": {
-			input:    time.Date(2026, time.April, 11, 12, 0, 0, 0, time.UTC),
-			expected: time.Date(2026, time.April, 6, 0, 0, 0, 0, time.UTC),
-		},
-		"sunday rounds back six days to preceding monday": {
-			input:    time.Date(2026, time.April, 12, 12, 0, 0, 0, time.UTC),
-			expected: time.Date(2026, time.April, 6, 0, 0, 0, 0, time.UTC),
-		},
-		"rounding back across a month boundary": {
-			input:    time.Date(2026, time.May, 2, 10, 0, 0, 0, time.UTC),
-			expected: time.Date(2026, time.April, 27, 0, 0, 0, 0, time.UTC),
-		},
-	}
-
-	for name, tc := range testCases {
-		t.Run(name, func(t *testing.T) {
-			got := RoundToStartOfWeekMonday(tc.input)
-			if !got.Equal(tc.expected) {
-				t.Errorf("RoundToStartOfWeekMonday(%s) = %s; want %s", tc.input, got, tc.expected)
-			}
-			if got.Weekday() != time.Monday {
-				t.Errorf("RoundToStartOfWeekMonday(%s).Weekday() = %s; want Monday", tc.input, got.Weekday())
-			}
-		})
-	}
-}
-
-func TestRoundToStartOfWeekMonday_PreservesLocation(t *testing.T) {
-	// The helper rounds in the input's timezone so that callers using local
-	// time windows observe a local-Monday boundary.
-	boulder := time.FixedZone("Boulder", -7*60*60)
-	in := time.Date(2026, time.April, 7, 1, 30, 0, 0, boulder)
-	got := RoundToStartOfWeekMonday(in)
-	want := time.Date(2026, time.April, 6, 0, 0, 0, 0, boulder)
-	if !got.Equal(want) {
-		t.Errorf("RoundToStartOfWeekMonday(%s) = %s; want %s", in, got, want)
-	}
-	if got.Location() != boulder {
-		t.Errorf("expected location to be preserved; got %s", got.Location())
-	}
-}

+ 7 - 7
pkg/costmodel/step_align.go

@@ -11,16 +11,16 @@ import (
 // arbitrary offsets from the request's start time.
 //
 // This is currently used only for the weekly step, which is rolled back to
-// 00:00 on the preceding Monday. Other step durations are returned unchanged.
+// 00:00 UTC on the preceding Sunday via timeutil.RoundToStartOfWeek. Other
+// step durations are returned unchanged.
 //
-// For example, a request with a window starting on Tuesday and step=1w would,
-// without alignment, produce buckets that run Tuesday-to-Tuesday. With
-// alignment, the first bucket begins at the preceding Monday 00:00, which
-// matches the calendar week most users expect and avoids an initial bucket
-// that only covers a partial week of data.
+// For example, a request with a window starting mid-week and step=1w would,
+// without alignment, produce buckets that do not correspond to a calendar
+// week. With alignment, the first bucket begins at the preceding Sunday
+// 00:00 UTC, so subsequent bucket boundaries fall on Sunday 00:00 UTC.
 func alignStepStart(start time.Time, step time.Duration) time.Time {
 	if step == timeutil.Week {
-		return timeutil.RoundToStartOfWeekMonday(start)
+		return timeutil.RoundToStartOfWeek(start)
 	}
 	return start
 }

+ 15 - 9
pkg/costmodel/step_align_test.go

@@ -9,10 +9,11 @@ import (
 
 func TestAlignStepStart(t *testing.T) {
 	// Reference dates all in April 2026:
-	//   Mon Apr 6, Tue Apr 7, Wed Apr 8, ..., Sun Apr 12
-	//   Mon Apr 13, Tue Apr 14
+	//   Sun Apr 5, Mon Apr 6, Tue Apr 7, ..., Sat Apr 11
+	//   Sun Apr 12, Mon Apr 13, Tue Apr 14
+	sundayApr5 := time.Date(2026, time.April, 5, 0, 0, 0, 0, time.UTC)
+	sundayApr12 := time.Date(2026, time.April, 12, 0, 0, 0, 0, time.UTC)
 	tuesdayApr7 := time.Date(2026, time.April, 7, 0, 0, 0, 0, time.UTC)
-	mondayApr6 := time.Date(2026, time.April, 6, 0, 0, 0, 0, time.UTC)
 	mondayApr13 := time.Date(2026, time.April, 13, 0, 0, 0, 0, time.UTC)
 	tuesdayApr14MidDay := time.Date(2026, time.April, 14, 12, 30, 0, 0, time.UTC)
 
@@ -21,20 +22,25 @@ func TestAlignStepStart(t *testing.T) {
 		step     time.Duration
 		expected time.Time
 	}{
-		"weekly step from Tuesday aligns back to preceding Monday": {
+		"weekly step from Sunday is unchanged": {
+			start:    sundayApr5,
+			step:     timeutil.Week,
+			expected: sundayApr5,
+		},
+		"weekly step from Tuesday aligns back to preceding Sunday": {
 			start:    tuesdayApr7,
 			step:     timeutil.Week,
-			expected: mondayApr6,
+			expected: sundayApr5,
 		},
-		"weekly step from Monday is unchanged": {
+		"weekly step from Monday aligns back one day to preceding Sunday": {
 			start:    mondayApr13,
 			step:     timeutil.Week,
-			expected: mondayApr13,
+			expected: sundayApr12,
 		},
-		"weekly step from Tuesday mid-day aligns to preceding Monday midnight": {
+		"weekly step from Tuesday mid-day aligns to preceding Sunday midnight": {
 			start:    tuesdayApr14MidDay,
 			step:     timeutil.Week,
-			expected: mondayApr13,
+			expected: sundayApr12,
 		},
 		"daily step is unchanged on a Tuesday": {
 			start:    tuesdayApr7,