From db54213d56e37233a97f8d224c66e110f8b8fb58 Mon Sep 17 00:00:00 2001 From: kevin9327 <5299031+kevin9327@users.noreply.github.com> Date: Sun, 18 Oct 2026 07:47:23 +0900 Subject: [PATCH] Describe a repeated minute in a routine's schedule once --- CHANGELOG.md | 6 ++++++ server/src/routines/schedule.ts | 4 +++- server/tests/routine-schedule.test.ts | 6 ++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 123213f78..aea01132d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,12 @@ Newest first. `Unreleased` is what is on `main` and not yet tagged. `code_required` (below). A server and a computer must be upgraded together: an older computer has no `/sign-in/code`. +### A repeated minute in a routine's schedule is described once + +A schedule like `0,30,0 9 * * *` fires at 09:00 and 09:30, but the routines page and the model were +told "Every day at 09:00, 09:00 and 09:30". Repeated minutes are now said once, as repeated weekdays +already were. + ### The Bot's screen opens only when there is something to watch Asking a Bot a question it answered in words could still open its computer beside the conversation, diff --git a/server/src/routines/schedule.ts b/server/src/routines/schedule.ts index 7fae13c7c..dfa117359 100644 --- a/server/src/routines/schedule.ts +++ b/server/src/routines/schedule.ts @@ -298,7 +298,9 @@ export function describeCron(cron: string): string { .split(",") .map((part) => parsePlainInt(part, 0, 59)); if (minutes.every((value): value is number => value !== null)) { - const times = [...minutes] + // De-duplicated like `weekdays`: cron fires once for `0,0`, so it must not read as + // "09:00 and 09:00". + const times = [...new Set(minutes)] .sort((a, b) => a - b) .map((value) => `${pad2(hour)}:${pad2(value)}`); return `Every day at ${joinWords(times)}`; diff --git a/server/tests/routine-schedule.test.ts b/server/tests/routine-schedule.test.ts index 8f42bdf2b..6b84d10fb 100644 --- a/server/tests/routine-schedule.test.ts +++ b/server/tests/routine-schedule.test.ts @@ -182,6 +182,12 @@ describe("describeCron", () => { expect(describeCron("0,30 9 * * *")).toBe("Every day at 09:00 and 09:30"); }); + // cron fires once per distinct minute, so a repeated minute must not be described twice. + test("a repeated minute in the list is described once", () => { + expect(describeCron("0,30,0 9 * * *")).toBe("Every day at 09:00 and 09:30"); + expect(describeCron("15,15 9 * * *")).toBe("Every day at 09:15"); + }); + test("falls through to the raw expression when it is stranger than words", () => { expect(describeCron("*/7 3,4 * * *")).toBe("*/7 3,4 * * *"); });