Skip to content

[refactor][P3] timex/boundary 在 30 分钟 DST 位移时区打破「EndOfX 不与相邻周期重叠」不变量——EndOfHour 越入下一墙钟小时近 30 分钟(Lord Howe 实测) #418

Closed

Description

@zy84338719

来源:2026-11-12 第五轮深挖审查。行号基于 767fc57 的 main,动手前请核对现场。

位置

  • timex/boundary.go:42-53(wallHourStart:回拨判据假设位移恰为 1 小时——next := b.Add(time.Hour) 后比较「同一墙钟小时」)
  • timex/boundary.go:133-141(EndOfMinute/EndOfHour:wallXxxStart(t).Add(周期-1ns),起点对了、加的周期长度仍是绝对 1 小时/1 分钟)
  • 不变量出处:timex/boundary.go:11-12(「EndOfX = 下一周期起点 - 1ns,不与相邻周期重叠」)、timex/boundary.go:19-22(「两条不变量……无论时区是否处于 DST 切换窗口」)

证据

包内 DST 加固(boundary_dst_test.go)只覆盖整小时位移时区(America/New_York 等)。对非整小时 DST 位移时区(Australia/Lord_Howe、Antarctica/Macquarie:回拨 02:00 → 01:30,位移 30 分钟),wallHourStart 的「+1h 后仍是同一墙钟小时 ⇒ 重复」判据不成立,EndOfHour 直接把起点加满绝对 1 小时,越过墙钟小时边界。实测(go test 探针,Australia/Lord_Howe 2026-04-05 回拨日):

second hour: [01:30:00 +10:30, 02:29:59.999999999 +10:30]   ← EndOfHour 落进 02 点
after hour start: 02:00:00 +10:30                            ← 下一小时的 BeginningOfHour
VIOLATION B: EndOfHour(second)=02:29:59.999999999 >= BeginningOfHour(next)=02:00:00
             重叠 29m59.999999999s

即 EndOfHour(t) > t 成立、但「EndOfX = 下一周期起点 - 1ns、不与相邻周期重叠」被打破:墙钟 02:00–02:30 这半小时同时落在 hour-01 桶与 hour-02 桶里。按小时分桶的统计/对账/计费逻辑在这些时区会重复计 30 分钟;EndOfMinute 同理(wallMinuteStart 的小时基准取错次)。

顺带:wallHourStart 对第一次出现的 01:45(+11 段)也会因同一判据给出 01:30 起点的错位(探针里 first 段恰好因 b.Add(1h) 落点不同而未触发,但判据本身按 1 小时位移推导,30 分钟位移下「重复小时」的检测方向是猜的)。

触发条件:进程时区为 30 分钟 DST 位移时区(Australia/Lord_Howe、Antarctica/Macquarie,及若干历史 30 分钟位移时区)+ 调用 EndOfHour/EndOfMinute(或依赖其分区语义的 BeginningOfWeek/EndOfWeek 组合)。CST(无 DST)的主消费场景不受影响——这也是前四轮没扫到的原因。

建议

  • EndOfX 不再用「起点 + 固定周期长度」,改为「求下一周期起点再 -1ns」:next := wallHourStart(t.Add(time.Hour))(或对位移场景专门探测),保证与 BeginningOfX(next) 精确衔接;
  • wallHourStart 的重复检测改为不假设位移幅度:b.Add(1h) 落在同一墙钟小时仅是 1 小时位移的特例,可直接以 t 是否早于「按 t 的 offset 重建的下一整点」判次;
  • 两条不变量的表述加一句适用域(若选择不修):「已知边界:非整小时 DST 位移时区(Lord Howe 等)下 EndOfX 可能越入下一墙钟小时」——现在的「无论时区是否处于 DST 切换窗口」是过承诺。

补一条 Lord Howe 用例进 boundary_dst_test.go(时区数据在任何完整 tzdata 下可用),断言 EndOfHour(t) < BeginningOfHour(下一小时)。

Activity

  1. added a commit that references this issue on Nov 12, 2026
  2. zy84338719 commented on Nov 12, 2026

    @zy84338719
    ContributorAuthor

    口径声明已随 PR #429 合入 main(merge commit 6ee91ff5,2026-11-12T18:18:08Z):boundary godoc「无论时区是否处于 DST 切换窗口」的过承诺改为如实适用域——整小时位移时区守两条不变量;Lord Howe/Macquarie(30 分钟回拨)下 EndOfHour/EndOfMinute 会越入下一墙钟周期,按墙钟分桶的统计请改按绝对时间分桶。

    本单保持 open:完全修复需分段模型重设计(重复段长度 30 分钟、墙钟小时三段占用,桶身份需语义拍板)+ 全 tzdata 测试矩阵,留给专门设计轮,不在本轮半吊子修复。

    CI:link ✅ / Lint & Test / Test ✅ / Lint & Test / Lint ✅ / Zero Third-Party Deps ✅

  3. zy84338719 commented on Nov 12, 2026

    @zy84338719
    ContributorAuthor

    已在 v0.47.1 修复并发布 ✅

    变更内容

    本条修复(在 v0.47.1 的版本节里定位到):

    该版本 `v0.47.1` 的其余变更

    关联问题单:pkg#418。

    Release: https://github.com/oekit/pkg/releases/tag/v0.47.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactor重构机会(代码审查产出)

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions