TypeORM集成SQLite时Datetime字段存储日期比输入少一天问题
原因分析
时区转换偏移
客户端传入的是无时区信息的日期字符串'9/4/2024',Node.js的new Date()默认按服务器本地时区解析为当日0点。随后调用toISOString()会将这个本地时间转换为UTC标准时间,比如服务器位于东八区(UTC+8)时,本地9月4日00:00会被转成UTC的9月3日16:00。SQLite的datetime类型存储该UTC时间,用本地时区工具查看时,就会显示为前一天的日期。DTO类型处理错误
DTO中dateOfBirth被标记为string类型并使用@IsString()验证,但实际为日期类型,手动在Service中转换时容易引入时区问题,缺乏统一转换规则。SQLite与TypeORM时区适配问题
SQLite本身不支持时区,TypeORM默认按UTC处理日期,若未配置时区对齐,存储和读取时会出现时区偏移。
解决方法
1. 修正DTO的日期处理逻辑
将DTO中的dateOfBirth改为Date类型,使用class-transformer的@Transform统一解析日期字符串,避免手动转换错误:
import { IsDate, IsEmail, IsNotEmpty, IsString } from 'class-validator'; import { Transform } from 'class-transformer'; import { parse } from 'date-fns'; // 可选,确保解析格式一致 export class CreateUserDto { @IsEmail() @IsNotEmpty() email: string; @IsString() @IsNotEmpty() password: string; @IsString() @IsNotEmpty() fullName: string; @IsDate() @IsNotEmpty() // 若客户端固定传MM/DD/YYYY格式,用parse明确解析规则 @Transform(({ value }) => parse(value, 'MM/dd/yyyy', new Date()), { toClassOnly: true }) // 无需固定格式时可直接转Date:@Transform(({ value }) => new Date(value), { toClassOnly: true }) dateOfBirth: Date; }
2. 移除Service中手动转ISOString的代码
删除Service里的user.dateOfBirth = new Date(user.dateOfBirth).toISOString();,直接将DTO转换好的Date对象传给TypeORM:
async createUserAsync(user: CreateUserDto) { const users = await this.dataService.users.findOneBy({ email: user.email, }); if (users) { throw new BadRequestException(`Email in use email:${user.email}`); } const salt = randomBytes(8).toString('hex'); const hash = (await this._scrypt(user.password, salt, 32)) as Buffer; const result = salt + '.' + hash.toString('hex'); user.password = result; // 移除手动转换ISOString的代码 // user.dateOfBirth = new Date(user.dateOfBirth).toISOString(); const createdUser = await this.dataService.users.create(user); const savedUser = await this.dataService.users.save(createdUser); return savedUser; }
3. 配置TypeORM时区对齐
在TypeORM配置文件中添加timezone配置,确保存储和读取时使用本地时区或指定时区:
// typeorm.config.ts示例 export default { type: 'sqlite', database: 'database.sqlite', entities: [User], synchronize: true, timezone: 'local', // 使用服务器本地时区,或指定具体时区如'Asia/Shanghai' };
4. 统一日期解析库(可选)
如果客户端传的日期格式不固定,建议使用date-fns或moment.js等库解析日期,避免不同环境下new Date()解析规则不一致的问题。
内容的提问来源于stack exchange,提问作者Shofiul

