JS日期时间串格式

一.new Date()

据说有4种方式:

new Date();
new Date(value);
new Date(dateString);
new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]);

第1种用来取时间戳:

// get ts
var now = Date.now || function() {
    return new Date().getTime();
};

第2种把时间戳转Date对象:

// ts to Date
var ts2Date = function(ts) {
    return new Date(ts);
};

第3种把日期时间字符串转Date对象:

// string to Date
// eg.  '2016/10/01 12:01:02'
//      '2016/10/01'
var str2Date = function(str) {
    return new Date(str);
};

第4种一般不用,因为第4种有一个很难受的问题(month0-11):

// set Date
// eg.  2016, 9, 1, 12, 1, 2
//      2016, 9, 1
var setDate = function(y, m, d, h, mm, s, ms) {
    return new Date(y, m, d, h, mm, s, ms);
};
// getter/setter中,month是0-11
var date = new Date();  // 今天是2016-10-01
date.getMonth();    // 9
date.setMonth(10);
date.toString();    // Tue Nov 01 2016 11:46:58 GMT+0800 (China Standard Time)

第4种方法内部用的可能是setter,所以month0开始

比较有用的是第3种,因为一般需要处理的日期串都是ISO标准格式,比如2016-10-01 12:01:02,但这在JS中是非标准的

二.日期时间串格式

非标准格式

先看这两个东西:

// new一个0区时间
new Date('2016-10-01'); // Sat Oct 01 2016 08:00:00 GMT+0800 (China Standard Time)
// new一个当前时区时间
new Date('2016/10/01'); // Sat Oct 01 2016 00:00:00 GMT+0800 (China Standard Time)

第二个正确,第一个多了8小时,浏览器认为第一种传入的是0区时间,就为东八区用户私自加了8小时,分别等价于:

// Sat Oct 01 2016 08:00:00 GMT+0800 (China Standard Time)
new Date('01 October, 2016 GMT+0000');
// Sat Oct 01 2016 00:00:00 GMT+0800 (China Standard Time)
new Date('01 October, 2016 GMT+0800');

所以千万不要用yyyy-MM-dd,就算非常确定想要new个0区时间,也不应该这么做,因为别人可能看不懂,如果真想要new个0区时间,应该用其等价格式(带有GMT时区标识的)

添上时间'HH:mm:ss'再试试:

// new一个0区时间
// Sat Oct 01 2016 12:01:02 GMT+0800 (China Standard Time)
new Date('2016-10-01 12:01:02');
// new一个当前时区时间
// Sat Oct 01 2016 12:01:02 GMT+0800 (China Standard Time)
new Date('2016/10/01 12:01:02');

竟然一致了,但是Safari(包括IOS Safari)不支持yyyy-MM-dd HH:mm:ss格式,Safari从开始到现在(2016-10-01)都不支持这种格式,因为是非规范的,可能不打算支持

虽然yyyy/MM/dd HH:mm:ss也是非规范的,但因为历史悠久,包括IE6在内的所有浏览器都支持该格式

所以,得到广泛支持的非标准格式有两个:

// eg. 2016/10/01 12:01:02
yyyy/MM/dd HH:mm:ss
// eg. 01 October, 2016 12:01:02 GMT+0800
week, dd month yyyy HH:mm:ss GMT±HHmm

前者默认当前时区时间,后者可以设置时区

标准格式

直到ES5.1 15.9.1.15 Date Time String Format才有了日期字符串的格式标准:

// 0区时间
YYYY-MM-DDTHH:mm:ss.sssZ
// 东边时间
YYYY-MM-DDTHH:mm:ss.sss+HH:mm
// 西边时间
YYYY-MM-DDTHH:mm:ss.sss-HH:mm

这份规范是2011年6月的,所以兼容性可想而知,样子比较奇怪,但恪守标准的Safari是支持的,例如:

// 0区时间
// Sat Oct 01 2016 20:01:02 GMT+0800 (CST)
new Date('2016-10-01T12:01:02Z')
// 东8区时间
// Sat Oct 01 2016 12:01:02 GMT+0800 (CST)
new Date('2016-10-01T12:01:02+08:00')

同样的,0区时间还是会私自被加上时区偏移,那么很容易想到这两个:

new Date('2016-10-01T12:01:02+00:00')
new Date('2016-10-01T12:01:02-00:00')

它们全都等价于Z,同样会被本地化偏移

三.GMT与UTC

一般可以认为这两个东西是相同的,都表示世界标准时间,而北京位于东八区,那么:

// 一般可以认为
北京时间 === UTC+8 === GMT+8

UTC与GMT在时区的概念上一致,但UTC强调精确,误差更小,这个误差是相对UT来说的

GMT

GMT(Greenwich Mean Time 格林威治标准时间)是最早定义的世界时间。1884年,各国代表跑去华盛顿开会,对时区达成共识,以本初子午线为中心(位于0区),向东1到11区,向西1到11区,共用12区(叫“东西12区”),一共24个时区

由0区发布标准时间,然后其它区加上对应的偏移量得到本地时间

UT

UT(Universal Time 世界时)是基于天体观察计算出来的时间。UT本身是一个广泛的概念,其下包括UT0,UT1,UT2等。其中UT0是完全按照天体运行计算出来的时间,UT1是在UT0的基础上做了一些调整,UT2是在UT0和UT1的基础上又进行了一些调整。由于天体运行的一些不确定性(比如地球的自转并非匀速的,而是以复杂的方式进行着加速和减速),所以UT时间并不是均匀流过的。

UT就是理论上的“正确”时间

UTC

UTC(Universal Time Coordinate 协调世界时)是基于原子时钟的时间。什么是原子时钟?个人认为就是一个很小的,长度固定的,不可再分的时间段。所以UTC的时间是均匀的。为了能够尽量减小和UT时间的误差,UTC引入了闰秒(在某些年份的最后一分钟是61秒),以确保UTC是UT1之间的误差在0.9秒之内。

UTC认同GMT的时区概念,所以UTC相当于给GMT换了块更高级的表,GMT用的什么表就不知道了

四.潜在问题

与北京时间相差最多的是西12区(只有几个小岛):

deltaH = (+8) - (-12) = 20

如果做了一个本地时间判断,比如:

if (new Date() > new Date('2016/10/02')) {
    alert('a new day');
}

那么不同时区的用户都将在手头的设备显示2016年10月2日0点0分非0毫秒时看到a new day,这没问题,但如果场景是这样:

if (new Date() > new Date('2016/10/02')) {
    alert('考试时间到,3秒后回到首页');
    setTimeout(function() {
        location.href = '/index.html';
    }, 3000);
}

此时问题就大了,北京时间2016年10月2日0点0分非0毫秒,试卷状态变更为CLOSED,不再接受提交,那么位于其它时区的用户就发现问题了:

  • 住在西12区的朋友还有20个小时慢慢答题,交卷时发现人家不要

  • 住在东12区的朋友更惨,被提前4小时收卷了

当然,我们一般都是进页面先问服务要状态,以服务器时间为准,因为本地时间不可靠(用户可以随便修改设备时间),另一个原因就是上面提到的:时区的差异会让位于其它时区的用户很困惑

所以,即便非要用本地时间判断,也应该这样做:

if (new Date() > new Date('02 October, 2016 GMT+0800')) {
    alert('考试时间到,3秒后回到首页');
    setTimeout(function() {
        location.href = '/index.html';
    }, 3000);
}

拿用户本地时间与北京时间比,至少在用户设备时间的情况下,能保证状态一致。当然,手动改时间的问题是没有办法避免的(比如以前的某些农场游戏用的是本地时间),所以尽量不要把本地时间作为判断依据

五.总结

本地时间是指设备所在时区时间

兼容性最好的时间串格式:

// 不支持时区
2016/10/01 12:01:02
yyyy/MM/dd HH:mm:ss
// 支持时区
new Date('01 October, 2016 GMT+0800')
week, dd month yyyy HH:mm:ss GMT±HHmm

ES5.1标准时间串格式:

2016-10-01T12:01:02+08:00
YYYY-MM-DDTHH:mm:ss.sss±HH:mm

千万不要用的时间串格式:

// 兼容性没问题,但会被强制偏移,比如设备在北京就+8小时
yyyy-MM-dd
// Safari全家都不支持,IE6-11都不支持
yyyy-MM-dd HH:mm:ss

参考资料

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code