Часовой пояс JavaScript не подходит для прошлых правил перехода на летнее время
В 2007 году изменились дни перехода на летнее время. Любая дата, попадающая в расширенный диапазон летнего времени до этого изменения, сообщает о неправильном смещении часового пояса в Chrome и Firefox. Как будто Firefox и Chrome не обращают внимания на тот факт, что раньше у DST были разные дни.
Если вы запустите следующий скрипт, он сообщит о смещении в 240 минут. Это неправильно, он должен сообщить 300 минут. IE10 делает это правильно. Кто-нибудь знает об исправлении?
alert(new Date('11/04/2004').getTimezoneOffset());
ОБНОВИТЬ:
Вот интересный фрагмент кода, который я только что взломал (см. Ниже). Это действительно удивительно, как далеко большинство дат в каждом браузере, кроме IE. Сравните даты начала и окончания с этим: http://www.timeanddate.com/worldclock/timezone.html?n=77&syear=2000
В итоге я заменил прототип Date для getTimezoneOffset своим собственным, который рассчитывает его на основе жестко закодированной таблицы. Это работает для нас, потому что мы ведем бизнес только в США. Это самое худшее из возможных решений, которое я могу себе представить...
<!DOCTYPE html>
<html>
<head>
<title>Moment Test</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.0.0/moment.min.js"></script>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script>
var lastOffset = null;
var $tbody = null;
var endDate = new Date('01/01/2021');
function addDate(d) {
if($tbody === null)
$tbody = $('#dates');
var offset = d.getTimezoneOffset();
var s = '';
if(lastOffset != offset) {
if(lastOffset != null)
s = '<tr style="background-color: red;">';
lastOffset = offset;
}
else {
s = '<tr>';
}
var m = new moment(d);
s += '<td>' + m.format('YYYY-MM-DD') + '</td><td>' + m.format('YYYY-MM-DDTHH:mm:ssZ') + '</td><td>' + m.format('YYYY-MM-DDTHH:mm:ss') + '</td><td>' + offset + '</td></tr>';
$tbody.append($(s));
d.setDate(d.getDate() + 1);
if(d < endDate)
window.setTimeout(function(){addDate(d)}, 0);
}
</script>
</head>
<body>
<button onclick="addDate(new Date('01/01/1980'));">Fill Table</button>
<table border="1">
<thead><tr><th>Date</th><th>Date 2</th><th>Date 3</th><th>TZ Offset</th></tr></thead>
<tbody id='dates'></tbody>
</table>
</body>
</html>
2 ответа
Это на самом деле определенное поведение, чтобы использовать текущие правила DST и игнорировать те, которые действуют на определенную дату / время. См. ES5 15.9.1.8:
"Внедрение ECMAScript не должно пытаться определить, относится ли точное время к летнему времени, а только к тому, было ли бы применено летнее время, если бы в то время использовался текущий алгоритм перехода на летнее время. Это позволяет избежать таких осложнений. с учетом тех лет, в которых местное время наблюдалось в летнее время круглый год ".
Правила таковы: применяйте текущие правила DST к любому указанному времени. Это приводит к чрезмерному бессмысленному поведению, но это то, что требует ECMAScript.
Возможно - даже вероятно - что это поведение изменится в будущей версии ECMAScript, чтобы потребовались действительные правила DST во все моменты времени. Первоначально это не требовалось из-за бремени доставки tzdata, которое накладывает на разработчиков. Тем не менее, язык стал достаточно важным, так что, вероятно, каждый в конечном итоге будет вынужден смириться с этим. Но, насколько я знаю, это может произойти через несколько лет, так что не ждите этого.
Я подтвердил, что это настоящая ошибка в JavaScript.
- Протестировано с обычными часовыми поясами США, которые следуют за летним временем
- Восточная, центральная, горная, тихоокеанская
- Протестировано в Chrome, Firefox, Safari и не удалось (последние версии)
- Протестировано в IE 6, 7, 8, 9 и не удалось.
- Протестировано в IE 10 и прошло (не затронуто).
- Протестировано на Windows 7, 8 и Mac OSX.
Это довольно тревожно. Кто-нибудь знает причину?
Я думал, что это может быть ошибка WebKit, но Firefox использует Gecko.
Я проверил различные списки проблем и нигде не мог найти эту конкретную проблему. Возможно, я что-то пропустил. Я не уверен, куда подать отчет об ошибке, так как он затрагивает несколько мест.
Возможно, это основная ошибка JavaScript? Мне действительно трудно поверить, что это базовое тестирование было упущено.
Я подумал, что, возможно, это влияет только на системы Windows, потому что в ОС вместо часов TZDB используются часовые пояса Windows, но, похоже, это не так, поскольку это происходит и на Mac.
Мы все знаем, что даты в JavaScript не совсем правильные, но я подумал, что мы можем хотя бы зависеть от этого. Вам даже не нужно смотреть на смещение или анализировать. Просто проверьте значение:
new Date(2004,10,4) // recall, js months are 0-11, so this is Nov. 4 2004.
В 2004 году в Соединенных Штатах переход на летнее время закончился 31 октября в 2:00, когда часы вернулись к 1:00. Так что к 4 ноября все они должны быть в стандартное время, но это не так! Например, в инструментах разработчика Chrome на консоли с часами, установленными на восточный часовой пояс США:
> new Date(2004,10,7,0,0)
Sun Nov 07 2004 00:00:00 GMT-0400 (Eastern Daylight Time)
> new Date(2004,10,7,1,0)
Sun Nov 07 2004 01:00:00 GMT-0500 (Eastern Standard Time)
Это ставит дату перехода на 7 ноября. Это следующее правило "Первое воскресенье ноября", которое действует в настоящее время, но в 2004 году это должно было быть старое правило "Последнее воскресенье октября".
ОБНОВЛЕНИЕ 1
Это не кажется ограниченным браузером. Это также терпит неудачу в Node.js
И просто чтобы доказать, что IE в порядке, вот вывод из IE10:
Интересно, что IE и Firefox разрешают неопределенность 1:00 как летнее время, в то время как Chrome разрешает ее как стандартное время, но это отдельная проблема. Он выбирает правильную дату перехода.
ОБНОВЛЕНИЕ 2
Стоит отметить, что в последнем Firefox 21 эта проблема действительно возникает, но она проявляется по-другому, поскольку она усугубляется другой проблемой, которая переключает дневной свет на стандартные имена, даже если используется правильное смещение. Другими словами, в Firefox вывод выглядит так: