mongoose与时间相关的debug

前言

众所周知,mongoose的日期格式是ISODate,也就是使用的utc时间,举个栗子:2020-12-11T16:00:00.000Z ,T表示分隔符,Z表示的是UTC。

UTC:世界标准时间,在标准时间上加上8小时,即东八区时间,也就是北京时间。

咱举个例子:

北京时间:2020-12-12 00:00:00对应的国际标准时间格式为:2020-12-11T16:00:00.000Z

当我们的前端页面通过接口拿到我的utc时间后,一般通过new Date(时间),就能快速的转换成当地的时间。

这些周知的我就不再多举例了。

笔记原因

做这个笔记前,我遇到了时间进入到数据库没有准确的转换为utc,于是,好奇心驱使,我们开启mongoose的debug模式,来看看是什么实际mongoose到原生层的实际过程。

1
2
// 开启mongoose调试
this.app.mongoose.set('debug', true)

举例,我有一个schema,里面有个updateDate,我们先来看看不同的日期插入数据库时候,实际的表现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = (app) => {
const mongoose = app.mongoose
const Schema = mongoose.Schema

const FileSchema = new Schema(
{
fileName: { type: String }, // 文件名
uploadDate: { type: Date }, // 上传创建日期
creatorName: { type: String }, // 创建者姓名
},
{
collection: 'sys_files',
}
)

return mongoose.model('File', FileSchema)
}
  • 方式一:使用new Date 插入
    1
    2
    3
    4
    5
    const deliver = await ctx.model.Delivery.File.create({
    fileName: '文件名',
    uploadDate: new Date("2021-12-12 00:00:00"),
    creatorName: "张三",
    })

3suLxw

通过debug日志:

1
Mongoose: sys_files.insertOne({ isDel: false, _id: ObjectId("61b5599cba1fcfeeb79c57cd"), fileName: '文件名', uploadDate: new Date("Sat, 11 Dec 2021 16:00:00 GMT"), creatorName: '张三', __v: 0}, { session: null })

结论:`

我们发现mongoose的ORM层的create实际调用了insertOne,插入的本地时间new Date("2021-12-12 00:00:00")2021-12-11T16:00:00.000Z)到达原生层变成了Sat, 11 Dec 2021 16:00:00 GMT并在进行了一次new Date(),

所以整个orm层到mongodb原生层的过程是这样的:
日期传入-> (ORM)转换为GMT零时区-> (ORM)new Date()转为ISODate -> 入库

  • ->new Date("2021-12-12 00:00:00")传入 -> 2021-12-11T16:00:00.000Z
  • ->(ORM)转换为GMT零时区 Sat, 11 Dec 2021 16:00:00 GMT
  • ->(ORM)new Date()转为ISODate new Date("Sat, 11 Dec 2021 16:00:00 GMT") -> 2020-12-11T16:00:00.000Z
  • -> 入库

我们在拿一个字符串时间和moment时间对象来校验是不是这个过程:日期传入-> (ORM)转换为GMT零时区-> (ORM)new Date()转为ISODate -> 入库

  • 方式二:使用String 字符串插入
    1
    2
    3
    4
    5
    6
    7

    const deliver = await ctx.model.Delivery.File.create({
    fileName: '文件名',
    uploadDate: "2021-12-12 00:00:00",
    creatorName: "李四",
    })

FXDRVZ

结论:

  • ->2021-12-12 00:00:00传入

  • ->(ORM)转换为GMT零时区 Sat, 11 Dec 2021 16:00:00 GMT

  • ->(ORM)new Date()转为ISODatenew Date("Sat, 11 Dec 2021 16:00:00 GMT") -> 2020-12-11T16:00:00.000Z

  • -> 入库

  • 方式三:使用moment 插入

    1
    2
    3
    4
    5
    6

    const deliver = await ctx.model.Delivery.File.create({
    fileName: '文件名',
    uploadDate: moment("2021-12-12 00:00:00"),
    creatorName: '王五',
    })

    k6gMeQ

结论:

  • ->moment("2021-12-12 00:00:00")传入 -> Moment<2021-12-12T00:00:00+08:00>
  • ->(ORM)转换为GMT零时区 Sat, 11 Dec 2021 16:00:00 GMT
  • ->(ORM)new Date()转为ISODatenew Date("Sat, 11 Dec 2021 16:00:00 GMT") -> 2020-12-11T16:00:00.000Z
  • -> 入库

others

我们发现当我们的时间只要是精确到时分秒,进入到mongodb数据库后,都能正确的转换成UTC时间。

那我们来试试 年月日的情况

1
2
3
4
5
const file = await ctx.model.Delivery.File.create({
fileName: '文件名',
uploadDate: new Date('2021-12-12'),
creatorName: '刘九',
})

L8L1y4

结论:`

  • ->new Date('2021-12-12')传入 -> 2021-12-12T00:00:00.000Z注意此处年月日时间转换为UTC时间与上面带时分秒的差异
  • ->(ORM)转换为GMT零时区 Sat, 11 Dec 2021 00:00:00 GMT
  • ->(ORM)new Date()转为ISODatenew Date("Sat, 11 Dec 2021 00:00:00 GMT") -> 2020-12-12T00:00:00.000Z
  • -> 入库

这也就解释了为什么本人在项目中传入2021-12-12 的日期最终却变成了utc 2020-12-12T00:00:00.000Z,也就是为什么new Date()本地时间会多出来8个小时的原因了。

总结

mongoose这个ORM实际做了一步强制new Date()转换为utc时间。所以无论传入什么本地时间,都会强制转换mongodb所需要的ISODate时期格式。
所以无论是moment、dayjs等时间库的时间,最后都会被momgoose强制转换为new Date 的UTC时间。与用什么时间库或时间格式并无直接关系。

番外篇

我们来验证下查询的时候,传入的时间是不是也会通过mongoose自动强制new Date
De6b3B
uuLCtR

验证发现流程:

  • -> 传入时间
  • ->(ORM)转换为GMT零时区 Sat, 11 Dec 2021 00:00:00 GMT
  • ->(ORM)new Date()转为ISODatenew Date("Sat, 11 Dec 2021 00:00:00 GMT") -> 2020-12-12T00:00:00.000Z
  • -> 查询