Quellcode durchsuchen

Refactor StatsDashboardPage to adopt ISO week definitions for date handling

- Updated date range calculations and formatting to utilize ISO week standards, ensuring consistency in weekly report generation.
- Enhanced week date range function to reflect ISO week logic, improving clarity in date representations.
- Added necessary Day.js plugin for locale updates to set the week start to Monday, aligning with ISO standards.
0es vor 3 Wochen
Ursprung
Commit
10c547ed7b
2 geänderte Dateien mit 23 neuen und 15 gelöschten Zeilen
  1. 18 15
      src/app/(dashboard)/statistics/dashboard/page.tsx
  2. 5 0
      src/utils/dayjs.ts

+ 18 - 15
src/app/(dashboard)/statistics/dashboard/page.tsx

@@ -59,14 +59,14 @@ function getDefaultRange(type: number): [Dayjs, Dayjs] {
     case 2: // 月报:过去 6 个月
       return [today.subtract(5, "month").startOf("month"), today];
     default: // 周报:过去 8 周
-      return [today.subtract(7, "week").startOf("week"), today];
+      return [today.subtract(7, "week").startOf("isoWeek"), today];
   }
 }
 
 /**
  * Generates all period IDs covered by [start, end] for the given report type.
  * - type 0 (daily):   "YYYY-MM-DD"
- * - type 1 (weekly):  "gggg-[W]ww"
+ * - type 1 (weekly):  "GGGG-[W]WW"  (ISO week year – ISO week number)
  * - type 2 (monthly): "YYYY-MM"
  */
 function generatePeriodIds(start: Dayjs, end: Dayjs, type: number): string[] {
@@ -78,10 +78,10 @@ function generatePeriodIds(start: Dayjs, end: Dayjs, type: number): string[] {
       cur = cur.add(1, "day");
     }
   } else if (type === 1) {
-    let cur = start.startOf("week");
+    let cur = start.startOf("isoWeek");
     while (!cur.isAfter(end)) {
-      const week = cur.week();
-      const year = cur.weekYear();
+      const week = cur.isoWeek();
+      const year = cur.isoWeekYear();
       ids.push(`${year}-W${String(week).padStart(2, "0")}`);
       cur = cur.add(1, "week");
     }
@@ -163,20 +163,23 @@ function fillPlaymateData(
 }
 
 /**
- * Given a natural-week ID like "2026-W10", returns the Sun–Sat date range string.
+ * Given an ISO-week ID like "2026-W10", returns the Mon–Sun date range string.
  * Returns null for non-week IDs.
+ *
+ * ISO 8601: week 1 is the week containing the first Thursday of the year;
+ * Jan 4 always falls in ISO week 1, so startOf("isoWeek") on Jan 4 reliably
+ * gives the Monday of week 1.
  */
 function getWeekDateRange(id: string): string | null {
   const match = id.match(/^(\d{4})-W(\d{2})$/);
   if (!match) return null;
   const year = Number(match[1]);
   const week = Number(match[2]);
-  // Natural week: week 1 starts on the Sunday of the week containing Jan 1
-  const sunday = dayjs(`${year}-01-01`)
-    .startOf("week")
+  const monday = dayjs(`${year}-01-04`)
+    .startOf("isoWeek")
     .add((week - 1) * 7, "day");
-  const saturday = sunday.add(6, "day");
-  return `${sunday.format("YYYY-MM-DD")} ~ ${saturday.format("YYYY-MM-DD")}`;
+  const sunday = monday.add(6, "day");
+  return `${monday.format("YYYY-MM-DD")} ~ ${sunday.format("YYYY-MM-DD")}`;
 }
 
 function renderPeriodId(id: string, showRaw: boolean): React.ReactNode {
@@ -229,9 +232,9 @@ const StatsDashboardPage: React.FC = () => {
       let startDay = range[0];
       let endDay = range[1];
       if (reportType === 1) {
-        // Natural week: Sunday (day=0) ~ Saturday (day=6)
-        startDay = range[0].day(0);
-        endDay = range[1].day(6);
+        // ISO week: Monday ~ Sunday
+        startDay = range[0].startOf("isoWeek");
+        endDay = range[1].startOf("isoWeek").add(6, "day");
       } else if (reportType === 2) {
         startDay = range[0].startOf("month");
         endDay = range[1].endOf("month");
@@ -724,7 +727,7 @@ const StatsDashboardPage: React.FC = () => {
               reportType === 0
                 ? "YYYY-MM-DD"
                 : reportType === 1
-                  ? "gggg-[W]ww"
+                  ? "GGGG-[W]WW"
                   : "YYYY-MM"
             }
           />

+ 5 - 0
src/utils/dayjs.ts

@@ -1,6 +1,7 @@
 import dayjs from "dayjs";
 import isoWeek from "dayjs/plugin/isoWeek";
 import timezone from "dayjs/plugin/timezone";
+import updateLocale from "dayjs/plugin/updateLocale";
 import utc from "dayjs/plugin/utc";
 import weekOfYear from "dayjs/plugin/weekOfYear";
 import weekYear from "dayjs/plugin/weekYear";
@@ -11,9 +12,13 @@ dayjs.extend(timezone);
 dayjs.extend(isoWeek);
 dayjs.extend(weekOfYear);
 dayjs.extend(weekYear);
+dayjs.extend(updateLocale);
 
 // Set default timezone to Indonesia (GMT+7)
 dayjs.tz.setDefault("Asia/Jakarta");
+dayjs.updateLocale("en", {
+  weekStart: 1,
+});
 
 // Wrap default export so that all usages of this module
 // get time in the default timezone (Asia/Jakarta, GMT+7)