消除 Javascript 夏令时差距,一种跨浏览器的解决方案
Eliminating Javascript daylight saving time gap, a cross-browser solution
经过一番折腾,我终于找到了真正的问题所在。这是由夏令时引起的差距,如果时区设置为 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>");
我在 Firefox 和 Chromium 上 运行 它是这样的,他们是这样说的:
红框内的部分为gap发生的时间范围。我的问题是像日历这样的组件通常依赖于时间部分设置为“00:00:00”的日期对象,并且它们有一个循环,通过在前一个日期上添加一天的时间戳来生成新日期。因此,一旦对象落入 3/22/2015 00:00:00
,它将被视为 3/22/2015 01:00:00
或 21/3/2015 23:00:00
(取决于浏览器),因此生成的日期将从那个时间点开始无效!
问题是如何检测此类日期对象以及如何处理它们?
您必须使用变长参数构造函数。
function convert(dateStr) {
var date = dateStr.split('/');
return new Date(date[2], (date[0] | 0) - 1, date[1]);
}
function formatDate(d) {
function pad(i) {
return i < 10 ? '0' + i : i;
}
return d.getFullYear()
+ '-' + pad(d.getMonth() + 1)
+ '-' + pad(d.getDate())
+ ' ' + pad(d.getHours())
+ ':' + pad(d.getMinutes())
+ ':' + pad(d.getSeconds())
}
var str = formatDate(convert('3/22/2015'));
document.write(str);
使用 moment.js 会让您省去很多麻烦,并且是实现此类事情的跨浏览器兼容性的最简单方法。
var m = moment.utc("3/22/2015","M/D/YYYY")
var s = m.format("YYYY-MM-DD HH:mm:ss")
为此使用 UTC 很重要,因为您不希望受到用户时区的影响。否则,如果您的日期进入 DST 转换,它可能会调整为其他值。 (您并不是 真正 对 UTC 感兴趣,您只是为了稳定使用它。)
针对您更新问题的这一部分:
To simplify the question, I'm looking for a function like this:
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)
方法时,您调用的功能是 实现特定的 .
There's a chart showing many of the formatting differences here.
即使在所有环境中实现一致,您仍需要应对区域格式和时区差异。
如果出现区域格式问题,请考虑 01/02/2015
。一些地区使用 mm/dd/yyyy
排序并将其视为 1 月 2 日,而其他地区使用 dd/mm/yyyy
排序并将其视为 2 月 1 日。 (此外,世界上的某些地区使用 yyyy/mm/dd
格式。)
Wikipedia has a list and map of where in the world different date formats are used.
在时区的情况下,考虑October 19th, 2014 at Midnight (00:00) in Brazil did not exist, and November 2nd, 2014 at Midnight (00:00) in Cuba存在两次。
同样的事情发生在不同时区的其他日期和时间。根据你提供的信息,我可以推断你在伊朗时区,标准时间使用UTC+03:30,白天使用UTC+04:30。的确,March 22, 2105 at Midnight (00:00) did not exist in Iran.
当您尝试解析这些无效或不明确的值时,每个浏览器都有自己的行为,并且浏览器之间确实存在差异。
对于无效时间,有些浏览器会向前跳一个小时,而有些浏览器会向后跳一个小时。
对于模棱两可的时间,一些浏览器会假定您指的是第一个(夏令时)实例,而其他浏览器会假定您指的是第二个(标准时间)实例。
综上所述,您当然可以拿一个 Date
对象 并解构它的各个部分,非常简单:
function date_decomposition(d) {
return [d.getFullYear(), d.getMonth()+1, d.getDate(),
d.getHours(), d.getMinutes(), d.getSeconds()];
}
但这将始终基于代码为运行的本地时区。您会看到,在 Date
对象内部,只有一个值 - 一个数字,表示自 1970-01-01T00:00:00Z
以来经过的毫秒数(不考虑闰秒)。该数字是基于 UTC 的。
因此,总而言之,您遇到的所有问题都与将字符串解析为 Date
对象的方式有关。再多关注输出函数也无法帮助您以完全安全的方式解决该问题。无论您使用库还是编写自己的代码,都需要获取原始数据字符串才能获得所需的结果。当它在 Date
对象中时,您已经丢失了完成这项工作所需的信息。
顺便说一下,您可以考虑观看我的 Pluralsight 课程 Date and Time Fundamentals,其中更详细地介绍了其中的大部分内容。第 7 单元完全是关于 JavaScript 和这些陷阱的。
日期解析(修改问题前的第一个答案)
这将解析输入并隔离每个值。
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,d.getUTCDate(),
d.getUTCHours(),d.getUTCMinutes(),d.getUTCSeconds() ];
};
function pdte() {
var ndte=new Date(Date.parse(document.getElementById('datein').value));
var ar=dateDecomposition(ndte);
document.getElementById('yyyy').innerHTML=ar[0];
document.getElementById('mm').innerHTML=ar[1];
document.getElementById('dd').innerHTML=ar[2];
document.getElementById('HH').innerHTML=ar[3];
document.getElementById('MN').innerHTML=ar[4];
document.getElementById('SS').innerHTML=ar[5];
ar=dateUTCDecomposition(ndte);
document.getElementById('Uyyyy').innerHTML=ar[0];
document.getElementById('Umm').innerHTML=ar[1];
document.getElementById('Udd').innerHTML=ar[2];
document.getElementById('UHH').innerHTML=ar[3];
document.getElementById('UMN').innerHTML=ar[4];
document.getElementById('USS').innerHTML=ar[5];
document.getElementById('test').innerHTML='';
for (var i=1426896000000;i<1427068800000;i+=1800000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(ar[0]+"/"+ar[1]+"/"+ar[2]+" "+ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail=1 };
document.getElementById('test').innerHTML+=fail+" -- "+ar.join(":")+'<br />';
};
};
document.getElementById('datein').addEventListener('change',pdte,true);
window.onload=pdte
<input id="datein" value="2015/03/14 12:34:56" />
<table border=1>
<tr><td>Locale</td>
<td id="yyyy"></td>
<td id="mm"></td>
<td id="dd"></td>
<td id="HH"></td>
<td id="MN"></td>
<td id="SS"></td>
</tr><tr><td>UTC</td>
<td id="Uyyyy"></td>
<td id="Umm"></td>
<td id="Udd"></td>
<td id="UHH"></td>
<td id="UMN"></td>
<td id="USS"></td>
</tr>
</table>
<div id="test"></div>
测试规则#1
正在搜索从 2000 年到 2015 年的 差距,以 1/2 小时为步长
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,
d.getUTCDate(), d.getUTCHours(),
d.getUTCMinutes(),d.getUTCSeconds() ];
};
for (var i=946684800000;i<1420070400000;i+=1800000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail=1 };
if (fail!==0) {
document.getElementById('test').innerHTML+=fail+
" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
}
}
div#test {
font-family: mono, monospace, terminal;
font-size: .8em;
}
<div id="test"> </div>
总之,这似乎不是 javascript 错误,但我在 ExtJs 中发现了这个:
Ext.Date.parse - problem when parsing dates near daylight time change.
第二个测试规则
由于这个问题被修改了几次,有一个测试规则在过去 10 年内有效,以 15 分钟为步长,用于显示 gaps AND overlaps:
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,
d.getUTCDate(), d.getUTCHours(),
d.getUTCMinutes(),d.getUTCSeconds() ];
};
var end=(Date.now()/900000).toFixed(0)*900000;
var start=end-365250*86400*10;
for (var i=start;i<end;i+=900000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail++ };
if (fail!==0) {
document.getElementById('test').innerHTML+=
fail+" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
} else {
ar=dateDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
if (cmp != i) {
document.getElementById('test').innerHTML+=
fail+" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
}
}
}
div#test {
font-family: mono, monospace, terminal;
font-size: .8em;
}
<div id="test"> </div>
您可以通过 运行 您喜欢的浏览器在一些不同的时区下进行测试:
env TZ=Iran firefox
env TZ=Iran opera
env TZ=Iran chrome
env TZ=Iran chromium
env TZ=Europe/Berlin firefox
env TZ=Europe/Berlin opera
env TZ=Europe/Berlin chrome
env TZ=US/Central firefox
env TZ=US/Central opera
env TZ=US/Central chrome
env TZ=Asia/Gaza firefox
env TZ=Asia/Gaza opera
env TZ=Asia/Gaza chromium
我希望您能够为您的 ExtJS 扩展 创建自己的测试规则,从最后一个片段中汲取灵感。
屏幕截图
TZ=伊朗铬
TZ=伊朗冰鼬 (firefox)
TZ=US/Central 铬
TZ=US/Central冰鼬
TZ=Asia/Gaza 铬
TZ=Asia/Gaza冰鼬
JavaScript 日期以浏览器的本地时间解析,除非您使用 ISO 日期格式 "2015-03-22"
,它将被解析为 UTC。在您的情况下,由于您控制服务器,并且您知道日期实际上是 UTC,因此您可以保持格式不变并解析日期,然后从日期中减去时区偏移值以将其转换为 UTC。以下函数应该 return 完全符合您在上一个用例中请求的结果。您显然可以修改它以按照您认为合适的任何方式格式化字符串。:
function date_decomposition(dateString) {
var local = new Date(dateString);
var d = new Date(local.valueOf() - (local.getTimezoneOffset() * 60000));
return [ d.getUTCFullYear(),
(d.getUTCMonth() +1 ),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds() ];
}
关于此方法的唯一问题是,如果您将服务器上的日期格式更改为使用 ISO 8601 日期格式,它仍会减去时区偏移量,并且您最终会得到错误的输出。
为了 100% 安全,您应该 使用 ISO 8601 日期格式格式化日期字符串,例如 "2015-03-22"
或 "2015-03-22T00:00:00Z"
。然后你可以删除从解析日期中减去时区偏移量的行,它可以与任何 ISO 日期字符串一起正常工作:
function date_decomposition(dateString) {
var d = new Date(dateString);
return [ d.getUTCFullYear(),
(d.getUTCMonth() +1 ),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds() ];
}
更多的是方法上的问题。浏览器的行为完全符合预期,因为它们都知道 Upcoming Daylight Saving Time Clock Changes。因此,您的 Chromium 浏览器知道在 3 月 22 日当地时间 12:00 午夜,需要将时钟向前移动到 1:00 AM,而您的 Firefox 浏览器知道在 3 月 22 日当地时间周六至周日午夜, 需要将时钟调回星期六下午 11:00。
根据 UTC 定义不同的时区是有原因的。 UTC 是一个标准,而不是时区。 如果你想跨浏览器,你需要遵守标准。坚持 UTC,存储在 UTC 中,并在 UTC 中进行所有日期计算。如果您有日历组件,请告诉您的组件您正在传递 UTC 日期。当您需要向用户显示日期(因此依赖于浏览器)时,您(如果您没有使用组件)或您的日历组件负责检查夏令时是否有效,并调整要显示的日期因此。
¿你怎么知道夏令时是否有效?
检查此 article about adding methods to the date object 以完全做到这一点。代码背后的逻辑是,无论夏令时是否生效,UTC 和本地时间之间的时区差异是不同的。如果你只是想检查这里的代码是两个函数:
Date.prototype.stdTimezoneOffset = function() {
var jan = new Date(this.getFullYear(), 0, 1);
var jul = new Date(this.getFullYear(), 6, 1);
return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}
Date.prototype.dst = function() {
return this.getTimezoneOffset() < this.stdTimezoneOffset();
}
我找到了你的问题的解决方案
一定会帮到你
首先我想解释一下夏令时
日照时间:夏季日照时间较长;太阳升得早,落得晚。几十年前,国会决定晚上的日光比早上多,因此他们提出了夏令时。 spring 时钟向前移动一小时,秋季则向后移动一小时。这样,您就不会在 4:00 a.m 看到日出。日落在 8:00 p.m,但日出在 5:00 a.m。和日落 9:00 p.m,在盛夏。他们认为人们会在晚上用更少的电来照亮他们的房子,等等。
所以现在,该国大部分地区在夏季使用夏令时,在冬季使用标准时间。 yahoo link
首先我想给你一个问题的解决方案:
将您的计算机(在 win 7 上测试)的时区设置为 GMT+3:30,并将您的日期更改为 2015 年 3 月 23 日。
和 运行 您在两种浏览器上的代码 (chromium/mozila)。
您会发现相同的结果(即输入时间增加 1 小时)
是的,问题解决了。
您的输入时间将增加 +1 小时,因为在 2015 年 3 月 22 日 12:00 AM(格林威治标准时间 +3:30)之后,夏令时开始。 (you can check it here also from here here)
现在的原因是:
请查看此屏幕截图:http://prntscr.com/6j4gdn(在 win 7 上)
在这里你可以看到“夏令时开始于 2015 年 3 月 22 日 12:00 AM。
表示时间将在 2015 年 3 月 22 日上午 12:00 后提前 1 小时。(仅适用于那些使用夏令时系统的国家)
因此,如果您将时间更改为 2015 年 3 月 23 日,则夏令时开始于(例如 GMT+3:30)
你会发现在两种浏览器上的输出都是正确的。
我猜chrome总是使用夏令时系统(仅适用于那些使用夏令时系统的国家)
我已经通过更改系统时间对其进行了测试(但不是 100% 确定)
另一方面,mozila 支持夏令时系统。因此,在 2015 年 3 月 22 日 12:00 AM(格林威治标准时间+3:30)之后,mozila 将为您输入的日期时间增加 1 小时
我想这对你们所有人都有帮助。
经过一番折腾,我终于找到了真正的问题所在。这是由夏令时引起的差距,如果时区设置为 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>");
我在 Firefox 和 Chromium 上 运行 它是这样的,他们是这样说的:
红框内的部分为gap发生的时间范围。我的问题是像日历这样的组件通常依赖于时间部分设置为“00:00:00”的日期对象,并且它们有一个循环,通过在前一个日期上添加一天的时间戳来生成新日期。因此,一旦对象落入 3/22/2015 00:00:00
,它将被视为 3/22/2015 01:00:00
或 21/3/2015 23:00:00
(取决于浏览器),因此生成的日期将从那个时间点开始无效!
问题是如何检测此类日期对象以及如何处理它们?
您必须使用变长参数构造函数。
function convert(dateStr) {
var date = dateStr.split('/');
return new Date(date[2], (date[0] | 0) - 1, date[1]);
}
function formatDate(d) {
function pad(i) {
return i < 10 ? '0' + i : i;
}
return d.getFullYear()
+ '-' + pad(d.getMonth() + 1)
+ '-' + pad(d.getDate())
+ ' ' + pad(d.getHours())
+ ':' + pad(d.getMinutes())
+ ':' + pad(d.getSeconds())
}
var str = formatDate(convert('3/22/2015'));
document.write(str);
使用 moment.js 会让您省去很多麻烦,并且是实现此类事情的跨浏览器兼容性的最简单方法。
var m = moment.utc("3/22/2015","M/D/YYYY")
var s = m.format("YYYY-MM-DD HH:mm:ss")
为此使用 UTC 很重要,因为您不希望受到用户时区的影响。否则,如果您的日期进入 DST 转换,它可能会调整为其他值。 (您并不是 真正 对 UTC 感兴趣,您只是为了稳定使用它。)
针对您更新问题的这一部分:
To simplify the question, I'm looking for a function like this:
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)
方法时,您调用的功能是 实现特定的 .There's a chart showing many of the formatting differences here.
即使在所有环境中实现一致,您仍需要应对区域格式和时区差异。
如果出现区域格式问题,请考虑
01/02/2015
。一些地区使用mm/dd/yyyy
排序并将其视为 1 月 2 日,而其他地区使用dd/mm/yyyy
排序并将其视为 2 月 1 日。 (此外,世界上的某些地区使用yyyy/mm/dd
格式。)Wikipedia has a list and map of where in the world different date formats are used.
在时区的情况下,考虑October 19th, 2014 at Midnight (00:00) in Brazil did not exist, and November 2nd, 2014 at Midnight (00:00) in Cuba存在两次。
同样的事情发生在不同时区的其他日期和时间。根据你提供的信息,我可以推断你在伊朗时区,标准时间使用UTC+03:30,白天使用UTC+04:30。的确,March 22, 2105 at Midnight (00:00) did not exist in Iran.
当您尝试解析这些无效或不明确的值时,每个浏览器都有自己的行为,并且浏览器之间确实存在差异。
对于无效时间,有些浏览器会向前跳一个小时,而有些浏览器会向后跳一个小时。
对于模棱两可的时间,一些浏览器会假定您指的是第一个(夏令时)实例,而其他浏览器会假定您指的是第二个(标准时间)实例。
综上所述,您当然可以拿一个 Date
对象 并解构它的各个部分,非常简单:
function date_decomposition(d) {
return [d.getFullYear(), d.getMonth()+1, d.getDate(),
d.getHours(), d.getMinutes(), d.getSeconds()];
}
但这将始终基于代码为运行的本地时区。您会看到,在 Date
对象内部,只有一个值 - 一个数字,表示自 1970-01-01T00:00:00Z
以来经过的毫秒数(不考虑闰秒)。该数字是基于 UTC 的。
因此,总而言之,您遇到的所有问题都与将字符串解析为 Date
对象的方式有关。再多关注输出函数也无法帮助您以完全安全的方式解决该问题。无论您使用库还是编写自己的代码,都需要获取原始数据字符串才能获得所需的结果。当它在 Date
对象中时,您已经丢失了完成这项工作所需的信息。
顺便说一下,您可以考虑观看我的 Pluralsight 课程 Date and Time Fundamentals,其中更详细地介绍了其中的大部分内容。第 7 单元完全是关于 JavaScript 和这些陷阱的。
日期解析(修改问题前的第一个答案)
这将解析输入并隔离每个值。
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,d.getUTCDate(),
d.getUTCHours(),d.getUTCMinutes(),d.getUTCSeconds() ];
};
function pdte() {
var ndte=new Date(Date.parse(document.getElementById('datein').value));
var ar=dateDecomposition(ndte);
document.getElementById('yyyy').innerHTML=ar[0];
document.getElementById('mm').innerHTML=ar[1];
document.getElementById('dd').innerHTML=ar[2];
document.getElementById('HH').innerHTML=ar[3];
document.getElementById('MN').innerHTML=ar[4];
document.getElementById('SS').innerHTML=ar[5];
ar=dateUTCDecomposition(ndte);
document.getElementById('Uyyyy').innerHTML=ar[0];
document.getElementById('Umm').innerHTML=ar[1];
document.getElementById('Udd').innerHTML=ar[2];
document.getElementById('UHH').innerHTML=ar[3];
document.getElementById('UMN').innerHTML=ar[4];
document.getElementById('USS').innerHTML=ar[5];
document.getElementById('test').innerHTML='';
for (var i=1426896000000;i<1427068800000;i+=1800000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(ar[0]+"/"+ar[1]+"/"+ar[2]+" "+ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail=1 };
document.getElementById('test').innerHTML+=fail+" -- "+ar.join(":")+'<br />';
};
};
document.getElementById('datein').addEventListener('change',pdte,true);
window.onload=pdte
<input id="datein" value="2015/03/14 12:34:56" />
<table border=1>
<tr><td>Locale</td>
<td id="yyyy"></td>
<td id="mm"></td>
<td id="dd"></td>
<td id="HH"></td>
<td id="MN"></td>
<td id="SS"></td>
</tr><tr><td>UTC</td>
<td id="Uyyyy"></td>
<td id="Umm"></td>
<td id="Udd"></td>
<td id="UHH"></td>
<td id="UMN"></td>
<td id="USS"></td>
</tr>
</table>
<div id="test"></div>
测试规则#1
正在搜索从 2000 年到 2015 年的 差距,以 1/2 小时为步长
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,
d.getUTCDate(), d.getUTCHours(),
d.getUTCMinutes(),d.getUTCSeconds() ];
};
for (var i=946684800000;i<1420070400000;i+=1800000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail=1 };
if (fail!==0) {
document.getElementById('test').innerHTML+=fail+
" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
}
}
div#test {
font-family: mono, monospace, terminal;
font-size: .8em;
}
<div id="test"> </div>
总之,这似乎不是 javascript 错误,但我在 ExtJs 中发现了这个: Ext.Date.parse - problem when parsing dates near daylight time change.
第二个测试规则
由于这个问题被修改了几次,有一个测试规则在过去 10 年内有效,以 15 分钟为步长,用于显示 gaps AND overlaps:
function dateDecomposition(d) {
return [ d.getFullYear(),d.getMonth()+1,d.getDate(),
d.getHours(),d.getMinutes(),d.getSeconds() ];
};
function dateUTCDecomposition(d) {
return [ d.getUTCFullYear(),d.getUTCMonth()+1,
d.getUTCDate(), d.getUTCHours(),
d.getUTCMinutes(),d.getUTCSeconds() ];
};
var end=(Date.now()/900000).toFixed(0)*900000;
var start=end-365250*86400*10;
for (var i=start;i<end;i+=900000) {
ar=dateUTCDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
var re=dateDecomposition(new Date(cmp));
var fail=0;
for (var l=0;l<6;l++) { if (ar[l]!==re[l]) fail++ };
if (fail!==0) {
document.getElementById('test').innerHTML+=
fail+" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
} else {
ar=dateDecomposition(new Date(i));
var cmp=Date.parse(
ar[0]+"/"+ar[1]+"/"+ar[2]+" "+
ar[3]+":"+ar[4]+":"+ar[5]);
if (cmp != i) {
document.getElementById('test').innerHTML+=
fail+" -- "+new Date(i)+" -- "+ar.join(":")+'<br />';
}
}
}
div#test {
font-family: mono, monospace, terminal;
font-size: .8em;
}
<div id="test"> </div>
您可以通过 运行 您喜欢的浏览器在一些不同的时区下进行测试:
env TZ=Iran firefox
env TZ=Iran opera
env TZ=Iran chrome
env TZ=Iran chromium
env TZ=Europe/Berlin firefox
env TZ=Europe/Berlin opera
env TZ=Europe/Berlin chrome
env TZ=US/Central firefox
env TZ=US/Central opera
env TZ=US/Central chrome
env TZ=Asia/Gaza firefox
env TZ=Asia/Gaza opera
env TZ=Asia/Gaza chromium
我希望您能够为您的 ExtJS 扩展 创建自己的测试规则,从最后一个片段中汲取灵感。
屏幕截图
TZ=伊朗铬JavaScript 日期以浏览器的本地时间解析,除非您使用 ISO 日期格式 "2015-03-22"
,它将被解析为 UTC。在您的情况下,由于您控制服务器,并且您知道日期实际上是 UTC,因此您可以保持格式不变并解析日期,然后从日期中减去时区偏移值以将其转换为 UTC。以下函数应该 return 完全符合您在上一个用例中请求的结果。您显然可以修改它以按照您认为合适的任何方式格式化字符串。:
function date_decomposition(dateString) {
var local = new Date(dateString);
var d = new Date(local.valueOf() - (local.getTimezoneOffset() * 60000));
return [ d.getUTCFullYear(),
(d.getUTCMonth() +1 ),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds() ];
}
关于此方法的唯一问题是,如果您将服务器上的日期格式更改为使用 ISO 8601 日期格式,它仍会减去时区偏移量,并且您最终会得到错误的输出。
为了 100% 安全,您应该 使用 ISO 8601 日期格式格式化日期字符串,例如 "2015-03-22"
或 "2015-03-22T00:00:00Z"
。然后你可以删除从解析日期中减去时区偏移量的行,它可以与任何 ISO 日期字符串一起正常工作:
function date_decomposition(dateString) {
var d = new Date(dateString);
return [ d.getUTCFullYear(),
(d.getUTCMonth() +1 ),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds() ];
}
更多的是方法上的问题。浏览器的行为完全符合预期,因为它们都知道 Upcoming Daylight Saving Time Clock Changes。因此,您的 Chromium 浏览器知道在 3 月 22 日当地时间 12:00 午夜,需要将时钟向前移动到 1:00 AM,而您的 Firefox 浏览器知道在 3 月 22 日当地时间周六至周日午夜, 需要将时钟调回星期六下午 11:00。
根据 UTC 定义不同的时区是有原因的。 UTC 是一个标准,而不是时区。 如果你想跨浏览器,你需要遵守标准。坚持 UTC,存储在 UTC 中,并在 UTC 中进行所有日期计算。如果您有日历组件,请告诉您的组件您正在传递 UTC 日期。当您需要向用户显示日期(因此依赖于浏览器)时,您(如果您没有使用组件)或您的日历组件负责检查夏令时是否有效,并调整要显示的日期因此。
¿你怎么知道夏令时是否有效?
检查此 article about adding methods to the date object 以完全做到这一点。代码背后的逻辑是,无论夏令时是否生效,UTC 和本地时间之间的时区差异是不同的。如果你只是想检查这里的代码是两个函数:
Date.prototype.stdTimezoneOffset = function() {
var jan = new Date(this.getFullYear(), 0, 1);
var jul = new Date(this.getFullYear(), 6, 1);
return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
}
Date.prototype.dst = function() {
return this.getTimezoneOffset() < this.stdTimezoneOffset();
}
我找到了你的问题的解决方案
一定会帮到你
首先我想解释一下夏令时
日照时间:夏季日照时间较长;太阳升得早,落得晚。几十年前,国会决定晚上的日光比早上多,因此他们提出了夏令时。 spring 时钟向前移动一小时,秋季则向后移动一小时。这样,您就不会在 4:00 a.m 看到日出。日落在 8:00 p.m,但日出在 5:00 a.m。和日落 9:00 p.m,在盛夏。他们认为人们会在晚上用更少的电来照亮他们的房子,等等。 所以现在,该国大部分地区在夏季使用夏令时,在冬季使用标准时间。 yahoo link
首先我想给你一个问题的解决方案:
将您的计算机(在 win 7 上测试)的时区设置为 GMT+3:30,并将您的日期更改为 2015 年 3 月 23 日。 和 运行 您在两种浏览器上的代码 (chromium/mozila)。 您会发现相同的结果(即输入时间增加 1 小时)
是的,问题解决了。
您的输入时间将增加 +1 小时,因为在 2015 年 3 月 22 日 12:00 AM(格林威治标准时间 +3:30)之后,夏令时开始。 (you can check it here also from here here)
现在的原因是:
请查看此屏幕截图:http://prntscr.com/6j4gdn(在 win 7 上) 在这里你可以看到“夏令时开始于 2015 年 3 月 22 日 12:00 AM。
表示时间将在 2015 年 3 月 22 日上午 12:00 后提前 1 小时。(仅适用于那些使用夏令时系统的国家)
因此,如果您将时间更改为 2015 年 3 月 23 日,则夏令时开始于(例如 GMT+3:30) 你会发现在两种浏览器上的输出都是正确的。
我猜chrome总是使用夏令时系统(仅适用于那些使用夏令时系统的国家) 我已经通过更改系统时间对其进行了测试(但不是 100% 确定)
另一方面,mozila 支持夏令时系统。因此,在 2015 年 3 月 22 日 12:00 AM(格林威治标准时间+3:30)之后,mozila 将为您输入的日期时间增加 1 小时
我想这对你们所有人都有帮助。