]> acesimba.cloud Git - words.git/commitdiff
fix: 时区bug-StudyMode/TestMode改用中国时间; 修正8/17误记到前一天的记录
authorroot <root@localhost.localdomain>
Tue, 18 Aug 2026 11:58:32 +0000 (19:58 +0800)
committerroot <root@localhost.localdomain>
Tue, 18 Aug 2026 11:58:32 +0000 (19:58 +0800)
- 新增 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+ 历史误记记录按用户要求保持不变

backend/db/bcd.db
frontend/src/api.js
frontend/src/components/StudyMode.js
frontend/src/components/TestMode.js

index ca3a2be4b0d82bf8187196f8c8869c7ee3ebc78f..da4027c10f43ed2e3d215d7291dfb237cddc3485 100644 (file)
Binary files a/backend/db/bcd.db and b/backend/db/bcd.db differ
index 6e735fd9d2d1e342551a9b05ab749aa7aebf960e..204688638ed890ac647097580b144f841566fc98 100644 (file)
@@ -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'),
index c03b78bd4f100c7298a06c6ddb12298d74f5c3b2..b4a501f75f17350de174d1c7fff4ae560851483e 100644 (file)
@@ -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, 失败保留)
index 8f93e94e90807205c12074c36a677518c5f2ba74..1ebd1fb1a5780b2743b4de474750e04cf3466a0a 100644 (file)
@@ -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, 失败保留)