問題描述
經(jīng)過一番折騰,我終于找到了真正的問題.這是夏令時造成的差距,如果時區(qū)設(shè)置為 UTC+3:30(我不確定其他時區(qū)),不同瀏覽器的行為會有所不同.
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).
這是產(chǎn)生問題的片段(如果您的系統(tǒng)的 TZ 設(shè)置為 UTC+3:30,則該問題是可重現(xiàn)的):
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>");
我在 Firefox 和 Chromium 上運行過它,他們是這樣說的:
I've run it on Firefox and Chromium and here are what they say:
紅框內(nèi)的部分是差距發(fā)生的時間范圍.我的問題是,像日歷這樣的組件通常依賴于時間部分設(shè)置為00:00:00"的日期對象,并且它們有一個循環(huán),通過在前一個日期添加一天的時間戳來生成新日期.因此,一旦一個對象落入 3/22/2015 00:00:00
它將被視為 3/22/2015 01:00:00
或 21/3/2015 23:00:00
(取決于瀏覽器),因此生成的日期將從該時間點開始失效!
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?
推薦答案
使用 moment.js 可以省去很多麻煩,而且是最簡單的為這類事情實現(xiàn)跨瀏覽器兼容性的方法.
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")
為此使用 UTC 很重要,因為您不想受到用戶時區(qū)的影響.否則,如果您的日期進入 DST 轉(zhuǎn)換,則可以將其調(diào)整為其他值.(你真的對 UTC 不感興趣,你只是為了穩(wěn)定而使用它.)
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:
為了簡化問題,我正在尋找這樣的函數(shù):
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]
雖然現(xiàn)在很清楚您的要求是什么,但您必須了解不可能達到您的確切要求,至少不能以跨瀏覽器、跨區(qū)域、跨時區(qū)的方式實現(xiàn).
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.
每個瀏覽器都有自己的實現(xiàn)字符串到日期解析的方式.當(dāng)您使用構(gòu)造函數(shù)
new Date(string)
或Date.parse(string)
方法時,您調(diào)用的是 特定于實現(xiàn)的功能.
Each browser has it's own way of implementing the string-to-date parsing. When you use either the constructor
new Date(string)
or theDate.parse(string)
method, you're invoking functionality that is implementation specific.
這里有一張圖表顯示了許多格式差異.
即使實施在所有環(huán)境中都是一致的,您也需要應(yīng)對區(qū)域格式和時區(qū)差異.
Even if the implementation were consistent across all environments, you'd have regional formatting and time zone differences to contend with.
如果出現(xiàn)區(qū)域格式問題,請考慮
01/02/2015
.一些地區(qū)使用mm/dd/yyyy
排序并將其視為 1 月 2 日,而其他地區(qū)使用dd/mm/yyyy
排序并將其視為 2 月 1 日.(此外,世界上的某些地方使用yyyy/mm/dd
格式.)
In the case of regional formatting issues, consider
01/02/2015
. Some regions usemm/dd/yyyy
ordering and will treat this as January 2nd, while other regions usedd/mm/yyyy
ordering and will treat this as February 1st. (Also, some parts of the world useyyyy/mm/dd
formatting.)
維基百科有一份世界各地使用不同日期格式的列表和地圖.
對于時區(qū),請考慮 10 月 19 日,2014 年午夜 (00:00) 在巴西 不存在,并且 2014 年 11 月 2 日午夜 (00:00) 在古巴存在兩次.
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.
同樣的事情發(fā)生在不同時區(qū)的其他日期和時間.根據(jù)您提供的信息,我可以推斷您在伊朗時區(qū),標(biāo)準(zhǔn)時間使用 UTC+03:30,白天使用 UTC+04:30.事實上,2105 年 3 月 22 日午夜 (00:00)不存在 在伊朗.
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.
當(dāng)您嘗試解析這些無效或不明確的值時,每個瀏覽器都有自己的行為,并且瀏覽器之間確實存在差異.
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.
對于模棱兩可的時間,一些瀏覽器會假設(shè)您指的是第一個(白天)實例,而其他瀏覽器會假設(shè)您指的是第二個(標(biāo)準(zhǔn)時間)實例.
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.
現(xiàn)在說了這么多,你當(dāng)然可以取一個 Date
object 并解構(gòu)它的各個部分,非常簡單:
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()];
}
但這將始終基于代碼運行的本地時區(qū).您會看到,在 Date
對象內(nèi)部,只有一個值 - 一個表示自 1970-01-01T00:00:00Z
以來經(jīng)過的毫秒數(shù)(不考慮閏秒)).該數(shù)字基于 UTC.
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.
因此,總而言之,您遇到的所有問題都與將字符串解析為 Date
對象的方式有關(guān).再多的關(guān)注輸出函數(shù)都不會幫助您以完全安全的方式解決這個問題.無論您使用庫還是編寫自己的代碼,都需要獲取原始數(shù)據(jù)字符串才能獲得所需的結(jié)果.當(dāng)它在 Date
對象中時,您已經(jīng)丟失了完成這項工作所需的信息.
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.
順便說一句,您可以考慮觀看我的 Pluralsight 課程,日期和時間基礎(chǔ)知識,其中涵蓋其中大部分內(nèi)容更詳細.第 7 單元完全是關(guān)于 JavaScript 和這些問題的.
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.
這篇關(guān)于消除 Javascript 夏令時差距,一種跨瀏覽器的解決方案的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!