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 | // 开启mongoose调试 |
举例,我有一个schema,里面有个updateDate,我们先来看看不同的日期插入数据库时候,实际的表现。
1 | module.exports = (app) => { |
- 方式一:使用new Date 插入
1
2
3
4
5const deliver = await ctx.model.Delivery.File.create({
fileName: '文件名',
uploadDate: new Date("2021-12-12 00:00:00"),
creatorName: "张三",
})
通过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: "李四",
})
结论:
->
2021-12-12 00:00:00
传入->(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 插入
1
2
3
4
5
6
const deliver = await ctx.model.Delivery.File.create({
fileName: '文件名',
uploadDate: moment("2021-12-12 00:00:00"),
creatorName: '王五',
})
结论:
- ->
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()转为ISODate
new Date("Sat, 11 Dec 2021 16:00:00 GMT")
->2020-12-11T16:00:00.000Z
- -> 入库
others
我们发现当我们的时间只要是精确到时分秒,进入到mongodb数据库后,都能正确的转换成UTC时间。
那我们来试试 年月日的情况
1 | const file = await ctx.model.Delivery.File.create({ |
结论:`
- ->
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()转为ISODate
new 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
验证发现流程:
- -> 传入时间
- ->(ORM)转换为GMT零时区
Sat, 11 Dec 2021 00:00:00 GMT
- ->(ORM)new Date()转为ISODate
new Date("Sat, 11 Dec 2021 00:00:00 GMT")
->2020-12-12T00:00:00.000Z
- -> 查询