请 AI 代理修复日期之前,先说清时区要求

使用 AI 辅助编写与复核。

本文目录

请 AI 代理修复时区问题之前,先说明什么必须保持不变:时间线上的某一时刻、本地时钟读数,还是日历日期。这些要求并不相同。如果一律转换成 UTC,却没有保留原来的含义,就可能把显示修复变成排期错误。

本文使用两个虚构功能:跨地区会议,以及只有日期的提交截止期限。列出的换算曾在本地使用 Python 3.12.14 和系统 IANA 时区数据 2026c 执行。它们是小型教学示例,并不是对浏览器或排期产品的测试。

先明确正在修改的对象

对象 需求示例 需要保留什么
确定时刻 所有人同时加入同一场会议 同一个时刻;每位查看者看到的时钟时间可以不同
周期性本地钟表时间 团队每周一纽约时间 09:00 开会 本地时间、重复规则和具名时区;UTC 偏移量可能变化
日历日期 按纽约时间,11 月 1 日全天均接受提交 日期、判定所依据的时区,以及明确的截止规则

生日可能只是一个日期,完全不需要判定所依据的时区。提交截止期限则需要时区,因为应用必须决定这一天何时结束。不要仅仅因为数据库能接收时间戳,就给这两类字段中的任何一类加上 UTC 午夜。

如果用户说“日期不对”,请在修改存储或显示代码前,先问清这个字段表示上述哪种对象。还要明确时区来自哪里:用户显式选择、已保存的项目设置,还是查看者的运行环境。旅行中的用户打开同一条记录时,这些选择会产生不同的行为。

案例一:同一场会议,三种显示

这场虚构会议发生在 2026-07-15T13:00:00Z。它的时刻固定,查看者时区只改变显示。

查看者时区 同一时刻的显示
America/New_York 2026 年 7 月 15 日,09:00,UTC−04:00
Europe/London 2026 年 7 月 15 日,14:00,UTC+01:00
Asia/Tokyo 2026 年 7 月 15 日,22:00,UTC+09:00

这些偏移量适用于本练习所用时区数据下的这个时刻,并不是全年不变的常量。未来的本地时间规则会影响行为时,应保留具名时区。

Python zoneinfo 文档介绍了 IANA 时区支持、通过 fold 处理歧义时间,以及时区数据查找方式。部署环境需要一个可用且来源已知的数据源;本练习使用系统文件,没有新装软件包。记录可复现案例时,也应记录运行时和数据版本。

决定钟表时间不能唯一确定时刻时怎么处理

纽约本地时间 2026-03-08 02:30 在春季时钟切换中并不存在。在本地示例中,通过往返转换进行分类后,找到的匹配时刻数量为零。

在 2026-11-01,01:30 会出现两次:

用户的钟表时间 偏移量 对应的 UTC 时刻
11 月 1 日 01:30,较早的一次 UTC−04:00 2026-11-01T05:30:00Z
11 月 1 日 01:30,较晚的一次 UTC−05:00 2026-11-01T06:30:00Z

对于这个虚构会议编辑器,选择以下策略:选中的钟表时间不存在时,请用户选择另一个有效时间;时间重复时,请用户选择较早或较晚的一次。在选项旁显示偏移量和对应的 UTC 时间。其他产品可以选择另一种有文档说明的策略,但代理不应悄悄替产品作出这个业务决定。

通过 replace(tzinfo=...) 附加时区,本身并不会拒绝不可能存在的钟表时间。下面这个小型分类函数会检查两种 fold 取值,经 UTC 往返转换,并对匹配时刻去重:

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

def possible_instants(wall, zone):
    # wall is a naive datetime representing the requested local clock time.
    found = set()
    for fold in (0, 1):
        candidate = wall.replace(tzinfo=zone, fold=fold)
        instant = candidate.astimezone(timezone.utc)
        returned = instant.astimezone(zone)
        if returned.replace(tzinfo=None) == wall:
            found.add(instant)
    return sorted(found)

zone = ZoneInfo("America/New_York")
assert possible_instants(datetime(2026, 3, 8, 2, 30), zone) == []
assert len(possible_instants(datetime(2026, 11, 1, 1, 30), zone)) == 2

配套练习采用了这种分类方法,并检查了两个精确 UTC 值。这只是一个约定输入为无时区 datetime 的小型辅助函数,并不是完整的用户输入解析器或周期性排期引擎。

案例二:持续 25 小时的截止日

假设这个虚构表单在 America/New_York 的 2026 年 11 月 1 日全天接受提交。选定的截止点是下一个本地午夜,且不包含该截止时刻。

边界 UTC 时刻
11 月 1 日本地午夜,包含此时刻 2026-11-01T04:00:00Z
11 月 2 日本地午夜,不包含此时刻 2026-11-02T05:00:00Z

本例实际经过的时间间隔为 25 小时。如果给起始时刻加上 24 小时,表单就会比预期的下一个本地午夜早关闭一小时。

应存储截止日期、判定所依据的时区和截止规则。使用相关规则确定边界,而不是把日期当成 UTC 午夜。比较时间时,在排他结束时刻或之后提交,就应判为已截止。这样可以避免凭空定义“一天的最后一毫秒”,因为不同系统的时间精度可能不同。

还要定义以哪个时钟为准。在本例中,服务器决定是否接受提交;浏览器倒计时仅作辅助,不能改变截止规则。时区数据变更和未来的周期性安排,需要单独制定更新策略。不要暗示上面的小示例已经实现了这项策略。

给代理的任务说明

修复虚构会议编辑器,以及只有日期的提交截止逻辑。
先识别每个字段的含义,不要只修改标签。

会议约定:
- 已保存的单次会议表示一个确定时刻。
- 查看者时区改变显示,不改变已保存的时刻。
- 新输入的本地时间包含一个显式 IANA 时区。
- 钟表时间不存在:请用户选择另一个有效时间。
- 钟表时间重复:请用户选择较早/较晚的一次,并显示偏移量/UTC。
- 不要从一个例子推断永久不变的偏移量。

截止约定:
- 保存日期 2026-11-01、America/New_York 和下一个午夜规则。
- 按本练习的规则,排他结束时刻为 2026-11-02T05:00:00Z。
- 服务器比较结果决定是否接受;到达结束时刻或之后即已截止。
- 推导下一个本地午夜,不要直接加上 24 小时的经过时长。

验收案例:
- 2026-07-15T13:00:00Z 在该日期显示为纽约 09:00、伦敦 14:00,
  以及东京 22:00。
- 纽约 2026-03-08 02:30 没有候选时刻。
- 纽约 2026-11-01 01:30 的候选时刻为 05:30Z 和 06:30Z。
- 指定截止日的区间是 11 月 1 日 04:00Z 至 11 月 2 日 05:00Z。
- 分别检查截止前的一个时刻和恰好到达截止点的时刻。

交付字段模型、转换路径、歧义处理和检查结果。
记录实际使用的运行时与 IANA 数据,以及任何无法执行的检查。
保留无关字段。周期性安排、替换浏览器 API 和数据库
迁移不在本次修改范围内;扩大范围前先提出依赖。

好的修复,会让同一条业务规则在存储、编辑和显示中都保持成立。先从这条规则出发;时间戳格式只是实现它的一部分。