Skip to content

[Feature Request] 支持用户自定义时区配置,修复 L1 提取和召回中的 UTC/本地时区混用问题 #87

@Elvisvon

Description

@Elvisvon

[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"
}

具体改动:

  1. openclaw.plugin.jsonconfigSchema:新增 timezone 字段,类型 string,描述为用户所在 IANA 时区

  2. src/core/prompts/l1-extraction.tsformatExtractionPrompt 中的 toISOString() 替换为按配置时区格式化的函数(Node.js 可用 Intl.DateTimeFormat({ timeZone: config.timezone })),确保注入给 L1 提取模型的消息时间戳始终是用户时区

  3. System prompt 更新:把"ISO 8601格式"改为明确指定用户时区,如 "YYYY-MM-DD HH:MM (时区 Asia/Shanghai)",要求提取模型按此时区输出 metadata 中的 activity_start_time / activity_end_time

  4. auto-recall.tsformatTimestamp 加一步兜底:即使历史数据存的是 UTC,展示时也做 UTC→用户时区换算,确保向后兼容

  5. 其余 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:

  1. openclaw.plugin.json configSchema: Add timezone field (string), description: user's IANA timezone

  2. 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

  3. 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

  4. Add fallback in auto-recall.ts formatTimestamp: Even for historical UTC data, convert to user timezone at display time for backward compatibility

  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions