From aad372e15fb95f5b1e914465f16e302a5d724799 Mon Sep 17 00:00:00 2001 From: Jacob Riff Date: Thu, 5 Mar 2026 14:26:34 -0800 Subject: [PATCH] feat: append UTC time alongside local time in shared Current time lines (#32423) Merged via squash. Prepared head SHA: 9e8ec13933b5317e7cff3f0bc048de515826c31a Co-authored-by: jriff <50276+jriff@users.noreply.github.com> Co-authored-by: altaywtf <9790196+altaywtf@users.noreply.github.com> Reviewed-by: @altaywtf --- CHANGELOG.md | 1 + src/agents/current-time.ts | 3 ++- src/auto-reply/reply/memory-flush.test.ts | 5 +++-- src/auto-reply/reply/post-compaction-context.test.ts | 7 ++++--- src/auto-reply/reply/session-reset-prompt.test.ts | 7 ++++--- ...solated-agent.uses-last-non-empty-agent-text-as.test.ts | 2 +- 6 files changed, 15 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 292984d5f9a..9d853a3e0ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -140,6 +140,7 @@ Docs: https://docs.openclaw.ai - Agents/failover cooldown classification: stop treating generic `cooling down` text as provider `rate_limit` so healthy models no longer show false global cooldown/rate-limit warnings while explicit `model_cooldown` markers still trigger failover. (#32972) thanks @stakeswky. - Agents/failover service-unavailable handling: stop treating bare proxy/CDN `service unavailable` errors as provider overload while keeping them retryable via the timeout/failover path, so transient outages no longer show false rate-limit warnings or block fallback. (#36646) thanks @jnMetaCode. +- Agents/current-time UTC anchor: append a machine-readable UTC suffix alongside local `Current time:` lines in shared cron-style prompt contexts so agents can compare UTC-stamped workspace timestamps without doing timezone math. (#32423) thanks @jriff. ## 2026.3.2 diff --git a/src/agents/current-time.ts b/src/agents/current-time.ts index b1f13512e71..b98b8594669 100644 --- a/src/agents/current-time.ts +++ b/src/agents/current-time.ts @@ -25,7 +25,8 @@ export function resolveCronStyleNow(cfg: TimeConfigLike, nowMs: number): CronSty const userTimeFormat = resolveUserTimeFormat(cfg.agents?.defaults?.timeFormat); const formattedTime = formatUserTime(new Date(nowMs), userTimezone, userTimeFormat) ?? new Date(nowMs).toISOString(); - const timeLine = `Current time: ${formattedTime} (${userTimezone})`; + const utcTime = new Date(nowMs).toISOString().replace("T", " ").slice(0, 16) + " UTC"; + const timeLine = `Current time: ${formattedTime} (${userTimezone}) / ${utcTime}`; return { userTimezone, formattedTime, timeLine }; } diff --git a/src/auto-reply/reply/memory-flush.test.ts b/src/auto-reply/reply/memory-flush.test.ts index e5905e677cf..e444b9e7a80 100644 --- a/src/auto-reply/reply/memory-flush.test.ts +++ b/src/auto-reply/reply/memory-flush.test.ts @@ -20,8 +20,9 @@ describe("resolveMemoryFlushPromptForRun", () => { }); expect(prompt).toContain("memory/2026-02-16.md"); - expect(prompt).toContain("Current time:"); - expect(prompt).toContain("(America/New_York)"); + expect(prompt).toContain( + "Current time: Monday, February 16th, 2026 — 10:00 AM (America/New_York) / 2026-02-16 15:00 UTC", + ); }); it("does not append a duplicate current time line", () => { diff --git a/src/auto-reply/reply/post-compaction-context.test.ts b/src/auto-reply/reply/post-compaction-context.test.ts index 9091548f161..34da43f2e7e 100644 --- a/src/auto-reply/reply/post-compaction-context.test.ts +++ b/src/auto-reply/reply/post-compaction-context.test.ts @@ -203,7 +203,7 @@ Never modify memory/YYYY-MM-DD.md destructively. `; fs.writeFileSync(path.join(tmpDir, "AGENTS.md"), content); const cfg = { - agents: { defaults: { userTimezone: "America/New_York" } }, + agents: { defaults: { userTimezone: "America/New_York", timeFormat: "12" } }, } as OpenClawConfig; // 2026-03-03 14:00 UTC = 2026-03-03 09:00 EST const nowMs = Date.UTC(2026, 2, 3, 14, 0, 0); @@ -211,8 +211,9 @@ Never modify memory/YYYY-MM-DD.md destructively. expect(result).not.toBeNull(); expect(result).toContain("memory/2026-03-03.md"); expect(result).not.toContain("memory/YYYY-MM-DD.md"); - expect(result).toContain("Current time:"); - expect(result).toContain("America/New_York"); + expect(result).toContain( + "Current time: Tuesday, March 3rd, 2026 — 9:00 AM (America/New_York) / 2026-03-03 14:00 UTC", + ); }); it("appends current time line even when no YYYY-MM-DD placeholder is present", async () => { diff --git a/src/auto-reply/reply/session-reset-prompt.test.ts b/src/auto-reply/reply/session-reset-prompt.test.ts index 30976fae024..c6a1d2d9562 100644 --- a/src/auto-reply/reply/session-reset-prompt.test.ts +++ b/src/auto-reply/reply/session-reset-prompt.test.ts @@ -11,13 +11,14 @@ describe("buildBareSessionResetPrompt", () => { it("appends current time line so agents know the date", () => { const cfg = { - agents: { defaults: { userTimezone: "America/New_York" } }, + agents: { defaults: { userTimezone: "America/New_York", timeFormat: "12" } }, } as OpenClawConfig; // 2026-03-03 14:00 UTC = 2026-03-03 09:00 EST const nowMs = Date.UTC(2026, 2, 3, 14, 0, 0); const prompt = buildBareSessionResetPrompt(cfg, nowMs); - expect(prompt).toContain("Current time:"); - expect(prompt).toContain("America/New_York"); + expect(prompt).toContain( + "Current time: Tuesday, March 3rd, 2026 — 9:00 AM (America/New_York) / 2026-03-03 14:00 UTC", + ); }); it("does not append a duplicate current time line", () => { diff --git a/src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts b/src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts index bd6f937ff7e..2ef6df271d5 100644 --- a/src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts +++ b/src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts @@ -354,7 +354,7 @@ describe("runCronIsolatedAgentTurn", () => { const lines = call?.prompt?.split("\n") ?? []; expect(lines[0]).toContain("[cron:job-1"); expect(lines[0]).toContain("do it"); - expect(lines[1]).toMatch(/^Current time: .+ \(.+\)$/); + expect(lines[1]).toMatch(/^Current time: .+ \(.+\) \/ \d{4}-\d{2}-\d{2} \d{2}:\d{2} UTC$/); }); });