[Feature Request] 支持用户自定义时区配置,修复 L1 提取和召回中的 UTC/本地时区混用问题
问题描述
记忆召回时,下游模型(OpenClaw / Hermes)看到的记忆时间戳混用了 UTC 和本地时区两种格式,且无任何时区标记,导致下游模型在做时间推算时频繁出错(例如把 UTC 时间当成东八区时间计算时间差、生成错误的时间叙述)。
根因分析
问题出在 L1 提取阶段的 prompt builder。
src/core/prompts/l1-extraction.ts 第 120 行和第 125 行的 formatExtractionPrompt 函数:
`[${m.id}] [${m.role}] [${new Date(m.timestamp).toISOString()}]: ${m.content}`
toISOString() 永远输出 UTC(带 Z 后缀),所以 L1 提取模型看到的原始消息时间戳格式为 2026-04-07T03:04:45Z。
而 system prompt 要求模型"基于消息的 timestamp 推算绝对时间,在 metadata 中输出 activity_start_time 和 activity_end_time(ISO 8601格式)"。提取模型直接把看到的 UTC 时间原样写入 metadata 存入数据库。
下游召回时(src/core/hooks/auto-recall.ts 第 687-699 行),从数据库取出这些 UTC 时间,经过 formatTimestamp 函数格式化为 YYYY-MM-DD HH:MM(无时区标记),拼到记忆正文的 (活动时间: ...) 里。下游模型就当成用户本地时区了。
错误链条:
L1 prompt builder 注入 UTC → 提取模型写入 UTC metadata →
召回时格式化掉时区标记 → 下游模型误认为本地时区 → 时间计算错误
其他 toISOString() 调用点(均输出 UTC,可能产生类似问题):
| 文件 |
行号 |
用途 |
src/core/scene/scene-extractor.ts |
438 |
formatTimestamp 函数 |
src/core/record/l1-writer.ts |
163 |
L1 写入时间记录 |
src/core/hooks/auto-capture.ts |
175 |
自动捕获时间记录 |
src/core/conversation/l0-recorder.ts |
171, 271, 374 |
L0 对话记录 |
src/core/store/tcvdb.ts |
98 |
云数据库存储 |
src/core/report/reporter.ts |
75 |
指标上报 |
src/utils/pipeline-manager.ts |
925-926 |
管线调度状态 |
src/utils/backup.ts |
53, 90, 108 |
备份文件命名 |
建议修复方案
新增用户时区配置项(建议命名为 timezone),默认值按服务器时区或 "Asia/Shanghai":
{
"timezone": "Asia/Shanghai"
}
具体改动:
-
openclaw.plugin.json 的 configSchema:新增 timezone 字段,类型 string,描述为用户所在 IANA 时区
-
src/core/prompts/l1-extraction.ts:formatExtractionPrompt 中的 toISOString() 替换为按配置时区格式化的函数(Node.js 可用 Intl.DateTimeFormat({ timeZone: config.timezone })),确保注入给 L1 提取模型的消息时间戳始终是用户时区
-
System prompt 更新:把"ISO 8601格式"改为明确指定用户时区,如 "YYYY-MM-DD HH:MM (时区 Asia/Shanghai)",要求提取模型按此时区输出 metadata 中的 activity_start_time / activity_end_time
-
auto-recall.ts 的 formatTimestamp 加一步兜底:即使历史数据存的是 UTC,展示时也做 UTC→用户时区换算,确保向后兼容
-
其余 toISOString() 调用点:统一替换为时区感知格式化函数(或统一用 Date.now() 毫秒值存储,仅展示层做时区换算)
影响范围: 纯格式化改动,不改变记忆数据结构、向量存储、搜索逻辑。通过第 4 步兜底,历史 UTC 数据自动兼容,无需数据迁移。
为什么这很重要
对于非 UTC 时区的用户(如中国、日本、印度等全球大多数用户),时间戳是记忆召回中最关键的结构化信息之一。时区混用会导致:
- AI 生成错误的时间叙述("昨天"变"上周"、"几分钟前"变"8 小时前")
- 定时任务/提醒基于错误时间触发
- 用户对 AI 记忆系统的信任度下降
统一时区配置可以让插件在保持全球化的同时,为每个用户提供正确的时间感知。
Issue Description (English Version)
Problem
When memories are recalled, downstream models (OpenClaw / Hermes) receive timestamps that mix UTC and local timezone formats without any timezone indicator, causing frequent time calculation errors (e.g., treating UTC timestamps as local timezone, producing incorrect time-difference narratives).
Root Cause
The issue originates in the L1 extraction prompt builder.
In src/core/prompts/l1-extraction.ts, lines 120 and 125, the formatExtractionPrompt function:
`[${m.id}] [${m.role}] [${new Date(m.timestamp).toISOString()}]: ${m.content}`
toISOString() always outputs UTC (with Z suffix), so the L1 extraction model sees timestamps like 2026-04-07T03:04:45Z.
The system prompt asks the model to "derive absolute time from message timestamps and output activity_start_time / activity_end_time in ISO 8601 format in metadata." The extraction model copies the UTC timestamps directly into metadata, which are then stored in the database.
During recall (src/core/hooks/auto-recall.ts, lines 687-699), these UTC timestamps are formatted via formatTimestamp into YYYY-MM-DD HH:MM (no timezone marker) and appended to the memory content as (活动时间: ...). The downstream model then assumes this is the user's local timezone.
Error chain:
L1 prompt builder injects UTC → extraction model writes UTC to metadata →
recall layer strips timezone indicator → downstream model assumes local timezone → time calculations fail
Proposed Fix
Add a user timezone config option (suggested name: timezone), defaulting to server timezone or "Asia/Shanghai":
{
"timezone": "Asia/Shanghai"
}
Specific changes:
-
openclaw.plugin.json configSchema: Add timezone field (string), description: user's IANA timezone
-
src/core/prompts/l1-extraction.ts: Replace toISOString() in formatExtractionPrompt with a timezone-aware formatter (Node.js Intl.DateTimeFormat({ timeZone: config.timezone })), ensuring L1 extraction models always receive timestamps in the user's timezone
-
Update system prompt: Change "ISO 8601 format" to explicitly specify the user's timezone, e.g., "YYYY-MM-DD HH:MM (timezone Asia/Shanghai)", instructing the extraction model to output metadata in this timezone
-
Add fallback in auto-recall.ts formatTimestamp: Even for historical UTC data, convert to user timezone at display time for backward compatibility
-
Replace all other toISOString() calls: Unify with a timezone-aware formatter (or store as Date.now() milliseconds and convert only at display layer)
Impact: Purely formatting changes — no modifications to memory data structures, vector storage, or search logic. Step 4 ensures backward compatibility with historical UTC data; no data migration required.
Why This Matters
For users outside the UTC timezone (China, Japan, India, and most of the world), timestamps are one of the most critical structured fields in memory recall. Timezone mixing causes:
- AI to generate incorrect temporal narratives ("yesterday" becomes "last week", "a few minutes ago" becomes "8 hours ago")
- Scheduled tasks/reminders triggering at wrong times
- Degraded user trust in the memory system
A unified timezone config allows the plugin to remain globally usable while providing each user with correct time awareness.
[Feature Request] 支持用户自定义时区配置,修复 L1 提取和召回中的 UTC/本地时区混用问题
问题描述
记忆召回时,下游模型(OpenClaw / Hermes)看到的记忆时间戳混用了 UTC 和本地时区两种格式,且无任何时区标记,导致下游模型在做时间推算时频繁出错(例如把 UTC 时间当成东八区时间计算时间差、生成错误的时间叙述)。
根因分析
问题出在 L1 提取阶段的 prompt builder。
src/core/prompts/l1-extraction.ts第 120 行和第 125 行的formatExtractionPrompt函数:`[${m.id}] [${m.role}] [${new Date(m.timestamp).toISOString()}]: ${m.content}`toISOString()永远输出 UTC(带 Z 后缀),所以 L1 提取模型看到的原始消息时间戳格式为2026-04-07T03:04:45Z。而 system prompt 要求模型"基于消息的 timestamp 推算绝对时间,在 metadata 中输出 activity_start_time 和 activity_end_time(ISO 8601格式)"。提取模型直接把看到的 UTC 时间原样写入 metadata 存入数据库。
下游召回时(
src/core/hooks/auto-recall.ts第 687-699 行),从数据库取出这些 UTC 时间,经过formatTimestamp函数格式化为YYYY-MM-DD HH:MM(无时区标记),拼到记忆正文的(活动时间: ...)里。下游模型就当成用户本地时区了。错误链条:
其他
toISOString()调用点(均输出 UTC,可能产生类似问题):src/core/scene/scene-extractor.tsformatTimestamp函数src/core/record/l1-writer.tssrc/core/hooks/auto-capture.tssrc/core/conversation/l0-recorder.tssrc/core/store/tcvdb.tssrc/core/report/reporter.tssrc/utils/pipeline-manager.tssrc/utils/backup.ts建议修复方案
新增用户时区配置项(建议命名为
timezone),默认值按服务器时区或"Asia/Shanghai":{ "timezone": "Asia/Shanghai" }具体改动:
openclaw.plugin.json的configSchema:新增timezone字段,类型 string,描述为用户所在 IANA 时区src/core/prompts/l1-extraction.ts:formatExtractionPrompt中的toISOString()替换为按配置时区格式化的函数(Node.js 可用Intl.DateTimeFormat({ timeZone: config.timezone })),确保注入给 L1 提取模型的消息时间戳始终是用户时区System prompt 更新:把"ISO 8601格式"改为明确指定用户时区,如
"YYYY-MM-DD HH:MM (时区 Asia/Shanghai)",要求提取模型按此时区输出 metadata 中的 activity_start_time / activity_end_timeauto-recall.ts的formatTimestamp加一步兜底:即使历史数据存的是 UTC,展示时也做 UTC→用户时区换算,确保向后兼容其余
toISOString()调用点:统一替换为时区感知格式化函数(或统一用Date.now()毫秒值存储,仅展示层做时区换算)影响范围: 纯格式化改动,不改变记忆数据结构、向量存储、搜索逻辑。通过第 4 步兜底,历史 UTC 数据自动兼容,无需数据迁移。
为什么这很重要
对于非 UTC 时区的用户(如中国、日本、印度等全球大多数用户),时间戳是记忆召回中最关键的结构化信息之一。时区混用会导致:
统一时区配置可以让插件在保持全球化的同时,为每个用户提供正确的时间感知。
Issue Description (English Version)
Problem
When memories are recalled, downstream models (OpenClaw / Hermes) receive timestamps that mix UTC and local timezone formats without any timezone indicator, causing frequent time calculation errors (e.g., treating UTC timestamps as local timezone, producing incorrect time-difference narratives).
Root Cause
The issue originates in the L1 extraction prompt builder.
In
src/core/prompts/l1-extraction.ts, lines 120 and 125, theformatExtractionPromptfunction:`[${m.id}] [${m.role}] [${new Date(m.timestamp).toISOString()}]: ${m.content}`toISOString()always outputs UTC (with Z suffix), so the L1 extraction model sees timestamps like2026-04-07T03:04:45Z.The system prompt asks the model to "derive absolute time from message timestamps and output activity_start_time / activity_end_time in ISO 8601 format in metadata." The extraction model copies the UTC timestamps directly into metadata, which are then stored in the database.
During recall (
src/core/hooks/auto-recall.ts, lines 687-699), these UTC timestamps are formatted viaformatTimestampintoYYYY-MM-DD HH:MM(no timezone marker) and appended to the memory content as(活动时间: ...). The downstream model then assumes this is the user's local timezone.Error chain:
Proposed Fix
Add a user timezone config option (suggested name:
timezone), defaulting to server timezone or"Asia/Shanghai":{ "timezone": "Asia/Shanghai" }Specific changes:
openclaw.plugin.jsonconfigSchema: Addtimezonefield (string), description: user's IANA timezonesrc/core/prompts/l1-extraction.ts: ReplacetoISOString()informatExtractionPromptwith a timezone-aware formatter (Node.jsIntl.DateTimeFormat({ timeZone: config.timezone })), ensuring L1 extraction models always receive timestamps in the user's timezoneUpdate system prompt: Change "ISO 8601 format" to explicitly specify the user's timezone, e.g.,
"YYYY-MM-DD HH:MM (timezone Asia/Shanghai)", instructing the extraction model to output metadata in this timezoneAdd fallback in
auto-recall.tsformatTimestamp: Even for historical UTC data, convert to user timezone at display time for backward compatibilityReplace all other
toISOString()calls: Unify with a timezone-aware formatter (or store asDate.now()milliseconds and convert only at display layer)Impact: Purely formatting changes — no modifications to memory data structures, vector storage, or search logic. Step 4 ensures backward compatibility with historical UTC data; no data migration required.
Why This Matters
For users outside the UTC timezone (China, Japan, India, and most of the world), timestamps are one of the most critical structured fields in memory recall. Timezone mixing causes:
A unified timezone config allows the plugin to remain globally usable while providing each user with correct time awareness.