消除JavaScript夏令时间隙,跨浏览器解决方案 [英] Eliminating Javascript daylight saving time gap, a cross-browser solution

查看:183
本文介绍了消除JavaScript夏令时间隙,跨浏览器解决方案的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

经过大量的麻烦,我终于找到了实际的问题。这是夏令时导致的差距,如果时区设置为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>输入日期< / td>输入日期< / td>< td>解析时间戳< / td>< td>输出日期< / td>< / tr>); var m = 22 * 60; for(var i = 0; i+:+ zeroPad(m%60)+:00; var input =3/21/2015+ zeroPad(Math.floor(m / 60))+ var d = new Date(input); var output = d.getFullYear()+' - '+ zeroPad(d.getMonth()+ 1)+' - '+ zeroPad(d.getDate())+ '0 + zeroPad(d.getHours())+ :'+ zeroPad(d.getMinutes())+':'+ zeroPad(d.getSeconds()); document.write(< tr>< td>+ input +< / td>< td>+ d.getTime()+< / td>< td>+ output + < / td>< / tr>);对于(var i = 0; i <7; i ++){var input =3/22/2015+ zeroPad(Math.floor(m / 60))+:m = + zeroPad(m%60)+:00; var d = new Date(input); var output = d.getFullYear()+' - '+ zeroPad(d.getMonth()+ 1)+' - '+ zeroPad(d.getDate())+ '0 + 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上运行它,这里是他们说的:





红框内的部分是发生差距的时间范围。我的问题是,像日历这样的组件通常依赖于时间部分设置为00:00:00的日期对象,并且它们有一个循环通过向前一个日期添加一天的时间戳来生成新的日期。因此,一旦对象落入 3/22/2015 00:00:00 ,它将被视为 3/22/2015 01:00:00 21/3/2015 23:00:00 (取决于浏览器),因此生成的日期将从该时间点开始无效

解决方案



问题是如何检测这些日期对象和如何处理它们? / div>

使用 moment.js 会为您节省许多头痛,是实现跨浏览器兼容性的最简单方法

  var m = moment.utc(3/22/2015,M / D / YYYY)
var s = m.format(YYYY-MM-DD HH:mm:ss)

使用UTC对此很重要,因为您不想受用户时区的影响。否则,如果您的日期落入DST转换,则可以调整为其他值。 (

为了回答您更新的问题的这一部分:


为了简化问题,我在寻找一个这样的函数:

  function date_decomposition(d){
...
}

console.log(date_decomposition(new Date(3/22/2015 00:00:00)));
=> [2015,3,22,0,0,0]


虽然现在已经清楚您要求什么,但您必须了解这是不可能的才能达到您的确切要求,至少不是在跨浏览器,跨区域,跨时区的方式。






的话,你肯定可以取一个 Date 对象并解构它的部分:

  function date_decomposition(d){
return [d.getFullYear(),d.getMonth()+ 1,d.getDate(),
.getHours(),d.getMinutes(),d.getSeconds()];
}

但这将始终代码正在运行的时区。您会看到, Date 对象中只有一个值 - 表示自 1970-01-01T00:00:00Z (不考虑闰秒)。这个数字是基于UTC的。



因此,在重述中,您所有的问题都与字符串解析为 Date 对象开始。没有数量的焦点在输出功能将帮助你以一个完全安全的方式解决。无论您使用库还是编写自己的代码,您都需要获取原始的数据字符串,以获取您要查找的结果。到 Date 对象时,您已丢失了完成此操作所需的信息。



顺便说一句,您可以考虑观看我的Pluralsight课程,日期和时间基础知识,其中涵盖了大部分甚至更详细。模块7完全关于JavaScript和这些种类的陷阱。


After lots of hassle I finally found the actual problem. It's the gap induced by daylight saving and the fact that different browsers act differently if timezone is set on UTC+3:30 (I'm not sure of other timezones).

Here's a snippet to generate the problem (the problem is reproducible if your system's TZ is set to 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>");

I've run it on Firefox and Chromium and here are what they say:

The parts within the red boxes are the range of times in which the gap happens. My problem is that components like calendars usually depend on date objects with time part set to "00:00:00" and they've got a loop generating new dates by adding a day worth of timestamp to the previous date. So once an object falls into 3/22/2015 00:00:00 it will be considered 3/22/2015 01:00:00 or 21/3/2015 23:00:00 (depending on the browser) and hence the generated dates will be invalid from that point of time forth!

The question is how to detect such date objects and how to treat them?

解决方案

Using moment.js will save you lots of headache, and is the easiest way to achieve cross-browser compatibility for this sort of thing.

var m = moment.utc("3/22/2015","M/D/YYYY")
var s = m.format("YYYY-MM-DD HH:mm:ss")

Using UTC for this is important since you don't want to be affected by the user's time zone. Otherwise, if your date fell into a DST transition, it could be adjusted to some other value. (You're not really intersted in UTC, you're just using it for stability.)


In response to this part of your updated question:

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]

While it's now clear what you are asking for, you must understand it is not possible to achieve your exact requirements, at least not in a cross-browser, cross-region, cross-timezone manner.

  • Each browser has it's own way of implementing the string-to-date parsing. When you use either the constructor new Date(string) or the Date.parse(string) method, you're invoking functionality that is implementation specific.

    There's a chart showing many of the formatting differences here.

  • Even if the implementation were consistent across all environments, you'd have regional formatting and time zone differences to contend with.

    • In the case of regional formatting issues, consider 01/02/2015. Some regions use mm/dd/yyyy ordering and will treat this as January 2nd, while other regions use dd/mm/yyyy ordering and will treat this as February 1st. (Also, some parts of the world use yyyy/mm/dd formatting.)

      Wikipedia has a list and map of where in the world different date formats are used.

    • In the case of time zones, consider that October 19th, 2014 at Midnight (00:00) in Brazil did not exist, and November 2nd, 2014 at Midnight (00:00) in Cuba existed twice.

      The same thing happens on other dates and times in different time zones. From the information you provided, I can deduce that you are in Iran time zone, which uses UTC+03:30 during standard time, and UTC+04:30 during daylight time. Indeed, March 22, 2105 at Midnight (00:00) did not exist in Iran.

      When you try to parse these invalid or ambiguous values, each browser has its own behavior, and there indeed differences between the browsers.

      • For invalid times, some browsers will jump forward an hour, while others will jump backwards an hour.

      • For ambiguous times, some browsers will assume you meant the first (daylight-time) instance, while others will assume you meant the second (standard-time) instance.

Now with all of that said, you can certainly take a Date object and deconstruct its parts, quite simply:

function date_decomposition(d) {
    return [d.getFullYear(), d.getMonth()+1, d.getDate(),
            d.getHours(), d.getMinutes(), d.getSeconds()];
}

But this will always be based on the local time zone where the code is running. You see, inside the Date object, there is just one value - a number representing the elapsed milliseconds since 1970-01-01T00:00:00Z (without leap seconds being considered). That number is UTC-based.

So, in recap, all of the issues you are having are related to the way the string was parsed into the Date object to begin with. No amount of focusing on the output functions will help you to resolve that in a completely safe manner. Whether you use a library or write your own code, you'll need to obtain that original string of data to get the result you are looking for. By the time it's in a Date object, you've lost the information you need to make this work.

By the way, you might consider watching my Pluralsight course, Date and Time Fundamentals, which covers much of this in even greater detail. Module 7 is entirely about JavaScript and these sorts of gotchas.

这篇关于消除JavaScript夏令时间隙,跨浏览器解决方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆