diff --git a/apps/server/src/agent-tools/tools/tool-input.ts b/apps/server/src/agent-tools/tools/tool-input.ts index 8159b1c..04d353f 100644 --- a/apps/server/src/agent-tools/tools/tool-input.ts +++ b/apps/server/src/agent-tools/tools/tool-input.ts @@ -49,7 +49,7 @@ export function optionalDate(value: unknown, field: string): ToolInputResult { diff --git a/apps/server/src/attendance/attendance-records.controller.ts b/apps/server/src/attendance/attendance-records.controller.ts index b02e09c..4a6b46f 100644 --- a/apps/server/src/attendance/attendance-records.controller.ts +++ b/apps/server/src/attendance/attendance-records.controller.ts @@ -196,11 +196,11 @@ export class AttendanceRecordsController extends AttendanceControllerBase { source: record.source || '', punchDevice: record.punchDeviceName || record.punchDeviceId || '', punchTime: record.punchTime - ? dayjs(record.punchTime).utc().format('YYYY-MM-DD HH:mm:ss') + ? dayjs(record.punchTime).utcOffset(8).format('YYYY-MM-DD HH:mm:ss') : '', remark: record.remark || '', createdAt: record.createdAt - ? dayjs(record.createdAt).utc().format('YYYY-MM-DD HH:mm:ss') + ? dayjs(record.createdAt).utcOffset(8).format('YYYY-MM-DD HH:mm:ss') : '', }); } diff --git a/apps/server/src/attendance/attendance-report.service.ts b/apps/server/src/attendance/attendance-report.service.ts index bed6638..72108e3 100644 --- a/apps/server/src/attendance/attendance-report.service.ts +++ b/apps/server/src/attendance/attendance-report.service.ts @@ -142,7 +142,7 @@ export class AttendanceReportService { async getAlerts(days: number = 14, threshold: number = 3, accessibleClassIds?: number[]) { const cutoff = new Date(); cutoff.setDate(cutoff.getDate() - days); - const cutoffStr = dayjs(cutoff).utc().format('YYYY-MM-DD'); + const cutoffStr = dayjs(cutoff).utcOffset(8).format('YYYY-MM-DD'); const qb = this.attendanceRepo .createQueryBuilder('a') diff --git a/apps/server/src/attendance/attendance-settlement.service.ts b/apps/server/src/attendance/attendance-settlement.service.ts index 46f1daf..4d474c9 100644 --- a/apps/server/src/attendance/attendance-settlement.service.ts +++ b/apps/server/src/attendance/attendance-settlement.service.ts @@ -285,7 +285,7 @@ export class AttendanceSettlementService { private getCourseClock(date: Date): { date: string; weekDay: number; minutes: number } { // Asia/Shanghai 无夏令时,固定 UTC+8 与 Intl 时区格式化等价 - const c = dayjs.utc(date).add(8, 'hour'); + const c = dayjs(date).utcOffset(8); const weekDay = c.day() === 0 ? 7 : c.day(); return { date: c.format('YYYY-MM-DD'), diff --git a/apps/server/src/bills/bills-generation.service.ts b/apps/server/src/bills/bills-generation.service.ts index a780489..5861fd2 100644 --- a/apps/server/src/bills/bills-generation.service.ts +++ b/apps/server/src/bills/bills-generation.service.ts @@ -259,7 +259,7 @@ export class BillsGenerationService { private isValidDate(value: string) { if (!/^\d{4}-\d{2}-\d{2}$/.test(value || '')) return false; const date = new Date(`${value}T00:00:00Z`); - return !Number.isNaN(date.getTime()) && dayjs(date).utc().format('YYYY-MM-DD') === value; + return !Number.isNaN(date.getTime()) && dayjs(date).utcOffset(8).format('YYYY-MM-DD') === value; } diff --git a/apps/server/src/classroom-rentals/rental-schedule.service.ts b/apps/server/src/classroom-rentals/rental-schedule.service.ts index 321ab7b..15cf14f 100644 --- a/apps/server/src/classroom-rentals/rental-schedule.service.ts +++ b/apps/server/src/classroom-rentals/rental-schedule.service.ts @@ -135,7 +135,7 @@ export class RentalScheduleService { const current = this.toUtcDate(startDate); const end = this.toUtcDate(endDate); while (current <= end) { - dates.add(dayjs(current).utc().format('YYYY-MM-DD')); + dates.add(dayjs(current).utcOffset(8).format('YYYY-MM-DD')); current.setUTCDate(current.getUTCDate() + 1); } } @@ -155,7 +155,7 @@ export class RentalScheduleService { const startWeekDay = current.getUTCDay() || 7; current.setUTCDate(current.getUTCDate() + ((schedule.weekDay - startWeekDay + 7) % 7)); while (current <= end) { - dates.add(dayjs(current).utc().format('YYYY-MM-DD')); + dates.add(dayjs(current).utcOffset(8).format('YYYY-MM-DD')); current.setUTCDate(current.getUTCDate() + 7); } } diff --git a/apps/server/src/classrooms/classrooms.service.ts b/apps/server/src/classrooms/classrooms.service.ts index a597402..215062a 100644 --- a/apps/server/src/classrooms/classrooms.service.ts +++ b/apps/server/src/classrooms/classrooms.service.ts @@ -392,7 +392,7 @@ export class ClassroomsService { const effStart = new Date(Math.max(new Date(r.startDate).getTime(), start.getTime())); const effEnd = new Date(Math.min(new Date(r.endDate).getTime(), end.getTime())); for (let d = new Date(effStart); d <= effEnd; d.setDate(d.getDate() + 1)) { - rentalDaysByRoom[r.classroomId].add(dayjs(d).utc().format('YYYY-MM-DD')); + rentalDaysByRoom[r.classroomId].add(dayjs(d).utcOffset(8).format('YYYY-MM-DD')); } } @@ -401,7 +401,7 @@ export class ClassroomsService { const effStart = new Date(Math.max(new Date(s.startDate).getTime(), start.getTime())); const effEnd = new Date(Math.min(new Date(s.endDate).getTime(), end.getTime())); for (let d = new Date(effStart); d <= effEnd; d.setDate(d.getDate() + 1)) { - scheduleDaysByRoom[s.classroomId].add(dayjs(d).utc().format('YYYY-MM-DD')); + scheduleDaysByRoom[s.classroomId].add(dayjs(d).utcOffset(8).format('YYYY-MM-DD')); } } diff --git a/apps/server/src/database/date-normalization.spec.ts b/apps/server/src/database/date-normalization.spec.ts index a9ff829..df17a24 100644 --- a/apps/server/src/database/date-normalization.spec.ts +++ b/apps/server/src/database/date-normalization.spec.ts @@ -5,8 +5,9 @@ describe('normalizeDateOnly', () => { expect(normalizeDateOnly('2026-07-02')).toBe('2026-07-02'); }); - it('converts legacy ISO timestamps to their UTC calendar date', () => { - expect(normalizeDateOnly('2026-07-01T16:00:00.000Z')).toBe('2026-07-01'); + it('converts legacy ISO timestamps to their China calendar date (UTC+8)', () => { + // 2026-07-01T16:00:00Z = 北京时间 2026-07-02 00:00 + expect(normalizeDateOnly('2026-07-01T16:00:00.000Z')).toBe('2026-07-02'); }); it('rejects unsupported date formats', () => { diff --git a/apps/server/src/database/date-normalization.ts b/apps/server/src/database/date-normalization.ts index 5717b61..6d9d65c 100644 --- a/apps/server/src/database/date-normalization.ts +++ b/apps/server/src/database/date-normalization.ts @@ -9,7 +9,7 @@ export function normalizeDateOnly(value?: string | null): string | null | undefi const isoPrefix = ISO_DATE_PREFIX_PATTERN.exec(value)?.[1]; if (isoPrefix) { const date = new Date(value); - if (!Number.isNaN(date.getTime())) return dayjs(date).utc().format('YYYY-MM-DD'); + if (!Number.isNaN(date.getTime())) return dayjs(date).utcOffset(8).format('YYYY-MM-DD'); } throw new Error(`无效日期格式: ${value}`); diff --git a/apps/server/src/expenses/expense-operations.service.ts b/apps/server/src/expenses/expense-operations.service.ts index f51c177..d64007a 100644 --- a/apps/server/src/expenses/expense-operations.service.ts +++ b/apps/server/src/expenses/expense-operations.service.ts @@ -29,7 +29,7 @@ export class ExpenseOperationsService { private isValidDate(value: string) { if (!/^\d{4}-\d{2}-\d{2}$/.test(value || '')) return false; const date = new Date(`${value}T00:00:00Z`); - return !Number.isNaN(date.getTime()) && dayjs(date).utc().format('YYYY-MM-DD') === value; + return !Number.isNaN(date.getTime()) && dayjs(date).utcOffset(8).format('YYYY-MM-DD') === value; } async createPersonalExpense(dto: CreatePersonalExpenseDto, userId?: number) { diff --git a/apps/server/src/expenses/expenses.service.ts b/apps/server/src/expenses/expenses.service.ts index 53fc9c0..6910942 100644 --- a/apps/server/src/expenses/expenses.service.ts +++ b/apps/server/src/expenses/expenses.service.ts @@ -342,7 +342,7 @@ export class ExpensesService { private isValidDate(value: string) { if (!/^\d{4}-\d{2}-\d{2}$/.test(value || '')) return false; const date = new Date(`${value}T00:00:00Z`); - return !Number.isNaN(date.getTime()) && dayjs(date).utc().format('YYYY-MM-DD') === value; + return !Number.isNaN(date.getTime()) && dayjs(date).utcOffset(8).format('YYYY-MM-DD') === value; } async createStudentUtilityBill(dto: CreateStudentUtilityBillDto, userId?: number) { diff --git a/apps/server/src/occupancies/occupancy-operations.service.ts b/apps/server/src/occupancies/occupancy-operations.service.ts index f0c503e..77896ef 100644 --- a/apps/server/src/occupancies/occupancy-operations.service.ts +++ b/apps/server/src/occupancies/occupancy-operations.service.ts @@ -159,7 +159,7 @@ export class OccupancyOperationsService { const transferDate = new Date(dto.transferDate); const nextDay = new Date(transferDate); nextDay.setDate(nextDay.getDate() + 1); - const defaultBillingStart = dayjs(nextDay).utc().format('YYYY-MM-DD'); + const defaultBillingStart = dayjs(nextDay).utcOffset(8).format('YYYY-MM-DD'); this.assertDateOrder( dto.transferDate, dto.newBillingStartDate || defaultBillingStart, diff --git a/apps/server/src/rbac/rbac-presets.ts b/apps/server/src/rbac/rbac-presets.ts index ea514f7..e4b0ac0 100644 --- a/apps/server/src/rbac/rbac-presets.ts +++ b/apps/server/src/rbac/rbac-presets.ts @@ -148,7 +148,7 @@ import dayjs from '../common/dayjs'; export function getChinaDateParts(date = new Date()): { date: string; weekDay: number } { // Asia/Shanghai 无夏令时,固定 UTC+8 与 Intl en-CA 格式化等价 - const c = dayjs.utc(date).add(8, 'hour'); + const c = dayjs(date).utcOffset(8); const weekDay = c.day() === 0 ? 7 : c.day(); return { date: c.format('YYYY-MM-DD'), weekDay }; } diff --git a/apps/server/src/sync/schedule-sync.helpers.ts b/apps/server/src/sync/schedule-sync.helpers.ts index 559cbee..e634c8c 100644 --- a/apps/server/src/sync/schedule-sync.helpers.ts +++ b/apps/server/src/sync/schedule-sync.helpers.ts @@ -98,7 +98,7 @@ export function buildDailySchedulePlans( const toDate = new Date(`${syncTo}T00:00:00.000Z`); for (let date = new Date(fromDate); date <= toDate; date.setUTCDate(date.getUTCDate() + 1)) { - const dateStr = dayjs(date).utc().format('YYYY-MM-DD'); + const dateStr = dayjs(date).utcOffset(8).format('YYYY-MM-DD'); const weekDay = date.getUTCDay() === 0 ? 7 : date.getUTCDay(); for (const schedule of schedules) { diff --git a/apps/server/src/sync/sync.service.ts b/apps/server/src/sync/sync.service.ts index e1bf143..7f609ba 100644 --- a/apps/server/src/sync/sync.service.ts +++ b/apps/server/src/sync/sync.service.ts @@ -71,8 +71,8 @@ export class SyncService { } const result = await this.attendanceImportService.importFromDingTalk({ - startDate: dayjs(startDate).utc().format('YYYY-MM-DD'), - endDate: dayjs(endDate).utc().format('YYYY-MM-DD'), + startDate: dayjs(startDate).utcOffset(8).format('YYYY-MM-DD'), + endDate: dayjs(endDate).utcOffset(8).format('YYYY-MM-DD'), userIds, autoMatch: true, });