
















@@ -28,6 +28,63 @@ import { findPreviousSessionFile, getRecentSessionContentWithResetFallback } fro
28282929const log = createSubsystemLogger("hooks/session-memory");
303031+function pickDateTimePart(
32+parts: Intl.DateTimeFormatPart[],
33+type: Intl.DateTimeFormatPartTypes,
34+): string | undefined {
35+return parts.find((part) => part.type === type)?.value;
36+}
37+38+function resolveLocalTimeZone(): string | undefined {
39+const timeZone = process.env.TZ?.trim();
40+if (!timeZone) {
41+return undefined;
42+}
43+try {
44+new Intl.DateTimeFormat("en-US", { timeZone }).format(new Date());
45+return timeZone;
46+} catch {
47+return undefined;
48+}
49+}
50+51+function formatLocalSessionTimestamp(date: Date): {
52+date: string;
53+time: string;
54+timeSlug: string;
55+timeZoneName?: string;
56+} {
57+const parts = new Intl.DateTimeFormat("en-US", {
58+timeZone: resolveLocalTimeZone(),
59+year: "numeric",
60+month: "2-digit",
61+day: "2-digit",
62+hour: "2-digit",
63+minute: "2-digit",
64+second: "2-digit",
65+hourCycle: "h23",
66+timeZoneName: "short",
67+}).formatToParts(date);
68+69+const year = pickDateTimePart(parts, "year") ?? String(date.getFullYear()).padStart(4, "0");
70+const month = pickDateTimePart(parts, "month") ?? String(date.getMonth() + 1).padStart(2, "0");
71+const day = pickDateTimePart(parts, "day") ?? String(date.getDate()).padStart(2, "0");
72+const hour = pickDateTimePart(parts, "hour") ?? String(date.getHours()).padStart(2, "0");
73+const minute = pickDateTimePart(parts, "minute") ?? String(date.getMinutes()).padStart(2, "0");
74+const second = pickDateTimePart(parts, "second") ?? String(date.getSeconds()).padStart(2, "0");
75+const timeZoneName = [...parts]
76+.toReversed()
77+.find((part) => part.type === "timeZoneName")
78+?.value?.trim();
79+80+return {
81+date: `${year}-${month}-${day}`,
82+time: `${hour}:${minute}:${second}`,
83+timeSlug: `${hour}${minute}`,
84+ timeZoneName,
85+};
86+}
87+3188function resolveDisplaySessionKey(params: {
3289cfg?: OpenClawConfig;
3390workspaceDir?: string;
@@ -80,9 +137,10 @@ const saveSessionToMemory: HookHandler = async (event) => {
80137const memoryDir = path.join(workspaceDir, "memory");
81138await fs.mkdir(memoryDir, { recursive: true });
8213983-// Get today's date for filename
140+// Use the user's local timezone for memory artifact names and headings.
84141const now = new Date(event.timestamp);
85-const dateStr = now.toISOString().split("T")[0]; // YYYY-MM-DD
142+const localTimestamp = formatLocalSessionTimestamp(now);
143+const dateStr = localTimestamp.date;
8614487145// Generate descriptive slug from session using LLM
88146// Prefer previousSessionEntry (old session before /new) over current (which may be empty)
@@ -160,8 +218,7 @@ const saveSessionToMemory: HookHandler = async (event) => {
160218161219// If no slug, use timestamp
162220if (!slug) {
163-const timeSlug = now.toISOString().split("T")[1].split(".")[0].replace(/:/g, "");
164-slug = timeSlug.slice(0, 4); // HHMM
221+slug = localTimestamp.timeSlug;
165222log.debug("Using fallback timestamp slug", { slug });
166223}
167224@@ -173,16 +230,16 @@ const saveSessionToMemory: HookHandler = async (event) => {
173230path: memoryFilePath.replace(os.homedir(), "~"),
174231});
175232176-// Format time as HH:MM:SS UTC
177-const timeStr = now.toISOString().split("T")[1].split(".")[0];
233+const timeStr = localTimestamp.time;
234+const timeZoneSuffix = localTimestamp.timeZoneName ? ` ${localTimestamp.timeZoneName}` : "";
178235179236// Extract context details
180237const sessionId = (sessionEntry.sessionId as string) || "unknown";
181238const source = (context.commandSource as string) || "unknown";
182239183240// Build Markdown entry
184241const entryParts = [
185-`# Session: ${dateStr} ${timeStr} UTC`,
242+`# Session: ${dateStr} ${timeStr}${timeZoneSuffix}`,
186243"",
187244`- **Session Key**: ${displaySessionKey}`,
188245`- **Session ID**: ${sessionId}`,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。