diff --git a/changelog/27656.txt b/changelog/27656.txt new file mode 100644 index 0000000000..e4445f826a --- /dev/null +++ b/changelog/27656.txt @@ -0,0 +1,3 @@ +```release-note:improvement +license utilization reporting (enterprise): Auto-roll billing start date. +``` \ No newline at end of file diff --git a/helper/timeutil/timeutil.go b/helper/timeutil/timeutil.go index eeba1797d0..30b894d9cc 100644 --- a/helper/timeutil/timeutil.go +++ b/helper/timeutil/timeutil.go @@ -179,3 +179,13 @@ func (_ DefaultClock) NewTicker(d time.Duration) *time.Ticker { func (_ DefaultClock) NewTimer(d time.Duration) *time.Timer { return time.NewTimer(d) } + +// NormalizeToYear returns date normalized to the latest date +// within one year of normal. Assumes the date argument is +// some date before normal. +func NormalizeToYear(date, normal time.Time) time.Time { + for date.AddDate(1, 0, 0).Compare(normal) <= 0 { + date = date.AddDate(1, 0, 0) + } + return date +} diff --git a/helper/timeutil/timeutil_test.go b/helper/timeutil/timeutil_test.go index df14a6fd17..7692fd280d 100644 --- a/helper/timeutil/timeutil_test.go +++ b/helper/timeutil/timeutil_test.go @@ -7,6 +7,8 @@ import ( "reflect" "testing" "time" + + "github.com/stretchr/testify/require" ) func TestTimeutil_StartOfPreviousMonth(t *testing.T) { @@ -367,3 +369,94 @@ func TestTimeUtil_ParseTimeFromPath(t *testing.T) { } } } + +// TestTimeUtil_NormalizeToYear tests NormalizeToYear function which returns the normalized input date wrt to the normal. +func TestTimeUtil_NormalizeToYear(t *testing.T) { + testCases := []struct { + inputDate time.Time + normalDate time.Time + expectedNormalizedDate time.Time + }{ + { + inputDate: time.Date(2024, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2024, 10, 1, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2024, 9, 29, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2024, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2025, 9, 29, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2025, 9, 29, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2024, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2025, 10, 1, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2025, 9, 29, 0, 0, 0, 0, time.UTC), + }, + // inputDate more than 2 years prior to normal date + { + inputDate: time.Date(2022, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2024, 6, 15, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2023, 9, 29, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2022, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2024, 9, 28, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2023, 9, 29, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2022, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2024, 9, 29, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2024, 9, 29, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2022, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2024, 9, 30, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2024, 9, 29, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2020, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2024, 12, 1, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2024, 9, 29, 0, 0, 0, 0, time.UTC), + }, + // leap year test cases + { + inputDate: time.Date(2020, 9, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2024, 9, 28, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2023, 9, 29, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2025, 2, 28, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2025, 3, 1, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2025, 3, 1, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2025, 3, 2, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2025, 3, 1, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2028, 2, 28, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2027, 3, 1, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2028, 2, 29, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2027, 3, 1, 0, 0, 0, 0, time.UTC), + }, + { + inputDate: time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC), + normalDate: time.Date(2028, 3, 1, 0, 0, 0, 0, time.UTC), + expectedNormalizedDate: time.Date(2028, 3, 1, 0, 0, 0, 0, time.UTC), + }, + } + for _, tc := range testCases { + normalizedDate := NormalizeToYear(tc.inputDate, tc.normalDate) + require.Equal(t, tc.expectedNormalizedDate, normalizedDate) + } +}