diff --git a/site/src/pages/AISettingsPage/SpendPage/SpendPage.tsx b/site/src/pages/AISettingsPage/SpendPage/SpendPage.tsx index e800775495..19306a7cc9 100644 --- a/site/src/pages/AISettingsPage/SpendPage/SpendPage.tsx +++ b/site/src/pages/AISettingsPage/SpendPage/SpendPage.tsx @@ -21,6 +21,7 @@ import { useAuthenticated } from "#/hooks/useAuthenticated"; import { usePaginatedQuery } from "#/hooks/usePaginatedQuery"; import { RequirePermission } from "#/modules/permissions/RequirePermission"; import { SpendPageView } from "./SpendPageView"; +import { toExclusiveEndOfDayDateRange } from "./utils/dateRange"; const startDateSearchParam = "startDate"; const endDateSearchParam = "endDate"; @@ -132,11 +133,13 @@ const SpendPage: FC = ({ now }) => { }; const onDateRangeChange = (value: DateRangeValue) => { + const nextDateRange = toExclusiveEndOfDayDateRange(value); + setSearchParams( (prev) => { const next = new URLSearchParams(prev); - next.set(startDateSearchParam, value.startDate.toISOString()); - next.set(endDateSearchParam, value.endDate.toISOString()); + next.set(startDateSearchParam, nextDateRange.startDate.toISOString()); + next.set(endDateSearchParam, nextDateRange.endDate.toISOString()); next.delete("page"); return next; }, diff --git a/site/src/pages/AISettingsPage/SpendPage/utils/dateRange.test.ts b/site/src/pages/AISettingsPage/SpendPage/utils/dateRange.test.ts index 512ac74613..3a5a98fec5 100644 --- a/site/src/pages/AISettingsPage/SpendPage/utils/dateRange.test.ts +++ b/site/src/pages/AISettingsPage/SpendPage/utils/dateRange.test.ts @@ -1,5 +1,32 @@ import { describe, expect, it } from "vitest"; -import { formatUsageDateRange, toInclusiveDateRange } from "./dateRange"; +import { + formatUsageDateRange, + toExclusiveEndOfDayDateRange, + toInclusiveDateRange, +} from "./dateRange"; + +describe("toExclusiveEndOfDayDateRange", () => { + it("moves a non-midnight end date to the next midnight", () => { + const startDate = new Date(2025, 5, 1, 0, 0, 0, 0); + const endDate = new Date(2025, 5, 8, 14, 30, 0, 0); + const result = toExclusiveEndOfDayDateRange({ startDate, endDate }); + expect(result.endDate).toEqual(new Date(2025, 5, 9, 0, 0, 0, 0)); + }); + + it("returns unchanged when the end date is already midnight", () => { + const startDate = new Date(2025, 5, 1, 0, 0, 0, 0); + const endDate = new Date(2025, 5, 8, 0, 0, 0, 0); + const result = toExclusiveEndOfDayDateRange({ startDate, endDate }); + expect(result.endDate).toBe(endDate); + }); + + it("preserves startDate", () => { + const startDate = new Date(2025, 5, 1, 0, 0, 0, 0); + const endDate = new Date(2025, 5, 8, 14, 30, 0, 0); + const result = toExclusiveEndOfDayDateRange({ startDate, endDate }); + expect(result.startDate).toBe(startDate); + }); +}); describe("toInclusiveDateRange", () => { it("subtracts 1ms when endDateIsExclusive is true and end date is midnight", () => { diff --git a/site/src/pages/AISettingsPage/SpendPage/utils/dateRange.ts b/site/src/pages/AISettingsPage/SpendPage/utils/dateRange.ts index 0bde5a85f3..e25e22a814 100644 --- a/site/src/pages/AISettingsPage/SpendPage/utils/dateRange.ts +++ b/site/src/pages/AISettingsPage/SpendPage/utils/dateRange.ts @@ -16,6 +16,19 @@ function isMidnight(date: Date): boolean { ); } +export function toExclusiveEndOfDayDateRange( + dateRange: DateRangeValue, +): DateRangeValue { + if (isMidnight(dateRange.endDate)) { + return dateRange; + } + + return { + startDate: dateRange.startDate, + endDate: dayjs(dateRange.endDate).startOf("day").add(1, "day").toDate(), + }; +} + /** * When the user picks an explicit date range whose end boundary is * midnight of the following day, adjust it by −1 ms so the