javascript与mongodb的日期时区问题

默认情况下,js的日期生成是当地时区,但默认显示是按照GMT显示的:

> var c = new Date('2017-10-31 06:00:00');
> c
2017-10-30T22:00:00.000Z

但是日期对象中有时区信息,可以获取:

> c.getTimezoneOffset()
-480  // 以分钟为单位 晚于GMT则为正,早于则为负

在获取关于日期/时间/星期等信息是按照当地时区的日期来获取的

> c.getDate();
31
> c.getHours();
6

但是要注意的是,时间戳永远是按照GMT来统计的,即按照1970-01-01T00:00:00.000Z开始算的毫秒数

> c.getTime();
1509400800000
> 1509400800000 / (60*60*1000*24) % 10000 % 1000 %100 %10 % 1 * 24
22.000000000029104 

也就是说,按照时间戳来算的话现在是晚上22点。
不过一般情况下,直观上都是将时间戳转换成格式化的时间来显示。

同样的mongodb的时间也是有时区信息的,也就是GMT时区,程序中的代码在写入数据库的时候会自动的转换成GMT保存,而在查询的时候也会像在js中那样显示。
这种显示在绝大多数情况夏不会引起问题,只要在程序中保证使用的是本地时区,解释器和数据库驱动会自动去处理。
极少遇到的一个困境是使用数据库本身的聚合操作进行统计的情况:
假设我要使用mongodb的group聚合方法来统计每天创建的document的数量,如果按照如下处理的话,会出现问题:

db.sales.aggregate(
   [
      {
        $group : {
           _id : { month: { $month: "$date" }, day: { $dayOfMonth: "$date" }, year: { $year: "$date" } },
           totalPrice: { $sum: { $multiply: [ "$price", "$quantity" ] } },
           averageQuantity: { $avg: "$quantity" },
           count: { $sum: 1 }
        }
      }
   ]
)

这里的聚合是按照数据库的时区来处理的,也就是会按照GMT时区来进行聚合,造成的结果是本地时区00:00-08:00的数据会后退一天,而我们期望的结果是按照当地时区的日期来统计数据。
一个解决问题是在group之前对日期进行处理,先将日期加上时区偏差,然后再进行group:

db.sales.aggregate(
   [
      {
        $group : {
           _id : { month: { $month: { "$add": [ "$date", 28800000] } }, day: { $dayOfMonth: { "$add": [ "$date", 28800000] } }, year: { $year: { "$add": [ "$date", 28800000] } } },
           totalPrice: { $sum: { $multiply: [ "$price", "$quantity" ] } },
           averageQuantity: { $avg: "$quantity" },
           count: { $sum: 1 }
        }
      }
   ]
)

这样以后进行group之后的日期就是按照本地时区来统计的,其中28800000 = 8 * 60 * 60 * 1000,东8时区。

posted @ 2017-11-02 23:29  zhangjpn  阅读(1083)  评论(0编辑  收藏  举报