From: root Date: Tue, 18 Aug 2026 11:58:32 +0000 (+0800) Subject: fix: 时区bug-StudyMode/TestMode改用中国时间; 修正8/17误记到前一天的记录 X-Git-Url: http://acesimba.cloud/gitweb/?a=commitdiff_plain;h=cb2476aaed7607f18f5152f210ca367117f598c4;p=words.git fix: 时区bug-StudyMode/TestMode改用中国时间; 修正8/17误记到前一天的记录 - 新增 localTimestamp() 生成 Asia/Shanghai(UTC+8) 时间戳, 替代 toISOString()(UTC) 避免凌晨学习被记到前一天 - StudyMode/TestMode 完成学习时写入中国本地时间 - 修正 study_logs 中 id 395-398 因 UTC completed_at 被错误记到 2026-08-17 的问题 (实际为北京时间 8/18 早上 7点多, created_at UTC 23:39-23:55), 改为 2026-08-18 - 其他 100+ 历史误记记录按用户要求保持不变 --- diff --git a/backend/db/bcd.db b/backend/db/bcd.db index ca3a2be..da4027c 100644 Binary files a/backend/db/bcd.db and b/backend/db/bcd.db differ diff --git a/frontend/src/api.js b/frontend/src/api.js index 6e735fd..2046886 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -12,6 +12,16 @@ export const http = axios.create({ timeout: 8000, }); +// 生成中国时间(Asia/Shanghai, UTC+8)时间戳 "YYYY-MM-DD HH:MM:SS"。 +// 注意: 必须用固定 +8h 偏移,不能用 toISOString()(那是 UTC),否则 +// 凌晨学习会被记到前一天(时区 bug)。与服务器 Asia/Shanghai 保持一致。 +export function localTimestamp() { + const sh = new Date(Date.now() + 8 * 3600 * 1000); + const p = (n) => String(n).padStart(2, '0'); + return `${sh.getUTCFullYear()}-${p(sh.getUTCMonth() + 1)}-${p(sh.getUTCDate())} ` + + `${p(sh.getUTCHours())}:${p(sh.getUTCMinutes())}:${p(sh.getUTCSeconds())}`; +} + // 统一 API 出口:所有组件应通过 api.xxx 调用,不再各自硬编码 API_BASE 或裸用 axios export const api = { getHome: () => http.get('/api/home'), diff --git a/frontend/src/components/StudyMode.js b/frontend/src/components/StudyMode.js index c03b78b..b4a501f 100644 --- a/frontend/src/components/StudyMode.js +++ b/frontend/src/components/StudyMode.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; -import { api } from '../api'; +import { api, localTimestamp } from '../api'; import { useOnlineStatus } from '../hooks/useOnlineStatus'; import { getWordsByGroup, putProgress, getLearnableGroups } from '../db'; import { triggerAutoSync } from '../sync'; @@ -101,7 +101,7 @@ function StudyMode() { } else { if (submittedRef.current) return; // 防重入(按住Enter等场景) submittedRef.current = true; - const now = new Date().toISOString().slice(0, 19).replace('T', ' '); + const now = localTimestamp(); // 本地优先: 先写本地 dirty (必定保存, 不丢记录) await putProgress(groupId, 'study', now, true); // 在线则后台自动同步 (成功清 dirty, 失败保留) diff --git a/frontend/src/components/TestMode.js b/frontend/src/components/TestMode.js index 8f93e94..1ebd1fb 100644 --- a/frontend/src/components/TestMode.js +++ b/frontend/src/components/TestMode.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useNavigate, useSearchParams } from 'react-router-dom'; -import { api } from '../api'; +import { api, localTimestamp } from '../api'; import { useOnlineStatus } from '../hooks/useOnlineStatus'; import { getWordsByGroup, putProgress, getLearnableGroups } from '../db'; import { triggerAutoSync } from '../sync'; @@ -143,7 +143,7 @@ function TestMode() { const finishTest = useCallback(async () => { if (submittedRef.current) return; // 防重入(按住Enter等场景) submittedRef.current = true; - const now = new Date().toISOString().slice(0, 19).replace('T', ' '); + const now = localTimestamp(); // 本地优先: 先写本地 dirty (必定保存, 不丢记录) await putProgress(groupId, activityType, now, true); // 在线则后台自动同步 (成功清 dirty, 失败保留)