03/04 这类日期在月日不同时无明显视觉缺陷,读者按自身习惯解析,导致生产环境 bug 难以在测试阶段被发现,144 天/年存在此歧义,三分之二的随机日期反而无法自辨别。
小数点位置错误会让读者觉得陌生并上报。货币符号放错位置看起来很别扭。但 ambiguous numeric date( ambiguous numeric date 类型的 ambiguous numeric date)根本没有任何可见的缺陷:读者按自己的习惯解析它,得到一个有效的日期,然后继续。没有人在任何时刻会注意到,这正是这个 bug 比同类问题更容易进入生产环境的原因——通常是通过后果发现的:错过了截止日期、合同日期倒签、轮班表错了一个月。
这种 ambiguity(歧义)并非普遍存在。它只发生在日期为 12 日或更早时,即每个月 12 天,一年 144 天。在另外 221 天里,日期数字超过 12,格式自动消除歧义——25/12/2026 只能是 December。这就是让这个 bug 躲过测试的特性。大约五分之三的随机日期可以无歧义地解析,所以抽样检查很可能通过,而 pipeline 实际上是坏的。
这也意味着 bug 在月初最严重,而那里恰恰是账单日期、报告期和合同生效日期密集出现的地方。
Month-first(月份优先)顺序在全球是少数惯例,但在英语训练语料库中的数字日期里占绝大多数。美国英语写作、美国来源的商业文档,以及大量软件输出都使用它。Day-first(日优先)在欧洲大部分地区、拉丁美洲、亚洲和非洲的许多地方使用,但那些地方的写作更多是当地语言,所以模型看到的英语日期格式证据远比世界实际情况更偏向美国惯例。
结果是一个特定且可复现的模式:模型用法语或德语写作时通常能正确使用日优先,因为法语和德语训练数据中的数字日期是日优先的。同一个模型用英语为英国或澳大利亚受众写作时会漂移到月份优先,因为"英语"和"美国惯例"在证据中纠缠在一起。仅仅声明语言是不够的。你必须声明地区,即使如此你也是在让一个生成器应用一条规则,而不是让一个格式化工具查询一条规则。
对于任何将被机器解析或被国际受众读取的日期,可靠的答案是 ISO 8601,由国际标准化组织发布,最近的修订版是 ISO 8601-1:2019。扩展日历格式是 YYYY-MM-DD:四位年份,连字符,两位月份,连字符,两位日期,始终补零。
它有三个特性是其他格式无法同时具备的。按结构设计它是 unambiguous(无歧义)的,因为组件从大到小排列且宽度固定。作为纯字符串它能正确排序,因为字典顺序和时间顺序一致。它的识别度足够高,以至于不知道该标准的读者仍然能得到正确答案,因为第一位是四位年份就排除了所有其他解读。
对于时间戳而非日期,相关的 profile 是 RFC 3339,它将分隔符固定为 T,并要求明确的偏移量:2026-08-11T14:30:00Z。任何跨时区的东西都使用它。没有偏移量的日期是日历日,日历日是一种完全有效的存储形式,只要你清楚你存储的就是这种东西。
所以给模型的指令是:所有日期以 YYYY-MM-DD 输出,绝不用带斜杠的数字格式。这比"使用英式日期格式"更容易执行,因为正则可以检查它。用 ^\d{4}-\d{2}-\d{2}$ 验证输出,拒绝任何不符合的,而不是信任指令被遵守了。
面向人类的 prose(正文)。德语信函中的 2026-08-11 读起来像机器输出。那里的惯例是 11.08.2026 或 11. August 2026,ISO 形式虽然正确但很陌生。ISO 是交换格式,不是显示格式;这是两个不同的工作,混淆它们本身就是一种 bug。
输入解析。修复输出对从用户、文档或上游系统到达的日期没有任何作用。从扫描发票中提取日期的 prompt 必须决定源文件中 03/04/2026 是什么意思,答案取决于发票开具地点——这通常可以从同一文档中找到,应该作为证据提取而不是假设。
非公历日历。ISO 8601 是公历的。日本文档可能按天皇纪年标注事件——日本年号日历涵盖这种情况,伊斯兰历、希伯来历、波斯历和佛历日期都在某些地方作为行政用途使用。转换它们不是格式决定。
基于周和序数形式。ISO 也定义了 2026-W33-2 和 2026-223。这些是合法的 ISO 8601,会让只期望日历形式的解析器出问题,所以验证器应该有意识地决定是否接受它们。
在接触真实系统后幸存下来的规则是:模型只将日期处理为 ISO 字符串,每个人类面对的日期都由一个附加了 locale 的格式化器从该 ISO 字符串渲染。具体来说,模型提取或生成 2026-08-11;你的代码将其转换为 11. August 2026、11 August 2026 或 August 11, 2026,取决于阅读者是谁。
const iso = "2026-08-11";
const d = new Date(iso + "T00:00:00Z");
new Intl.DateTimeFormat("de-DE", { dateStyle: "long", timeZone: "UTC" }).format(d)
// "11. August 2026"
new Intl.DateTimeFormat("en-GB", { dateStyle: "long", timeZone: "UTC" }).format(d)
// "11 August 2026"
new Intl.DateTimeFormat("en-US", { dateStyle: "long", timeZone: "UTC" }).format(d)
// "August 11, 2026"
注意显式的 timeZone: "UTC"。解析裸的 YYYY-MM-DD 会给你午夜 UTC,在格林威治西边的时区格式化会渲染出前一天。这是在你修复第一个 bug 时立即到来的第二个、完全独立的 off-by-one date bug,它具有相同的特性——在有人看错日子之前是不可见的。同样的问题——十二小时制对二十四小时输出——有着相同的形态和相同的修复方法,值得一次解决,而不是以后再发现第二个。
Getting 24-Hour or 12-Hour Time Right in AI-Generated Text by Locale
Using the Japanese Era Calendar in AI-Generated Dates