Эх сурвалжийг харах

fix: resolve the issue of logout failure caused by the timezone store

zhongming4762 7 сар өмнө
parent
commit
565be77e96

+ 143 - 0
packages/@core/base/shared/src/utils/__tests__/date.test.ts

@@ -0,0 +1,143 @@
+import dayjs from 'dayjs';
+import timezone from 'dayjs/plugin/timezone';
+import utc from 'dayjs/plugin/utc';
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+
+import {
+  formatDate,
+  formatDateTime,
+  getCurrentTimezone,
+  getSystemTimezone,
+  isDate,
+  isDayjsObject,
+  setCurrentTimezone,
+} from '../date';
+
+dayjs.extend(utc);
+dayjs.extend(timezone);
+
+describe('dateUtils', () => {
+  const sampleISO = '2024-10-30T12:34:56Z';
+  const sampleTimestamp = Date.parse(sampleISO);
+
+  beforeEach(() => {
+    // 重置时区
+    dayjs.tz.setDefault();
+    setCurrentTimezone(); // 重置为系统默认
+  });
+
+  afterEach(() => {
+    vi.restoreAllMocks();
+  });
+
+  // ===============================
+  // formatDate
+  // ===============================
+  describe('formatDate', () => {
+    it('should format a valid ISO date string', () => {
+      const formatted = formatDate(sampleISO, 'YYYY/MM/DD');
+      expect(formatted).toMatch(/2024\/10\/30/);
+    });
+
+    it('should format a timestamp correctly', () => {
+      const formatted = formatDate(sampleTimestamp);
+      expect(formatted).toMatch(/2024-10-30/);
+    });
+
+    it('should format a Date object', () => {
+      const formatted = formatDate(new Date(sampleISO));
+      expect(formatted).toMatch(/2024-10-30/);
+    });
+
+    it('should format a dayjs object', () => {
+      const formatted = formatDate(dayjs(sampleISO));
+      expect(formatted).toMatch(/2024-10-30/);
+    });
+
+    it('should return original input if date is invalid', () => {
+      const invalid = 'not-a-date';
+      const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
+      const formatted = formatDate(invalid);
+      expect(formatted).toBe(invalid);
+      expect(spy).toHaveBeenCalledOnce();
+    });
+
+    it('should apply given format', () => {
+      const formatted = formatDate(sampleISO, 'YYYY-MM-DD HH:mm');
+      expect(formatted).toMatch(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}/);
+    });
+  });
+
+  // ===============================
+  // formatDateTime
+  // ===============================
+  describe('formatDateTime', () => {
+    it('should format date into full datetime', () => {
+      const result = formatDateTime(sampleISO);
+      expect(result).toMatch(/2024-10-30 \d{2}:\d{2}:\d{2}/);
+    });
+  });
+
+  // ===============================
+  // isDate
+  // ===============================
+  describe('isDate', () => {
+    it('should return true for Date instances', () => {
+      expect(isDate(new Date())).toBe(true);
+    });
+
+    it('should return false for non-Date values', () => {
+      expect(isDate('2024-10-30')).toBe(false);
+      expect(isDate(null)).toBe(false);
+      expect(isDate(undefined)).toBe(false);
+    });
+  });
+
+  // ===============================
+  // isDayjsObject
+  // ===============================
+  describe('isDayjsObject', () => {
+    it('should return true for dayjs objects', () => {
+      expect(isDayjsObject(dayjs())).toBe(true);
+    });
+
+    it('should return false for other values', () => {
+      expect(isDayjsObject(new Date())).toBe(false);
+      expect(isDayjsObject('string')).toBe(false);
+    });
+  });
+
+  // ===============================
+  // getSystemTimezone
+  // ===============================
+  describe('getSystemTimezone', () => {
+    it('should return a valid IANA timezone string', () => {
+      const tz = getSystemTimezone();
+      expect(typeof tz).toBe('string');
+      expect(tz).toMatch(/^[A-Z]+\/[A-Z_]+/i);
+    });
+  });
+
+  // ===============================
+  // setCurrentTimezone / getCurrentTimezone
+  // ===============================
+  describe('setCurrentTimezone & getCurrentTimezone', () => {
+    it('should set and retrieve the current timezone', () => {
+      setCurrentTimezone('Asia/Shanghai');
+      expect(getCurrentTimezone()).toBe('Asia/Shanghai');
+    });
+
+    it('should reset to system timezone when called with no args', () => {
+      const guessed = getSystemTimezone();
+      setCurrentTimezone();
+      expect(getCurrentTimezone()).toBe(guessed);
+    });
+
+    it('should update dayjs default timezone', () => {
+      setCurrentTimezone('America/New_York');
+      const d = dayjs('2024-01-01T00:00:00Z');
+      // 校验时区转换生效(小时变化)
+      expect(d.tz().format('HH')).not.toBe('00');
+    });
+  });
+});

+ 25 - 9
packages/@core/base/shared/src/utils/date.ts

@@ -5,9 +5,11 @@ import utc from 'dayjs/plugin/utc';
 dayjs.extend(utc);
 dayjs.extend(timezone);
 
-export function formatDate(time: number | string, format = 'YYYY-MM-DD') {
+type FormatDate = Date | dayjs.Dayjs | number | string;
+
+export function formatDate(time: FormatDate, format = 'YYYY-MM-DD') {
   try {
-    const date = dayjs(time);
+    const date = dayjs.isDayjs(time) ? time : dayjs(time);
     if (!date.isValid()) {
       throw new Error('Invalid date');
     }
@@ -18,7 +20,7 @@ export function formatDate(time: number | string, format = 'YYYY-MM-DD') {
   }
 }
 
-export function formatDateTime(time: number | string) {
+export function formatDateTime(time: FormatDate) {
   return formatDate(time, 'YYYY-MM-DD HH:mm:ss');
 }
 
@@ -30,18 +32,32 @@ export function isDayjsObject(value: any): value is dayjs.Dayjs {
   return dayjs.isDayjs(value);
 }
 
+/**
+ * 获取当前时区
+ * @returns 当前时区
+ */
+export const getSystemTimezone = () => {
+  return dayjs.tz.guess();
+};
+
+/**
+ * 自定义设置的时区
+ */
+let currentTimezone = getSystemTimezone();
+
 /**
  * 设置默认时区
  * @param timezone
  */
-export const setDefaultTimezone = (timezone?: string) => {
-  timezone ? dayjs.tz.setDefault(timezone) : dayjs.tz.setDefault();
+export const setCurrentTimezone = (timezone?: string) => {
+  currentTimezone = timezone || getSystemTimezone();
+  dayjs.tz.setDefault(currentTimezone);
 };
 
 /**
- * 获取当前时区
- * @returns 当前时区
+ * 获取设置的时区
+ * @returns 设置的时区
  */
-export const getTimezone = () => {
-  return dayjs.tz.guess();
+export const getCurrentTimezone = () => {
+  return currentTimezone;
 };

+ 12 - 6
packages/stores/src/modules/timezone.ts

@@ -1,7 +1,10 @@
 import { ref, unref } from 'vue';
 
 import { DEFAULT_TIME_ZONE_OPTIONS } from '@vben-core/preferences';
-import { getTimezone, setDefaultTimezone } from '@vben-core/shared/utils';
+import {
+  getCurrentTimezone,
+  setCurrentTimezone,
+} from '@vben-core/shared/utils';
 
 import { acceptHMRUpdate, defineStore } from 'pinia';
 
@@ -59,9 +62,7 @@ const getTimezoneHandler = () => {
 const useTimezoneStore = defineStore(
   'core-timezone',
   () => {
-    const timezoneRef = ref(
-      getTimezone() || new Intl.DateTimeFormat().resolvedOptions().timeZone,
-    );
+    const timezoneRef = ref(getCurrentTimezone());
 
     /**
      * 初始化时区
@@ -74,7 +75,7 @@ const useTimezoneStore = defineStore(
         timezoneRef.value = timezone;
       }
       // 设置dayjs默认时区
-      setDefaultTimezone(unref(timezoneRef));
+      setCurrentTimezone(unref(timezoneRef));
     }
 
     /**
@@ -87,7 +88,7 @@ const useTimezoneStore = defineStore(
       await timezoneHandler.setTimezone?.(timezone);
       timezoneRef.value = timezone;
       // 设置dayjs默认时区
-      setDefaultTimezone(timezone);
+      setCurrentTimezone(timezone);
     }
 
     /**
@@ -103,10 +104,15 @@ const useTimezoneStore = defineStore(
       console.error('Failed to initialize timezone during store setup:', error);
     });
 
+    function $reset() {
+      timezoneRef.value = getCurrentTimezone();
+    }
+
     return {
       timezone: timezoneRef,
       setTimezone,
       getTimezoneOptions,
+      $reset,
     };
   },
   {