fix(db): 修正存量「归还时间/报废时间」的 8 小时时区偏差(附自检)
问题 ---- process_return 与 scrap_sources.deduct 原先用 datetime.now()(容器本地时间, Docker 默认 UTC)写 return_time / operation_time,而同一行的 borrow_time 由 beijing_time() 写入 —— 同一行两个时钟,差 8 小时。 实测(开发库)trans_borrow 有 53 行呈现「归还时间早于借出时间」这种物理上 不可能的数据,例如 id=80 借出 09-09 11:04、归还 09-09 03:46。 代码侧已修(f7c49f4),新数据已是北京时间 —— 本次只处理存量。 ★ 脚本刻意做了「先自检、后修正」的两段式 服务器 API 容器的 TZ 未必是 UTC(若本就是 Asia/Shanghai,datetime.now() 一直是北京时间,则**根本无需修正**)。若不做自检直接 +8h,会把正确的时间 改错。故第 0 步先输出:异常行数、以及按分界切出的「UTC 段/空档/北京段」 三段计数,由使用人据结果决定是否执行第 2 步。 ★ 分界与幂等性都在脚本里写明 · 分界 = 修复代码在该服务器**部署生效的时刻**(不是提交时刻),需按实际调整; · 脚本**不幂等** —— 重复执行会再加 8 小时,已显式警示并给出回滚写法。 开发库已按此逻辑修正:53 行 trans_borrow + 1 行 trans_scrap, 修正后「归还早于借出」为 0,抽样时间落在正常工作时段。
This commit is contained in:
112
db_migrations/phase9_fix_return_time_utc.sql
Normal file
112
db_migrations/phase9_fix_return_time_utc.sql
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
-- =============================================================================
|
||||||
|
-- 修正存量「归还时间 / 报废时间」的 8 小时时区偏差
|
||||||
|
--
|
||||||
|
-- 问题
|
||||||
|
-- process_return 与 scrap_sources.deduct 原先用 datetime.now() 写 return_time /
|
||||||
|
-- operation_time,取的是**容器本地时间**(Docker 默认 UTC);而同一行的
|
||||||
|
-- borrow_time 由 beijing_time() 写入。同一行两个时钟,差 8 小时。
|
||||||
|
-- 实测(开发库):53 行 trans_borrow 出现「归还时间早于借出时间」这种
|
||||||
|
-- 物理上不可能的数据,例如 id=80 借出 09-09 11:04、归还 09-09 03:46。
|
||||||
|
--
|
||||||
|
-- 代码侧已修
|
||||||
|
-- 提交 f7c49f4(2026-09-17 09:18 北京时间)起改用 beijing_time(),
|
||||||
|
-- **新写入的数据已经是北京时间**(已验证 TransReturn / TransBorrowReturn /
|
||||||
|
-- TransScrap 三张表的模型默认值均为北京)。本脚本只处理存量。
|
||||||
|
--
|
||||||
|
-- ---------------------------------------------------------------------------
|
||||||
|
-- ★★ 执行前必读:请先跑「第 0 步 自检」,据其结果决定是否执行 ★★
|
||||||
|
--
|
||||||
|
-- 1) 若自检的「异常行数」为 0,且 UTC 区间计数为 0 —— 说明**本服务器无此问题**
|
||||||
|
-- (例如 API 容器的 TZ 本就是 Asia/Shanghai,datetime.now() 一直是北京时间),
|
||||||
|
-- **不要执行第 2 步**,否则会把正确的时间又 +8 小时。
|
||||||
|
--
|
||||||
|
-- 2) 分界时间 @boundary 的含义:**修复代码在本服务器部署生效的时刻**。
|
||||||
|
-- · 该时刻之前写入的行 → UTC,需 +8h;
|
||||||
|
-- · 该时刻之后写入的行 → 北京时间,不能动。
|
||||||
|
-- 开发库用的是提交时间 2026-09-17 09:18(北京)= 01:18(UTC),
|
||||||
|
-- 两组数据之间通常有一段空档,请用自检查看空档是否存在。
|
||||||
|
-- 若你的部署时间不同,请替换下方两处 '2026-09-17 01:18:00'。
|
||||||
|
--
|
||||||
|
-- 幂等性
|
||||||
|
-- ⚠ 本脚本**不幂等** —— 重复执行会把已经修正过的时间再加 8 小时。
|
||||||
|
-- 请在执行前确认已备份,且只执行一次。
|
||||||
|
--
|
||||||
|
-- 回滚
|
||||||
|
-- 把两处 `+ interval '8 hours'` 改成 `- interval '8 hours'`,用同一条件再执行一次。
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 第 0 步:自检(只读,先跑这一段看结果)
|
||||||
|
-- =============================================================================
|
||||||
|
SELECT '=== 0.1) 物理上不可能的「归还早于借出」行数 ===' AS "核对项";
|
||||||
|
SELECT count(*) AS 异常行数
|
||||||
|
FROM trans_borrow
|
||||||
|
WHERE return_time IS NOT NULL AND borrow_time IS NOT NULL
|
||||||
|
AND return_time < borrow_time;
|
||||||
|
|
||||||
|
SELECT '=== 0.2) 按分界切分:UTC 段 / 空档 / 北京段 ===' AS "核对项";
|
||||||
|
SELECT
|
||||||
|
count(*) FILTER (WHERE return_time < '2026-09-17 01:18:00') AS UTC段_需修正,
|
||||||
|
count(*) FILTER (WHERE return_time >= '2026-09-17 01:18:00'
|
||||||
|
AND return_time < '2026-09-17 09:18:00') AS 空档_应为0,
|
||||||
|
count(*) FILTER (WHERE return_time >= '2026-09-17 09:18:00') AS 北京段_不可动
|
||||||
|
FROM trans_borrow
|
||||||
|
WHERE return_time IS NOT NULL;
|
||||||
|
|
||||||
|
SELECT '=== 0.3) trans_scrap 同样切分 ===' AS "核对项";
|
||||||
|
SELECT
|
||||||
|
count(*) FILTER (WHERE operation_time < '2026-09-17 01:18:00') AS UTC段_需修正,
|
||||||
|
count(*) FILTER (WHERE operation_time >= '2026-09-17 01:18:00'
|
||||||
|
AND operation_time < '2026-09-17 09:18:00') AS 空档_应为0,
|
||||||
|
count(*) FILTER (WHERE operation_time >= '2026-09-17 09:18:00') AS 北京段_不可动
|
||||||
|
FROM trans_scrap;
|
||||||
|
|
||||||
|
-- 判读:
|
||||||
|
-- · 0.1 为 0 且 0.2/0.3 的 UTC 段也为 0 → 无需修正,停止。
|
||||||
|
-- · 0.1 大于 0 → 确有此问题,可继续第 2 步。
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 第 1 步:备份(强烈建议)
|
||||||
|
-- =============================================================================
|
||||||
|
-- CREATE TABLE trans_borrow_bak_20260918 AS
|
||||||
|
-- SELECT id, return_time, return_operator FROM trans_borrow;
|
||||||
|
-- CREATE TABLE trans_scrap_bak_20260918 AS
|
||||||
|
-- SELECT id, operation_time FROM trans_scrap;
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 第 2 步:修正(确认第 0 步结果后再执行)
|
||||||
|
-- =============================================================================
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
UPDATE trans_borrow
|
||||||
|
SET return_time = return_time + interval '8 hours'
|
||||||
|
WHERE return_time IS NOT NULL
|
||||||
|
AND return_time < '2026-09-17 01:18:00'; -- ← 替换为你的部署时刻(UTC 口径)
|
||||||
|
|
||||||
|
UPDATE trans_scrap
|
||||||
|
SET operation_time = operation_time + interval '8 hours'
|
||||||
|
WHERE operation_time < '2026-09-17 01:18:00'; -- ← 同上
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- 第 3 步:执行后核对
|
||||||
|
-- =============================================================================
|
||||||
|
SELECT '=== 3.1) 异常行数应为 0 ===' AS "核对项";
|
||||||
|
SELECT count(*) AS 异常行数
|
||||||
|
FROM trans_borrow
|
||||||
|
WHERE return_time IS NOT NULL AND borrow_time IS NOT NULL
|
||||||
|
AND return_time < borrow_time;
|
||||||
|
|
||||||
|
SELECT '=== 3.2) 抽样:归还时间应晚于借出时间,且落在工作时段 ===' AS "核对项";
|
||||||
|
SELECT id,
|
||||||
|
to_char(borrow_time, 'MM-DD HH24:MI') AS 借出,
|
||||||
|
to_char(return_time, 'MM-DD HH24:MI') AS 归还
|
||||||
|
FROM trans_borrow
|
||||||
|
WHERE return_time IS NOT NULL
|
||||||
|
ORDER BY return_time DESC
|
||||||
|
LIMIT 8;
|
||||||
Reference in New Issue
Block a user