README
dsh-tool-time
DSH 时间工具插件 —— 严格 ISO 解析、IANA 时区转换、UTC 日历运算、固定时长差。零依赖、零进程、纯函数。
动机
Agent 处理时间信息是最高频需求之一——"现在几点"、"3 天后是几号"、"把 UTC 转成北京时间"。当前做法 bash date 的问题:每次起进程、跨平台语法不一致(macOS 与 GNU date -d 完全不同)、时区转换靠 TZ=... 手动拼。模型心算时区偏移和闰年/DST 边界是错误高发区。
安全模型
无 eval、无 new Function。输入只经过严格校验:
- 严格 ISO 8601 子集:仅接受
YYYY-MM-DD(UTC 零点)、YYYY-MM-DDTHH:mm:ssZ、YYYY-MM-DDTHH:mm:ss±HH:MM(±14:00 内,可选.SSS毫秒);不带时区的日期时间、RFC 2822、自然语言日期一律拒绝;2026-02-30等日历溢出由查表校验拒绝(不依赖 Date 的静默归一化) - 时区名:IANA 名交给
Intl.DateTimeFormat校验,非法即抛time: unknown timezone - 数值:
amount必须是安全整数;unit枚举白名单;所有字符串 ≤200 字符 - Intl 环境固定:
'en-CA'+hourCycle: 'h23'(避免午夜24:00与本地化数字)
架构
DSH Agent
│ ctx.tools.register()
▼
src/index.ts(Cordis 插件入口 + action 分发 + 独立校验)
│
▼
src/time.ts
├── parseStrictISO() — 严格 ISO 解析(正则 + 查表校验)
├── formatInTimezone() — Intl formatToParts 组装(时钟可注入)
├── addMonthsClamped() — 先置 1 日 → 目标年月 → 天数钳制
└── diffBetween() — 固定时长(sign + absolute 分解)
工具声明
ctx.tools.register(defineTool({
name: 'time',
parameters: {
action: { type: 'string', required: true, enum: ['now', 'convert', 'add', 'diff'] },
value: { type: 'string', description: 'Strict ISO 8601 timestamp' },
timezone: { type: 'string', description: 'IANA timezone (default UTC; display only)' },
from: { type: 'string', description: 'diff start' },
to: { type: 'string', description: 'diff end' },
amount: { type: 'integer', description: 'Signed safe integer' },
unit: { type: 'string', enum: ['seconds','minutes','hours','days','weeks','months','years'] },
},
output: { schema: { type: 'json' }, render: (_a, v) => [{ type: 'text', text: JSON.stringify(v, null, 2) }] },
execute: (args) => Promise.resolve(executeAction(args.action, args) as JsonValue),
timeoutMs: 1000,
}))
支持的操作
| action | 参数 | 返回 |
|---|---|---|
now |
timezone?(默认 UTC) |
当前时间结构化表示 |
convert |
value + timezone(必填) |
转换后的结构化时间 |
add |
value + amount + unit(timezone? 仅展示) |
加减后的结构化时间 |
diff |
from + to |
固定时长差(无月份/年份) |
返回结构(canonical value):
{
"iso": "2026-08-05T06:00:00.000Z",
"unix": 1785909600000,
"timezone": "Asia/Shanghai",
"timezoneSource": "explicit",
"local": "2026-08-05T14:00:00+08:00",
"formatted": "2026-08-05 14:00:00 (Asia/Shanghai)"
}
语义契约:
add始终基于 UTC 运算(日历日语义,跨 DST 保持"日历日"直觉);timezone仅决定local/formatted展示- 月份/年份钳制:
2026-01-31 + 1 month = 2026-02-28;2028-02-29 + 1 year = 2029-02-28(先置 1 日 → 定位目标年月 → 按目标月最大天数钳制) diff仅固定时长:sign+ 带符号总量 +absolute余数分解(月/年无法从毫秒唯一推导,不提供)。字段语义(AUDIT-TIME-03):milliseconds是精确带符号总时长seconds/minutes/hours/days/weeks是sign * floor(abs / unit)(对绝对值截断后带符号)absolute是abs的非负余数分解(如 -1500ms →milliseconds: -1500, seconds: -1, absolute: {seconds: 1, milliseconds: 500})- 差值超过安全整数范围抛
time: diff duration out of range
- 默认 UTC:省略
timezone时结果与运行环境无关(timezoneSource: "default-utc")
npm 0.1.0-rc.6 兼容(已验证)
本插件已迁移到 npm 0.1.0-rc.6 依赖线,并在 @deepseek-ai/dsh@0.1.0-rc.6(npm 私有包)的隔离 consumer 中完成全链路验证:
- 类型/运行时:peer 为
@deepseek-ai/cordis: ^4.0.1+@deepseek-ai/dsh-tools: >=0.0.1-rc.1 <0.2.0+@deepseek-ai/dsh-invariants: >=0.0.1-rc.1 <0.2.0;不再依赖 unscopedcordis - 独立构建:
npm install(devDependencies 自包含 typescript/vitest/@types/node)→npm run typecheck→npm test→npm run build→npm pack - 消费验证:tarball 装入 DSH 0.1.0-rc.6(npm)consumer →
dsh --profile compat --dump-config出现本插件 row → 工具真实注册与执行通过 - 启动方式:
npx -p @deepseek-ai/dsh@0.1.0-rc.6 dsh web(lib 生产模式;勿install -g全局安装)
版本适配
- 适配 DSH: DSH 0.1.0-rc.6(npm)(profile/bundle 插件系统)
- bundle 声明:
package.json的dsh.bundle(patch 指向cordis.patch.yml)+exports导出 - patch 格式:
cordis.patch.yml使用- insert:列表(patch 是 id-targeted 语义,裸- id:条目会报entry not found) - files: 发布 tarball 含
lib/、src/、cordis.patch.yml
安装
Profile Bundle(推荐)
将本插件作为独立 bundle 安装到 profile(DSH 0.1.0-rc.6(npm))。本仓库位于 omdsh-dev 组织,公开可访问:
# 交互式(web)profile —— 从 GitHub 仓库安装
dsh plugin --profile web add github:omdsh-dev/dsh-tool-time
# 一次性任务(headless)profile —— dsh run 默认使用 headless
dsh plugin --profile headless add github:omdsh-dev/dsh-tool-time
或使用 npm pack 生成的 tarball 安装:
npm pack # 生成 dsh-tool-time-<version>.tgz
# 交互式(web)profile
dsh plugin --profile web add ./dsh-tool-time-<version>.tgz
# 一次性任务(headless)profile
dsh plugin --profile headless add ./dsh-tool-time-<version>.tgz
包内 dsh.bundle.patch(指向 cordis.patch.yml)会在安装后自动把插件加入 profile 的 layer stack;插件的 cordis.patch.yml 以 - insert: 插入 tool-time 条目。插件缺失的 peer 依赖(@deepseek-ai/cordis、@deepseek-ai/dsh-tools)由 profile 的 healed profiles/node_modules 回退安装提供。
⚠️ web 与 headless 是不同 profile:web 安装不会自动覆盖 headless;
dsh run默认使用 headless profile。Windows 路径使用正斜杠(C:/...)。
验证安装
dsh --profile web --dump-config | grep tool-time
运行验证
dsh run "使用 time 工具获取当前 UTC 时间"
手动安装与旧版本兼容(monorepo 旧场景)
monorepo 方式仅适用于旧场景:不支持 Profile Bundle 的旧快照或插件开发调试环境:
- 放入 monorepo:
cp -r time ~/.dsh/source/master/packages/tools/time(开发调试) apps/cli/package.json加"@deepseek-ai/dsh-tool-time": "workspace:^";tsconfig.host.jsonreferences 加{ "path": "./packages/tools/time" }pnpm install && pnpm run build- 在 profile 用户层 patch 插入插件(
~/.dsh/profiles/<name>/cordis.patch.yml):
- insert:
- id: tool-time
name: '@deepseek-ai/dsh-tool-time'
- 验证:
dsh --profile <name> --dump-config | grep tool-time
注意:patch 是 id-targeted 语义(DSH 0.1.0-rc.6(npm))——裸
- id:条目会报entry "xxx" not found,必须用- insert:列表包裹。
已知限制
- 分发链路:peer 依赖(如
@deepseek-ai/dsh-tools)为 npm 私有包,独立安装不依赖 monorepo workspace(monorepo 仅旧场景) add是 UTC 日历运算,非当地墙钟语义(当地墙钟需 v2)- 不支持自然语言日期("next week")与日历月/年差(v2)
- 时区数据来自 Node 内置 ICU:"跨平台一致" = "相同 Node/ICU 数据下一致"
- 输入年份范围为 1000–9999(
0000–0999拒绝:JavaScriptDate.UTC对这些年份有 1900+ 映射的历史行为) - 精度契约:
iso/unix保留毫秒;local/formatted为秒精度展示字段 - offset 精度到分钟:v1 只支持现代时区;历史时区(<1900)的秒级 offset 会被截断
测试
pnpm test
功能/错误/攻击载荷共 63 个用例(fake clock、时区已知向量、月末钳制、严格 ISO 边界、年份范围、offset 跨年、极端 amount、原型链时区名等)。完整清单见本地维护的设计文档。
许可
MIT
更多「知识与研究」插件
dsh-browser
作者 lum1104
dsh plugin: Chrome sidebar extension that lets DSH operate your browser directly, no vision capabilities required. 一款 Chrome 侧边栏扩展程序,可让 DSH 直接操控您的浏览器,无需视觉能力。
modsearch
作者 liustack
The web search plugin for DeepSeek Harness, and the search bridge for every model without native web access. Ask the web or X, get structured JSON evidence. | DeepSeek Harness 的联网搜索插件,为不能联网的模型补上搜索。问网页或 X,拿回结构化 JSON 证据(搜索、抓取、引用)。
argo
作者 taxueseek
专门为 agent 打造的 agent 搜索工具,具备多语言搜索能力,覆盖中文/英文/学术/代码/购物/金融/新闻/百科。
dsh-chat-import
作者 nwflower
Import 14+ external agent chat histories (Claude Code, Codex, ChatGPT, Cursor, Gemini, Reasonix, opencode, ZCode, Grok Build, OpenClaw, Pi, Hermes, Kimi CLI, DSH) into DeepSeek Harness as resumable sessions — full-fidelity, reverse export/sync, bundle backup. | 从 Claude Code、Codex、Reasonix 等 Agent 工具导入历史消息到 DeepSeek Harness 并继续对话。
