- 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:
1 parent
3921c496cb
commit
cc51e305cf
15 files changed
+116
-251
No files matched your search
@@ -18,7 +18,7 @@ import { db, schema } from "../../db"
|
||||
import { failure, success } from "../../http"
|
||||
import { JudgeStatus } from "../../judge/status"
|
||||
import { completeChat } from "../../services/ai"
|
||||
import { localYear, TIME_ZONE_SQL } from "../../time"
|
||||
import { localTime, localYear } from "../../time"
|
||||
import { queryInteger, rounded } from "../helpers"
|
||||
import { findTagsByName, normalizeTagNames } from "./problem"
|
||||
|
||||
@@ -230,11 +230,8 @@ adminTagRoutes.get("/problem-analytics/ac-trend", requireTeacher, async (c) => {
|
||||
let minPerYear = queryInteger(c.req.query("minPerYear"), 100)
|
||||
if (![50, 100, 200].includes(minPerYear)) minPerYear = 100
|
||||
|
||||
// 年份一律按东八区切:`extract(year from timestamptz)` 默认走**数据库会话时区**,
|
||||
// 而 `currentYear` 走的是进程时区 —— 两个不同来源碰巧都等于 UTC 时才自洽。
|
||||
// 这里两处都用同一个常量,谁都不依赖环境。group by 会重复这个表达式,
|
||||
// 所以必须内联(见 time.ts 的 TIME_ZONE_SQL)。
|
||||
const year = sql<number>`extract(year from ${schema.submission.createTime} at time zone ${TIME_ZONE_SQL})`.mapWith(Number)
|
||||
// 年份按东八区切,和上面 `currentYear` 的夹逼同口径
|
||||
const year = sql<number>`extract(year from ${localTime(schema.submission.createTime)})`.mapWith(Number)
|
||||
const rows = await db.select({
|
||||
problemId: schema.problem.id,
|
||||
displayId: schema.problem.displayId,
|
||||
@@ -246,8 +243,8 @@ adminTagRoutes.get("/problem-analytics/ac-trend", requireTeacher, async (c) => {
|
||||
.innerJoin(schema.problem, eq(schema.submission.problemId, schema.problem.id))
|
||||
.where(and(
|
||||
isNull(schema.submission.contestId),
|
||||
gte(sql`extract(year from ${schema.submission.createTime} at time zone ${TIME_ZONE_SQL})`, sinceYear),
|
||||
lte(sql`extract(year from ${schema.submission.createTime} at time zone ${TIME_ZONE_SQL})`, untilYear),
|
||||
gte(year, sinceYear),
|
||||
lte(year, untilYear),
|
||||
))
|
||||
.groupBy(schema.problem.id, schema.problem.displayId, schema.problem.title, year)
|
||||
.orderBy(asc(schema.problem.id), asc(year))
|
||||
|
||||
Reference in new issue
Block a user