mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
fix(coach): count the daily question limit from the person's midnight
The limit on clarifying questions reset at UTC midnight, which is the middle of the night in Europe and the afternoon on the American west coast. It now counts from the start of the person's own day.
This commit is contained in:
1 parent
f4262e2cc6
commit
2372fee376
2 files changed
+36
-3
No files matched your search
@@ -229,6 +229,32 @@ describe("clarificationAllowed", () => {
|
||||
).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it("counts the day in the person's zone, not from UTC midnight", async () => {
|
||||
// 23:30 UTC on 1 March is already 2 March in Auckland and still 1 March
|
||||
// in Los Angeles; each day starts at its own local midnight.
|
||||
const now = new Date("2026-03-01T23:30:00.000Z");
|
||||
await clarificationAllowed({
|
||||
userId: "u1",
|
||||
conversationId: "c1",
|
||||
now,
|
||||
tz: "Pacific/Auckland",
|
||||
});
|
||||
await clarificationAllowed({
|
||||
userId: "u1",
|
||||
conversationId: "c1",
|
||||
now,
|
||||
tz: "America/Los_Angeles",
|
||||
});
|
||||
const since = db.messageCount.mock.calls.map(
|
||||
([q]) =>
|
||||
(q as { where: { createdAt: { gte: Date } } }).where.createdAt.gte,
|
||||
);
|
||||
expect(since).toEqual([
|
||||
new Date("2026-03-01T11:00:00.000Z"),
|
||||
new Date("2026-03-01T08:00:00.000Z"),
|
||||
]);
|
||||
});
|
||||
|
||||
it("refuses when the count cannot be read", async () => {
|
||||
db.messageFindMany.mockRejectedValue(new Error("down"));
|
||||
await expect(
|
||||
|
||||
@@ -30,6 +30,9 @@ import {
|
||||
type LatestMessagesLoader,
|
||||
} from "@/lib/ai/coach/latest-messages";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { startOfLocalDayKey } from "@/lib/tz/local-day";
|
||||
import { userDayKey } from "@/lib/tz/format";
|
||||
import { resolveUserTimezone } from "@/lib/tz/resolver";
|
||||
import { annotate } from "@/lib/logging/context";
|
||||
import type { Locale } from "@/lib/i18n/config";
|
||||
import { resolveIntlLocale } from "@/lib/format-locale";
|
||||
@@ -704,6 +707,8 @@ export async function clarificationAllowed(args: {
|
||||
userId: string;
|
||||
conversationId: string;
|
||||
now?: Date;
|
||||
/** The person's zone; resolved from the account when absent. */
|
||||
tz?: string;
|
||||
}): Promise<boolean> {
|
||||
const now = args.now ?? new Date();
|
||||
try {
|
||||
@@ -726,9 +731,11 @@ export async function clarificationAllowed(args: {
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
const dayStart = new Date(
|
||||
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()),
|
||||
);
|
||||
// The person's day, not the server's: a limit that reset at UTC
|
||||
// midnight reset at one or two in the morning in Berlin and in the
|
||||
// afternoon in the Pacific.
|
||||
const tz = args.tz ?? (await resolveUserTimezone(args.userId));
|
||||
const dayStart = startOfLocalDayKey(userDayKey(now, tz), tz);
|
||||
const today = await prisma.coachMessage.count({
|
||||
where: {
|
||||
conversation: { userId: args.userId },
|
||||
|
||||
Reference in new issue
Block a user