package service import ( "testing" "time" ) // TestParseRockTimeUsesBusinessZone 锁定「上游不带偏移的时间串必须按北京时间解析」。 // // 回归背景:上游返回的是不带偏移的北京时间墙面时间,用 time.Local 解析等于假设 // 进程时区就是北京时间。一旦 TZ≠+08:00,解析出的瞬时值就会整体偏移 // (UTC 下差 8 小时),而绑定时序判断依赖它来区分「历史残留」与「本次新绑定」—— // 误判会把刚扫码绑定的 Token 放回库存,直接造成重复发货。 func TestParseRockTimeUsesBusinessZone(t *testing.T) { original := time.Local time.Local = time.UTC t.Cleanup(func() { time.Local = original }) cases := []struct { name string raw string want time.Time }{ { name: "空格分隔的北京时间按 +08:00 解析", raw: "2026-10-05 20:32:17", want: time.Date(2026, 10, 5, 12, 32, 17, 0, time.UTC), }, { name: "T 分隔且无偏移", raw: "2026-09-27T14:38:51.23", want: time.Date(2026, 9, 27, 6, 38, 51, 230000000, time.UTC), }, { name: "自带偏移的时间串不受进程时区影响", raw: "2026-10-05T20:32:17Z", want: time.Date(2026, 10, 5, 20, 32, 17, 0, time.UTC), }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got, ok := parseRockTime(tc.raw) if !ok { t.Fatalf("解析 %q 失败", tc.raw) } if !got.Equal(tc.want) { t.Fatalf("解析 %q 期望 %s,实际 %s", tc.raw, tc.want, got) } }) } } // TestParseRockTimeRejectsEmpty 空串与空白必须解析失败。 func TestParseRockTimeRejectsEmpty(t *testing.T) { for _, raw := range []string{"", " "} { if _, ok := parseRockTime(raw); ok { t.Fatalf("%q 不应解析成功", raw) } } }