跨端: feat(P6): 表头与网格共用同一个 startOfWeek;"今天"的调用侧入可跑判据;禁用 toISOString 取日期键
pi 2026-09-14 两条,都赶在页面骨架之前定下来。 1. **唯一分叉点必须同时喂两处**:整月网格有两个地方依赖"周从哪天开始" —— 空格数(`leadingBlanks`)**与表头第一格**。表头若在页面里硬编码,就是**第二个分叉**: 格子全对、**表头整体错一列**,而原有 6 条判据一条都不会红(它们只看网格)。 新增 `weekdayLabels(startOfWeek)`(顺序只从这一个参数出)+ **交叉核对判据**: 把"1 号落在第几列"与"那一列的表头字"对上(6 个月份 × 2 种起始)。 2. **"今天"的调用侧**:`today` 入参化让纯逻辑侧干净了,代价是**唯一还能错的地方搬到了调用侧** —— 而它正好是纯逻辑判据够不着的。`toISOString()` 是 UTC 口径:UTC+8 的清晨会给**昨天**, "今天"就标到上一格,且在本机跑 UTC 的环境里**永远测不出来**。 新增 `isoOfLocal(now)`(本地年月日手工补零)+ `isoAtOffset(now, 分钟)`(与前者同源但不依赖进程时区, 好让三种偏移**可以真跑**)。判据钉住:UTC+8 / UTC-7 / UTC 三种偏移的日期、 **两种取法在 UTC+8 清晨必须不同**(把陷阱本身钉死)、以及本机两条取法自洽。 3. 附带一条**未来时**的判据:鸿蒙树里不许出现 `toISOString().slice(0,10)` 取日期键 (登记值 0 ⇒ 页面骨架写错时立刻红,报错写"正确修法 = isoOfLocal"与"最常见的错误修法 = 改期望值")。 它第一次跑就抓到了 `model/Calendar.ts` 里**解释这个陷阱的注释** —— 所以改扫 `code()` (去注释后的代码):规则管代码,注释是文档。
This commit is contained in:
@ -89,3 +89,58 @@ export function monthGrid(year: number, month: number, weekStart: number, today?
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 星期表头 —— **必须与 `leadingBlanks` 来自同一个 `startOfWeek`**(pi 2026-09-14)。
|
||||
*
|
||||
* 整月网格有两个地方依赖"周从哪天开始":**空格数**与**表头第一格**。
|
||||
* 如果表头在页面里硬编码成 `['日','一',…]` 或另算一次,那就是**第二个分叉点**:
|
||||
* 格子位置全对、**表头整体错一列** —— 而这是肉眼第一眼就看到的东西,
|
||||
* 纯逻辑判据却一条都不会红(它们只看了网格)。
|
||||
*
|
||||
* 所以:字的写法可以硬编码在这里,**顺序只能从这一个参数出**。
|
||||
*/
|
||||
export const WEEKDAY_NAMES: string[] = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
|
||||
export function weekdayLabels(startOfWeek: number): string[] {
|
||||
const s: number = ((startOfWeek % 7) + 7) % 7;
|
||||
const out: string[] = [];
|
||||
for (let i = 0; i < 7; i++) {
|
||||
out.push(WEEKDAY_NAMES[(s + i) % 7]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 某一天是星期几的**名字**(用于把表头与网格对齐做交叉核对) */
|
||||
export function weekdayNameOf(year: number, month: number, day: number): string {
|
||||
return WEEKDAY_NAMES[new Date(Date.UTC(year, month - 1, day)).getUTCDay()];
|
||||
}
|
||||
|
||||
function pad2(n: number): string {
|
||||
return n < 10 ? `0${n}` : `${n}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* "今天"的**调用侧**(pi 2026-09-14 §2,趁骨架之前定下来)。
|
||||
*
|
||||
* 为什么单独抽出来:`today` 入参化之后,`Calendar.ts` 是纯粹的,
|
||||
* **唯一还能错的地方搬到了调用侧** —— 而它恰好是纯逻辑判据够不着的那一格。
|
||||
*
|
||||
* 陷阱:UTC+8 的早上 7 点,`new Date().toISOString().slice(0,10)` 给的是**昨天**,
|
||||
* 而网格的 `iso` 键是 `Date.UTC` 造的 ⇒ "今天"会被标到昨天那一格,
|
||||
* 且在本机跑在 UTC 的环境里**永远测不出来**。
|
||||
* 所以页面侧取今天**只许**用这个函数(本地年月日手工补零),不许用 `toISOString()`。
|
||||
*/
|
||||
export function isoOfLocal(now: Date): string {
|
||||
return `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按**给定偏移**取日期(与 `isoOfLocal` 同源,但不依赖进程时区)——
|
||||
* 好让"UTC+8 / UTC-8 / UTC 三种下取到的本地日期都对"这条判据可以**真跑**,
|
||||
* 而不必依赖跑判据的机器正好在哪个时区。
|
||||
*/
|
||||
export function isoAtOffset(now: Date, offsetMinutes: number): string {
|
||||
const shifted = new Date(now.getTime() + offsetMinutes * 60000);
|
||||
return `${shifted.getUTCFullYear()}-${pad2(shifted.getUTCMonth() + 1)}-${pad2(shifted.getUTCDate())}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user