fix: resolve timezone for postgres birth dates
This commit is contained in:
@@ -1708,3 +1708,19 @@
|
||||
- 防复发:API 与组件测试必须锁定 Turn 关联、刷新恢复、运行中真实 phase、仅展示实际工具/技法、无安全 reasoning 时不补写摘要,以及旧记录、legacy、shadow 的兼容行为。
|
||||
- 相关记录:BUG-011、BUG-086、BUG-087、BUG-094
|
||||
- 修复版本:`birth-time-rectification-v6` / `rectification-agent-v6-1`
|
||||
|
||||
## BUG-096 | PostgreSQL 日期对象导致生时纠正 Case 创建误报资料格式错误
|
||||
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-07-29
|
||||
- 最近更新:2026-07-29
|
||||
- 影响面:staging 自托管 PostgreSQL 出生资料读取、历史时区偏移补全与生时纠正 Case 创建
|
||||
- 用户现象:已完成出生资料的账号启动生时纠正时返回 400,并显示“出生资料或提交内容格式不正确”。
|
||||
- 触发条件:数据库驱动将 `birth_date` 返回为 JavaScript `Date`,且历史资料的 `timezone_offset` 为空、需要根据日期和时区 ID 补全。
|
||||
- 根因:共享 `resolveMissingBirthTimezoneOffset()` 只把非空字符串识别为出生日期;合法 `Date` 被当成缺失值,补全提前返回,随后严格出生资料解析以 `Historical timezone offset must be resolved before parsing` 拒绝请求。
|
||||
- 修复:共享时区补全 resolver 在读取出生日期时同时接受有效 `Date` 与既有字符串,并统一归一化为 `YYYY-MM-DD`;不放宽后续 Zod 校验,也不改变生时纠正计算规格。
|
||||
- 验证:路由回归夹具改为 PostgreSQL 实际返回形态的 `Date`;聚焦测试、前端全量测试、lint、TypeScript、生产构建和 Python V5 服务测试通过,staging 精确 SHA smoke 随本次发布执行。
|
||||
- 防复发:所有从 PostgreSQL 进入纯日期字符串合同的共享边界必须先覆盖 `string | Date` 驱动返回类型;Case 创建继续要求历史时区偏移在严格解析前完成。
|
||||
- 相关记录:BUG-076、BUG-091、BUG-095
|
||||
- 复发自:无
|
||||
- 修复版本:待本次 staging 修复提交与部署验收
|
||||
|
||||
@@ -21,6 +21,11 @@ function text(value: unknown): string | null {
|
||||
return typeof value === "string" && value.trim() ? value.trim() : null;
|
||||
}
|
||||
|
||||
function calendarDate(value: unknown): string | null {
|
||||
if (value instanceof Date && !Number.isNaN(value.getTime())) return value.toISOString().slice(0, 10);
|
||||
return text(value);
|
||||
}
|
||||
|
||||
export function finiteBirthNumber(value: unknown): number | null {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
||||
}
|
||||
@@ -66,7 +71,7 @@ export async function resolveMissingBirthTimezoneOffset(
|
||||
if (existingOffset !== null) return value;
|
||||
const latitude = finiteBirthNumber(profile.latitude);
|
||||
const longitude = finiteBirthNumber(profile.longitude);
|
||||
const birthDate = text(valueFor(profile, "birth_date", "date"));
|
||||
const birthDate = calendarDate(valueFor(profile, "birth_date", "date"));
|
||||
const timezoneId = text(valueFor(profile, "timezone_id", "timezoneId"));
|
||||
if (latitude === null || longitude === null || !birthDate || !timezoneId) return value;
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ test("shared timezone resolver supports camelCase browser profiles and preserves
|
||||
|
||||
test("journey entry and stored-case loader resolve nullable offsets before strict chart parsing", async () => {
|
||||
const resolved = await resolveMissingBirthTimezoneOffset({
|
||||
birth_date: "1955-02-24",
|
||||
birth_date: new Date("1955-02-24T00:00:00.000Z"),
|
||||
reported_birth_time: null,
|
||||
birth_time_source: "period_only",
|
||||
birth_time_period: "evening",
|
||||
|
||||
Reference in New Issue
Block a user