refactor(server): 日期/月份格式化统一改用 dayjs

- 替换散落的 toISOString().slice(0,10) / split('T')[0] / replace('T',' ')(UTC 语义用 dayjs(...).utc() 保持完全一致)
- Asia/Shanghai 固定时区日期(Intl.DateTimeFormat en-CA)改用 dayjs().utcOffset(8)
- 月份边界 first/last、daysInMonth、nextMonth、shiftDate/addDays 等改 dayjs 简洁实现
- 涉及 attendance/classes/classrooms/room/dashboard/bills/expenses/occupancies/sync/ai-chat/rental 等 28 个文件
This commit is contained in:
2026-08-08 17:52:32 +08:00
parent 92ba2e779f
commit 8936453e84
28 changed files with 82 additions and 112 deletions

View File

@@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Between } from 'typeorm';
import { AttendanceRecord, ClassSchedule } from '../entities';
import type { AttendanceCalendarQueryDto } from './dto/attendance.dto';
import dayjs from '../common/dayjs';
@Injectable()
export class AttendanceCalendarService {
@@ -23,7 +24,7 @@ export class AttendanceCalendarService {
const diff = day === 0 ? -6 : 1 - day; // Monday offset
const monday = new Date(now);
monday.setDate(now.getDate() + diff);
const mondayStr = monday.toISOString().slice(0, 10);
const mondayStr = dayjs(monday).utc().format('YYYY-MM-DD');
return this.buildCalendar(classId, mondayStr);
}
@@ -77,7 +78,7 @@ export class AttendanceCalendarService {
const start = new Date(weekStart);
const end = new Date(start);
end.setDate(start.getDate() + 6);
const endStr = end.toISOString().slice(0, 10);
const endStr = dayjs(end).utc().format('YYYY-MM-DD');
const records = await this.attendanceRepo.find({
where: {

View File

@@ -97,7 +97,7 @@ export class AttendanceGenerationService {
const entities: AttendanceRecord[] = [];
const end = new Date(dateTo);
for (let d = new Date(dateFrom); d <= end; d.setDate(d.getDate() + 1)) {
const dateStr = d.toISOString().slice(0, 10);
const dateStr = dayjs(d).utc().format('YYYY-MM-DD');
const weekDay = d.getDay() === 0 ? 7 : d.getDay();
for (const sched of schedules) {
@@ -147,8 +147,8 @@ export class AttendanceGenerationService {
sunday.setDate(monday.getDate() + 6);
sunday.setHours(23, 59, 59, 999);
const dateFrom = startDate ?? monday.toISOString().slice(0, 10);
const dateTo = endDate ?? sunday.toISOString().slice(0, 10);
const dateFrom = startDate ?? dayjs(monday).utc().format('YYYY-MM-DD');
const dateTo = endDate ?? dayjs(sunday).utc().format('YYYY-MM-DD');
return this.generateAttendanceFromSchedules({
classId,

View File

@@ -2,6 +2,7 @@ import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import { Subject, Observable } from 'rxjs';
import dayjs from '../common/dayjs';
import {
DingAttendanceRaw,
Student,
@@ -238,7 +239,7 @@ export class AttendanceImportService {
}
private formatDate(value: Date): string {
return value.toISOString().slice(0, 10);
return dayjs.utc(value).format('YYYY-MM-DD');
}
/**

View File

@@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { DingLeaveRaw, Student, StudentDingMapping } from '../entities';
import { DingTalkService, DingTalkLeaveResult } from '../integration/dingtalk.service';
import dayjs from '../common/dayjs';
/**
* 钉钉请假数据同步服务。
@@ -152,7 +153,7 @@ export class AttendanceLeaveSyncService {
}
private formatDate(value: Date): string {
return value.toISOString().slice(0, 10);
return dayjs.utc(value).format('YYYY-MM-DD');
}
private async resolveStudentName(dingUserId: string): Promise<string> {

View File

@@ -6,6 +6,7 @@ import { AttendanceImportService } from './attendance-import.service';
import { OperationLogsService } from '../operation-logs/operation-logs.service';
import { AuthorizationService } from '../authorization';
import { logAudit } from '../common/with-audit-log';
import dayjs from '../common/dayjs';
import { extractRequestInfo, type RequestInfoSource } from '../common/request-utils';
import { RequirePermission } from '../auth/decorators/permission.decorator';
import {
@@ -195,11 +196,11 @@ export class AttendanceRecordsController extends AttendanceControllerBase {
source: record.source || '',
punchDevice: record.punchDeviceName || record.punchDeviceId || '',
punchTime: record.punchTime
? record.punchTime.toISOString().replace('T', ' ').substring(0, 19)
? dayjs(record.punchTime).utc().format('YYYY-MM-DD HH:mm:ss')
: '',
remark: record.remark || '',
createdAt: record.createdAt
? record.createdAt.toISOString().replace('T', ' ').substring(0, 19)
? dayjs(record.createdAt).utc().format('YYYY-MM-DD HH:mm:ss')
: '',
});
}

View File

@@ -4,6 +4,7 @@ import { Repository } from 'typeorm';
import { AttendanceRecord, AttendanceDevice } from '../entities';
import type { AttendanceReportQueryDto } from './dto/attendance.dto';
import { attachAttendanceDeviceMappings } from './attendance-device';
import dayjs from '../common/dayjs';
@Injectable()
export class AttendanceReportService {
@@ -141,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 = cutoff.toISOString().slice(0, 10);
const cutoffStr = dayjs(cutoff).utc().format('YYYY-MM-DD');
const qb = this.attendanceRepo
.createQueryBuilder('a')

View File

@@ -295,8 +295,6 @@ export class AttendanceSettlementService {
}
private shiftDate(date: string, days: number): string {
const shifted = new Date(`${date}T00:00:00.000Z`);
shifted.setUTCDate(shifted.getUTCDate() + days);
return shifted.toISOString().slice(0, 10);
return dayjs.utc(date).add(days, 'day').format('YYYY-MM-DD');
}
}