Meh*_*ran 3 javascript timezone date cross-browser dst
经过很多麻烦,我终于找到了实际的问题.这是由夏令时引起的差距以及如果时区设置在UTC + 3:30(不确定其他时区),不同浏览器的行为会有所不同.
这是一个产生问题的片段(如果您的系统的TZ设置为UTC + 3:30,问题是可重现的):
function zeroPad(n) {
n = n + '';
return n.length >= 2 ? n : new Array(2 - n.length + 1).join('0') + n;
}
document.write("<table border='1' cellpadding='3'><tr><td>Input date</td><td>Parsed timestamp</td><td>Output date</td></tr>");
var m = 22 * 60;
for (var i=0; i<8; i++) {
var input = "3/21/2015 " + zeroPad(Math.floor(m / 60)) + ":" + zeroPad(m % 60) + ":00";
var d = new Date(input);
var output = d.getFullYear()
+'-'+zeroPad(d.getMonth()+1)
+'-'+zeroPad(d.getDate())
+' '+zeroPad(d.getHours())
+':'+zeroPad(d.getMinutes())
+':'+zeroPad(d.getSeconds());
document.write("<tr><td>" + input + "</td><td>" + d.getTime() + "</td><td>" + output + "</td></tr>");
m = m + 15;
}
m = 0;
for (var i=0; i<7; i++) {
var input = "3/22/2015 " + zeroPad(Math.floor(m / 60)) + ":" + zeroPad(m % 60) + ":00";
var d = new Date(input);
var output = d.getFullYear()
+'-'+zeroPad(d.getMonth()+1)
+'-'+zeroPad(d.getDate())
+' '+zeroPad(d.getHours())
+':'+zeroPad(d.getMinutes())
+':'+zeroPad(d.getSeconds());
document.write("<tr><td>" + input + "</td><td>" + d.getTime() + "</td><td>" + output + "</td></tr>");
m = m + 15;
}
document.write("</table>");Run Code Online (Sandbox Code Playgroud)
我在Firefox和Chromium上运行它,这是他们所说的:

红色框内的部分是间隙发生的时间范围.我的问题是像日历这样的组件通常依赖于时间部分设置为"00:00:00"的日期对象,并且他们通过在上一个日期添加一天的时间戳来获得一个生成新日期的循环.因此,一旦一个对象落入3/22/2015 00:00:00它将被考虑3/22/2015 01:00:00或21/3/2015 23:00:00(取决于浏览器),因此生成的日期将从那个时间点无效!
问题是如何检测这些日期对象以及如何对待它们?
Mat*_*int 13
使用moment.js可以省去很多麻烦,并且是实现这种事情的跨浏览器兼容性的最简单方法.
var m = moment.utc("3/22/2015","M/D/YYYY")
var s = m.format("YYYY-MM-DD HH:mm:ss")
Run Code Online (Sandbox Code Playgroud)
使用UTC非常重要,因为您不希望受到用户时区的影响.否则,如果您的日期属于DST转换,则可以将其调整为其他值.(你并不是真的有兴趣参加UTC,你只是用它来保持稳定.)
针对此更新问题的这一部分:
为了简化问题,我正在寻找这样的函数:
Run Code Online (Sandbox Code Playgroud)function date_decomposition(d) { ... } console.log(date_decomposition(new Date("3/22/2015 00:00:00"))); => [2015, 3, 22, 0, 0, 0]
虽然现在很清楚你要求的是什么,但你必须明白,不可能达到你的确切要求,至少不是跨浏览器,跨区域,跨时区的方式.
每个浏览器都有自己的方式来实现字符串到目前的解析.使用构造函数new Date(string)或Date.parse(string)方法时,您将调用特定于实现的功能.
即使实施在所有环境中都是一致的,您也需要区域格式和时区差异来应对.
在区域格式问题的情况下,请考虑01/02/2015.一些地区使用mm/dd/yyyy订购,并将其视为1月2日,而其他地区使用dd/mm/yyyy订购,并将其视为2月1日.(另外,世界上某些地方使用yyyy/mm/dd格式化.)
在时区的情况下,考虑到2014年10月19日午夜(00:00)在巴西 并不存在,并在古巴2014年11月2日午夜(00:00)存在两次.
在不同时区的其他日期和时间也会发生同样的事情.根据您提供的信息,我可以推断您在伊朗时区,在标准时间内使用UTC + 03:30,在白天使用UTC + 04:30.事实上,2105年3月22日午夜(00:00)伊朗并不存在.
当您尝试解析这些无效或不明确的值时,每个浏览器都有自己的行为,并且浏览器之间确实存在差异.
对于无效时间,一些浏览器将向前跳一小时,而其他浏览器将向后跳一小时.
对于模糊的时间,某些浏览器会假设您指的是第一个(日光时间)实例,而其他浏览器则假设您指的是第二个(标准时间)实例.
现在说了所有这些,你当然可以拿一个Date 对象并解构它的部分,非常简单:
function date_decomposition(d) {
return [d.getFullYear(), d.getMonth()+1, d.getDate(),
d.getHours(), d.getMinutes(), d.getSeconds()];
}
Run Code Online (Sandbox Code Playgroud)
但这始终基于代码运行的本地时区.你会看到,在Date对象内部,只有一个值 - 一个数字,表示自那以后经过的毫秒数1970-01-01T00:00:00Z(没有考虑闰秒).该数字是基于UTC的.
因此,在概述中,您遇到的所有问题都与字符串解析为Date对象的方式有关.没有多少关注输出功能将帮助您以完全安全的方式解决这个问题.无论您使用库还是编写自己的代码,您都需要获取原始数据字符串以获得您要查找的结果.当它在一个Date物体中时,你已经失去了使这项工作所需的信息.
顺便说一下,您可以考虑观看我的Pluralsight课程,日期和时间基础知识,其中更详细地介绍了其中的大部分内容.模块7完全是关于JavaScript和这些类型的陷阱.