一.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种有一个很难受的问题(month
是0-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
,所以month
从0
开始
比较有用的是第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