diff --git a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailDetail.integration.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailDetail.integration.test.tsx index 6bbdf299c64..32f2998ee7e 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailDetail.integration.test.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/guardrails-monitor/_components/GuardrailDetail.integration.test.tsx @@ -51,8 +51,8 @@ describe("GuardrailDetail sends the viewer's local day as instants", () => { const params = usageLogsQuery(); // 00:00 and 23:59:59 on 2026-08-10 in IST, expressed as instants. - expect(params?.get("start_date")).toBe("2026-08-09T18:30:00Z"); - expect(params?.get("end_date")).toBe("2026-08-10T18:29:59Z"); + expect(params?.get("start_date")).toBe("2026-08-09T18:30:00.000Z"); + expect(params?.get("end_date")).toBe("2026-08-10T18:29:59.999Z"); }); it("never sends a bare calendar date, which the endpoint would read as UTC", async () => { diff --git a/ui/litellm-dashboard/src/components/GuardrailsMonitor/guardrailLogsWindow.test.ts b/ui/litellm-dashboard/src/components/GuardrailsMonitor/guardrailLogsWindow.test.ts index 433e74e2817..c86c903dd57 100644 --- a/ui/litellm-dashboard/src/components/GuardrailsMonitor/guardrailLogsWindow.test.ts +++ b/ui/litellm-dashboard/src/components/GuardrailsMonitor/guardrailLogsWindow.test.ts @@ -20,8 +20,8 @@ describe("toUtcInstantRange, east of UTC (Asia/Kolkata, +05:30)", () => { it("resolves the local day to instants rather than the UTC day", () => { expect(toUtcInstantRange("2026-08-10", "2026-08-10")).toEqual({ - start: "2026-08-09T18:30:00Z", - end: "2026-08-10T18:29:59Z", + start: "2026-08-09T18:30:00.000Z", + end: "2026-08-10T18:29:59.999Z", }); }); @@ -46,10 +46,16 @@ describe("toUtcInstantRange, east of UTC (Asia/Kolkata, +05:30)", () => { expect(Date.parse(end)).toBeGreaterThan(Date.parse("2026-08-10T12:00:00Z")); }); + it("keeps a row in the final millisecond of the local day", () => { + const { end } = toUtcInstantRange("2026-08-10", "2026-08-10"); + // 23:59:59.500 local. Truncating the inclusive bound to whole seconds drops it. + expect(Date.parse(end)).toBeGreaterThanOrEqual(Date.parse("2026-08-10T18:29:59.500Z")); + }); + it("spans a multi-day range end to end", () => { expect(toUtcInstantRange("2026-08-03", "2026-08-10")).toEqual({ - start: "2026-08-02T18:30:00Z", - end: "2026-08-10T18:29:59Z", + start: "2026-08-02T18:30:00.000Z", + end: "2026-08-10T18:29:59.999Z", }); }); }); @@ -59,8 +65,8 @@ describe("toUtcInstantRange, west of UTC (America/Los_Angeles, -07:00 in August) it("resolves the local day to instants rather than the UTC day", () => { expect(toUtcInstantRange("2026-08-10", "2026-08-10")).toEqual({ - start: "2026-08-10T07:00:00Z", - end: "2026-08-11T06:59:59Z", + start: "2026-08-10T07:00:00.000Z", + end: "2026-08-11T06:59:59.999Z", }); }); @@ -80,8 +86,8 @@ describe("toUtcInstantRange, on UTC", () => { it("matches the day the endpoint would have assumed", () => { expect(toUtcInstantRange("2026-08-10", "2026-08-10")).toEqual({ - start: "2026-08-10T00:00:00Z", - end: "2026-08-10T23:59:59Z", + start: "2026-08-10T00:00:00.000Z", + end: "2026-08-10T23:59:59.999Z", }); }); }); diff --git a/ui/litellm-dashboard/src/components/GuardrailsMonitor/guardrailLogsWindow.ts b/ui/litellm-dashboard/src/components/GuardrailsMonitor/guardrailLogsWindow.ts index b519b268f83..f75edf44c26 100644 --- a/ui/litellm-dashboard/src/components/GuardrailsMonitor/guardrailLogsWindow.ts +++ b/ui/litellm-dashboard/src/components/GuardrailsMonitor/guardrailLogsWindow.ts @@ -5,14 +5,14 @@ export interface UtcInstantRange { end: string; } +const UTC_INSTANT = "YYYY-MM-DDTHH:mm:ss.SSS[Z]"; + /** - * `/guardrails/usage/logs` filters `start_time`, a real timestamp column, but pads a - * bare `YYYY-MM-DD` out to `T00:00:00+00:00` / `T23:59:59+00:00`. Sending the picker's - * local calendar date therefore shifts the window by the viewer's UTC offset. Resolving - * the local day here and sending instants keeps the bound exact; the endpoint already - * parses a value carrying a `T`. + * `/guardrails/usage/logs` pads a bare `YYYY-MM-DD` to a whole UTC day, so the picker's + * local date lands the viewer's offset away from the range they chose. Milliseconds are + * kept because the inclusive end bound otherwise drops the final 999ms of that day. */ export const toUtcInstantRange = (startDate: string, endDate: string): UtcInstantRange => ({ - start: moment(startDate).startOf("day").utc().format(), - end: moment(endDate).endOf("day").utc().format(), + start: moment(startDate).startOf("day").utc().format(UTC_INSTANT), + end: moment(endDate).endOf("day").utc().format(UTC_INSTANT), });