refactor(时区): 常量收进契约、SQL 统一走 localTime,去掉会话时区与 TZ 兜底
Some checks failed
Deploy / deploy (push) Has been cancelled

- TIME_ZONE / TIME_ZONE_OFFSET_MINUTES 移到 packages/contract/src/time.ts,
  前后端共用一份,不再各写一遍靠注释对齐。
- apps/api/src/time.ts 新增 localTime(列),替换散落 7 处的
  `at time zone ${TIME_ZONE_SQL}`;ac-trend 的 where 复用同一个 year 表达式。
- /problems/:displayId/yearly-ac 漏写了时区、按 UTC 切年,被会话时区兜底掩盖;
  改为按东八区切(只影响每年 12-31 北京 0–8 点的提交归年)。
- 删掉数据库连接的 TimeZone 和 Dockerfile 的 TZ:正确代码不依赖它们,
  它们只会在线上掩盖漏写处、让 dev 与线上答案不同。
- time.ts:calendarDayYearsAgo/pad 并入 shiftMonthsByCalendar,startOfCalendarDay
  并入 todayStart,localWeekday 改用 getUTCDay,删掉历史叙述注释。
- 前端 zonedParts 改为固定偏移 + getUTC*(与后端、日期选择器同一写法),
  去掉 Intl formatToParts;10 万次 299ms → 11ms。zonedYear 去掉按浏览器时区的兜底。
- 两份 CLAUDE.md 同步;n-date-picker 那条过时说明改成现用法。

验证:新旧「近两年起点」21359 个时刻 0 差异、localWeekday 0 差异、
前端固定偏移与 Intl 在 America/New_York 下 47821 个时刻 0 差异;
localTime 在 select/group by/where 复用可用;fix-achievement-hours 预演仍为 148 / 60。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1d8B3f4SXJwDvUY625eQd
This commit is contained in:
2026-09-14 06:06:52 -06:00
parent 3921c496cb
commit cc51e305cf
15 changed files with 116 additions and 251 deletions

View File

@@ -96,10 +96,8 @@ const getColor = (count: number) => {
// 一格一周横向铺开。原来是一格一天、7 行 53 列,中职学生一年也就二三十天有提交,
// 365 格里三百多格空着,整张图看着像没用过。
//
// 服务端给的 timestamp 是**东八区某个周一**的 UTC 零点,所以年月日一律按东八区取
// `zonedParts`),不能用 `getMonth()` / `getDate()` —— 那是浏览器本地部件,
// 从别的时区打开会整体错一格。周末直接加 6 天的毫秒数:大陆没有夏令时,
// 那正好是东八区的 6 天。
// 服务端给的 timestamp 是**东八区某个周一**的 UTC 零点,年月日按东八区取`zonedParts`
// 不用浏览器本地的 `getMonth()` / `getDate()`。东八区没有夏令时,周日就是加 6 天。
const cells = computed(() =>
aiStore.heatmapData.map((item, i) => {
const start = new Date(item.timestamp)