Pull to refresh

Comments 55

Подобные проблемы характерны не только для JS, во многих языках (особенно динамических) работа с временем и таймзонами усложнена. Помогает наличие оттестированных и проверенных на практике библиотек. Для JS очень выручает momentjs.com/ там и работа с датой-временем есть, и расширение для таймзон

Читаю статью из будущего. С таймзонами всё ещё плохо
А вот вместо moment теперь лучше использовать date-fns

> Смещение относительно UTC — не константа. Это функция от даты, времени (ну или от таймштампа, если угодно) и, опять же, тайм-зоны.

это должно быть так, но стандарт ES5 этого не требует. к сожалению. В итоге полный бардак.

Firefox 33.1.1 — берет текущую локальную зону
>new Date(«1970-02-14T03:04:05»)
Date {Sat Feb 14 1970 03:04:05 GMT+0400 (Russia TZ 2 Daylight Time)}
>new Date(1970, 1, 14, 3, 4, 5)
Date {Sat Feb 14 1970 03:04:05 GMT+0400 (Russia TZ 2 Daylight Time)}

IE11 — берет зону на дату
>new Date(«1970-02-14T03:04:05»)
[date] Sat Feb 14 1970 03:04:05 GMT+0300 (Russia TZ 2 Standard Time)[date] Sat Feb 14 1970 03:04:05 GMT+0300 (Russia TZ 2 Standard Time)
>new Date(1970, 1, 14, 3, 4, 5)
[date] Sat Feb 14 1970 03:04:05 GMT+0300 (Russia TZ 2 Standard Time)[date] Sat Feb 14 1970 03:04:05 GMT+0300 (Russia TZ 2 Standard Time)

Chrome Canary 41.0 — как бог на душу положит
>new Date(«1970-02-14T03:04:05»)
Sat Feb 14 1970 07:04:05 GMT+0400 (Russia TZ 2 Daylight Time)
>new Date(1970, 1, 14, 3, 4, 5)
Sat Feb 14 1970 03:04:05 GMT+0400 (Russia TZ 2 Daylight Time)
new Date(1970, 0, 6, 3, 4, 5)
Tue Jan 06 1970 03:04:05 GMT+0300 (Russia TZ 2 Standard Time)

Плюс еще разные конструкторы ведут себя по-разному в Хроме — это вообще ад.
Хром по видимому делает правильно, он учитывает изменения часовых поясов.
Нпример в моей часовой зоне ваш пример смещение не меняет. Ваш часовй пояс не менялся в то время?
new Date(1970, 1, 14, 3, 4, 5)
Sat Feb 14 1970 03:04:05 GMT+0600 (OMST)
new Date(1970, 0, 6, 3, 4, 5)
Tue Jan 06 1970 03:04:05 GMT+0600 (OMST)
> Хром по видимому делает правильно, он учитывает изменения часовых поясов.
Он их как-то странно учитывает.

>Нпример в моей часовой зоне ваш пример смещение не меняет. Ваш часовй пояс не менялся в то время?
мой часовой пояс — это MSK, 6-го января 1970 и вообще в 1970 никаких изменений не было — см. историю изменений http://www.timeanddate.com/time/zone/russia/moscow
делает правильно, он учитывает изменения часовых поясов.
Разве по стандарту положено учитывать?
15.9.1.7 Local Time Zone Adjustment
An implementation of ECMAScript is expected to determine the local time zone adjustment. … The value LocalTZA does not vary with time but depends only on the geographic location.
www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.7
15.9.1.9 Local Time
Conversion from UTC to local time is defined by
LocalTime(t) = t + LocalTZA + DaylightSavingTA(t)
www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.9
Мне вот этот момент непонятен:

Между прочим, в этом как раз и есть разница между GMT и UTC. UTC – это время без смещения, а GMT – это время со смещением 0. Поясню на примере:

31.12.2014, 20:59:59 GMT в Московском часовом поясе должно выглядеть как 31.12.2014, 23:59:59
31.12.2014, 20:59:59 UTC в Московском часовом поясе должно выглядеть как 31.12.2014, 20:59:59

C чего это последняя строка?
UFO just landed and posted this here
Не так то, что в Московском часовом поясе 31.12.2014, 20:59:59 UTC выглядит сейчас как 31.12.2014, 23:59:59 с точностью до нескольких секунд (до одной секунды, как правило).

Насчёт лишней секунды — это не очень предсказуемо, так что можно на практике считать что её нет. Может быть, скоро и не будет.

Високосные секунды вносятся по астрономическим наблюдениям в конце суток по всемирному времени 30 июня или 31 декабря так, чтобы время UTC не отличалось от UT1 более, чем на ±0,9 секунды. Считается, что в такие дни после времени 23:59:59 идёт 23:59:60.

Теоретически возможно объявление отрицательной високосной секунды, если средние солнечные сутки окажутся короче календарных (при этом после 23:59:58 следующая секунда будет 00:00:00). На практике отрицательные високосные секунды никогда не вводились.
В январе 2012 года Международный консультативный комитет по радио решил отложить принятие решения о новом определении UTC до 2015 года, «секунды координации» отменены не были.

Обратите внимание, должно выглядеть — не значит что так сделано. Если вы задаёте время без смещения, то и отображаться оно должно без смещения. Во всех реализациях (ну или в большинстве) это не так. Почему то между GMT и UTC ставится знак равенства. На земле зон без смещения не существует. Есть GMT, у неё смещение = 0, но оно есть.
Строка принимается в формате RFC2822, весьма неудобном и трудном к ручному вводу. Получить такую строку из пользовательского ввода практически невозможно. Я не могу себе представить человека, который бы согласился вводить дату в таком формате.
Насколько я знаю, строка принимается так же в формате ISO 8601:

> new Date('1998-02-01T00:00:00+07:00')
Sat Jan 31 1998 20:00:00 GMT+0300 (MSK)
Вот что пишут на MDN
dateString
String value representing a date. The string should be in a format recognized by the Date.parse() method (IETF-compliant RFC 2822 timestamps and also a version of ISO8601).
Так что и то и то верно.
Конечно, потому я и написал «так же».
Я пока тоже остановился на ISO 8601.
var ISO_8601 = new Date().toJSON(); // ""2014-12-08T14:56:56.765Z""
new Date(ISO); // back
А еще есть такое различное поведение:

Chrome:
new Date('2011-11-24T09:00:27+02:00')
Thu Nov 24 2011 13:00:27 GMT+0600 (OMST) // от полуночи текущей зоны


Firefox:
new Date('2011-11-24T09:00:27+02:00')
Date 2011-11-24T07:00:27.000Z // от полуночи по гринвичу


Safari:
new Date('2011-11-24T09:00:27+0200')
Thu Nov 24 2011 14:00:27 GMT+0700 (OMST)
Между прочим, в этом как раз и есть разница между GMT и UTC. UTC – это время без смещения, а GMT – это время со смещением 0

Откуда трава? :-)
ru.wikipedia.org/wiki/%D0%92%D1%81%D0%B5%D0%BC%D0%B8%D1%80%D0%BD%D0%BE%D0%B5_%D0%BA%D0%BE%D0%BE%D1%80%D0%B4%D0%B8%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%B2%D1%80%D0%B5%D0%BC%D1%8F

Единственная библиотека, из попавшихся мне, которая использует базу данных Олсона для обсчета сдвигов — timezone-JS. Проблема использования этой библиотеки в том, что низлежащие библиотеки (date/time picker-ы) про неё ничего не знают и внутри активно используют стандартный Date.
стандартный Date — это на самом деле не дата, а метка времени. Сама дата по идее не зависит от часового пояса, но из-за того, что многие описывают дату, через метку времени, то и огребают кучу проблем — необходимость пересчёта этой метки в зависимоти от часового пояса, так, чтобы она соответствовала нужной дате в этом поясе.
В Java тоже есть такая проблема — до Java 8 был только один тип для даты — дата со временем и часовым поясом. Из за этого были большие путаницы когда нужно было хранить только время или только дату (без часового пояса). С такими проблемами встречался в разное время на абсолютно разных проектах! И только недавно ей решили — в Java 8 разделили тип для хранения даты на несколько — дата без часового пояса, дата с часовым поясом, время с/без часового пояса, дата со временем с/без часового пояса. В общем сделали всё по уму :)
И каково же было моё удивление что в Oracle уже давно используют разные типы для даты и времени с/без часового пояса!
Порой даже удивительно — такая типичная проблема и решается на первый взгляд легко, а начали исправлять только сейчас!
P.S. В Java есть JotaTime но это доп. либа поддерживаемая не всеми framework'ами.
И криво же исправили :-) Что если мне надо указать «номер часа» или «год-месяц» или экзотическое «год-неделя»?
Как программист .Net, столкнувшийся с JS, никак не могу понять, по какой причине один и тот же js-код, работающий с датами, работает в разных браузерах по-разному? Как вообще это может быть в языке программирования? :)
Потому, что объект Date — не реализован на JS, а предоставляется браузером. А в каждом браузере используется своя библиотека работы со временем.
Где можно подробнее почитать про «не реализован»?
Выполнить в консоли:
 Date 
Что реализован он не на JS.
Это из чего должно быть видно, кроме ваших слов?
Ээээ… Я вроде написал, что я НЕ js-программист, можно было бы и подробнее. Эта строка мне ни о чем не говорит.
Погуглил спецификацию, в ней есть объект Date (http://www.ecma-international.org/ecma-262/5.1/#sec-15.9). Насколько я понимаю, каждый браузер самостоятельно реализует эту спецификацию. Возникает вопрос, что значит «реализован не на js»?
Реализован на другом языке программирования, который скомпилирован в «native code».
После вашего коммента про «выполнить в консоли», специально посмотрел и увидел

function Date() { [native code] }

Решил, что «native code» — значит, что оно скомпилировано на другом ЯП. Начал гуглить, нашел список встроенных объектов JavaScript (или вот), прошелся по нескольким другим объектам, обнаружил что
скрин консоли
image

и отказался от этой мысли, решив, что, вероятно, я что-то не понимаю и пошел спать.

Но ваш коммент опять вызвал вопросы. Есть ли какие-то ссылки чтобы покурить эту тему насчет Date, а то мне не удалось найти ничего по этому вопросу. Спасибо.
Вероятно меня сбило с толку то, что вы указали, что Date не реализован на JS, при этом не упомянув, что это так же справедливо и для многих других объектов. Выходит, что проблемы «разночтений» могут распространяться так же и на другие объекты, но я слышал именно про Date.
А что, хоть какие-то из встроенных объектов и функций JS хоть в одном браузере реализованы на JS?..
Например, объект console в ie.
И как это можно увидеть? Попробовал дебажить console.log(), войти не смог.
О, точно. У меня просто интранет-сайт в режиме совместимости был открыт, там это не срабатывает.
Итого практически все остальные объекты JS во всех браузерах реализованы через native-код, так что непонятно, почему вдруг вы это упомянули про Date и что это должно оправдывать.
Потому, что объект Date — не реализован на JS, а предоставляется браузером. А в каждом браузере используется своя библиотека работы со временем.
Отсюда и различное поведение в разных браузерах.
Да там весь движок JS разный, почему-то никто этим не оправдывается.
Весь движок всё же пилится по спецификации, а вот сторонняя библиотека работает так, как работает. Например, одна либа использует только текущую конфигурацию часовых поясов для любых дат и не учитывает когда эта конфигурация менялась. Другая же — пересчитывает даты в соответствии историей изменения часовых поясов в вашем регионе. Одна библиотека считает длину промежутка между двумя временными метками в реальных секундах, без учёта часовых поясов, а другая с учётом. И многие другие нюансы, которые зависят от выбранной архитектуры.
Так работа со временем — это часть движка, если сторонняя либа не соответствует спецификациям, то зачем она в нём используется?
Не всё специфицировано.
Например, по стандарту (см. мой комментарий выше) сдвиг часового пояса не должен меняться в зависимости от даты:
Разве по стандарту положено учитывать?
15.9.1.7 Local Time Zone Adjustment
An implementation of ECMAScript is expected to determine the local time zone adjustment. … The value LocalTZA does not vary with time but depends only on the geographic location.
www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.7
15.9.1.9 Local Time
Conversion from UTC to local time is defined by
LocalTime(t) = t + LocalTZA + DaylightSavingTA(t)
www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.9
Ну а про DaylightSavingTA ничего такого не сказано. В любом случае этой спецификации всего 3 года, а в браузерах объект Date появился за долго до неё.
Ну а про DaylightSavingTA ничего такого не сказано.
Это я просто цитировать не стал:
15.9.1.8 Daylight Saving Time Adjustment
An implementation of ECMAScript is expected to determine the daylight saving time algorithm. The algorithm to determine the daylight saving time adjustment DaylightSavingTA(t), measured in milliseconds, must depend only on four things:
(1) the time since the beginning of the year
t – TimeFromYear(YearFromTime(t))
(2) whether t is in a leap year
InLeapYear(t)
(3) the week day of the beginning of the year
WeekDay(TimeFromYear(YearFromTime(t)))
and (4) the geographic location.
www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.8
Тут тоже, как видите, от конкретного года зависеть не должно. Только от времени с начала года, високосности, дня недели первого дня года и географического положения.
В любом случае этой спецификации всего 3 года, а в браузерах объект Date появился за долго до неё.
V8, скажем, всего вдвое старше.
Ну, а если заглянуть в архив, то эта часть там вообще с самой первой версии от 1997-го года (pdf):
скриншот
Ок, ок, не должно :-) О чём и, главное, зачем мы спорим?
О том, что ваше объяснение ничего не объясняет =) И различное поведение Date в браузерах не лучше никаких прочих нарушений стандарта движками. В «просто» Java бы авторов такой реализации VM гнали поганой метлой, а в JS, почему-то, терпят.
А я и не говорю, что лучше. Я объяснил как такое могло произойти. Если использовать реализацию на чистом JS, то во всех браузерах работало бы одинаково, хотя и не факт, что соответствовало бы спецификации. И это, кстати, спорный вопрос, надо ли учитывать изменения часовых поясов со временем. Если не учитывать — реализация проще, но время будет чудесным образом меняться, что не всегда приемлемо.
работает в разных браузерах по-разному

Сколько всего хочется на эту фразу написать, но не получается подобрать слов. :) В наше время уже гораздо реже приходится проверять код в разных браузерах, но чувство «ничего себе! оно работает даже под %любой браузер%» иногда тлеет где-то внутри. О, эта чудная "кроссбраузерность"…
Классическая ошибка новичков (иногда регуляров и бывает, даже, сениоров) путать вывод в консоль значения переменной и её реальное значение. Проходили уже в «хром, уркравший рождество», что каждый браузер выводит в консоль по-своему. На деле это дебаггер или интерфейсный логгер, а значит, как и в любом языке, формат вывода зависит не от ЯП, а от дебаггера/логгера.

timestamp в миллисекундах очень даже полезная штука, особенно, когда нужно с часовыми поясами. Наши любимые датапикеры да, работают с Date, из него можно вытянуть и таймстемп, и часовой пояс. Нужно только постараться, чтобы перевести это в другой часовой пояс =) Благо timestamp всегда работает в UTC формате =)
Столкнулся сегодня в Nashorn. Задаю 1 апреля 1983 года, получаю 31 марта 1983 года. Долго копал, пока не осознал, что 1 апреля нет времени с 00:00 до 01:00. Т.е. нельзя задать время, например, 00:30 на 01.04.1983. Это не значит, что человечество проскочило час времени через чёрную дыру, просто так поставлен учёт времени.
Sign up to leave a comment.

Articles