Pull to refresh

Comments 28

UFO just landed and posted this here
var time_zone = ((<? echo time();?> - loc/1000)/60).toFixed(0);

offset для временных зон не всегда кратен одному часу
В работе со временем вообще много приколов.

Исходим из того, что на сервере все даты хранятся в базе данных в формате GMT (и это правильно).

Это правильно только для дат в прошлом или ближайшем будущем. Для дат в отдалённом будущем, например, «17 июля 2018 г., 15:00 по Москве» хранить только дату в GMT недостаточно, т.к. теряется привязка к местности и неизвестно, в каком часовом поясе эта местность будет находиться через 3 года.
В случае расписаний поездов и самолётов (или чего-то подобного) подход используемый в статье неприменим. Там другая идеология, Вы правы.
Почему Вы использовали Date.UTC вместо getTime()?
Функция так же возвращается значение миллисекунд, прошедших с полуночи 01.01.1970 в UTC.
И для вывода в mydate я советовал бы посмотреть в сторону toLocaleFormat. Сайт может быть для нескольких стран, а следовательно, и время должно выдаваться в разных форматах, привычных жителям той или иной страны.
На вскидку:
Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()) != d.getTime();

Сам удивился, что не смог функцию попроще найти в javascript. Может плохо искал. Но именно вышеприведённое выражение (полученное чуть ли не методом тыка) правильно работает.
0.
// t — время в секундах из БД сервера

То есть в базе хранится время в виде количества секунд? Не-не-не, оставьте этот колхоз для mysql 3.x.
Сейчас все эти костыли уже надо выкинуть.

1. Любая нормальная база нормально поддерживает время с часовыми поясами. Например, в PostgreSQL есть специальный тип данных timestamp with time zone, который хранит и само время и нужное смещение. И при необходимости корректно все пересчитает в нужный часовой пояс.
Для Mysql и других так же уже все изобретено.

2. Если вы уже посчитали на клиенте — зачем это передавать на сервер? Создавать нагрузку и тормоза? Прямо на клиенте исправьте даты и все.
Например, можно выводить даты так:
<span class="date-fixable" data-time="Tue, 18 May 2015 15:35:00+0400">18 мая 15:35</span>

Где-нибудь вначале можно передать серверное время. На стороне клиента вычислили смещение и подправили даты в нужных спанах. Если у клиента JS не работает — то он видит стандартное время.
В PostgreSQL для этих целей time with time zone.
В случае с timestamp он всегда хранит UTC, но при отображении либо учитывает (with time zone), либо не учитывает (without time zone) временную зону PsostgreSQL-клиента.
1. Идеологически хранить часовой пояс очень часто не нужно. И никаких костылей тут я не вижу.
2. Вроде именно этот вариант тоже приведён (функция mydate).
1. Я тоже так раньше делал. Если задача «наго-кодить по-быстрому», то самое оно. Пока не сел и не потратил какое-то время, чтобы разобраться с нормальными инструментами.

Весь вопрос в том, видите вы какое-то развитие у системы вперед или нет. Если это конечная точка, то ок.
Время, которое мы используем, имеет свои законы — свои часовые пояса, свои лимиты, свои проблемы подсчета, свои невозможные даты и свой характер изменения всего этого — все это не так просто. Это не Медведеву часами щелкнуть, тут думать надо. Разработчики проделали большую работу, чтобы можно было легко и удобно использовать это в программах. Когда, вы все примитивизируете до int, вы теряете намного больше.

Вот уже привели пример, что будет с датами через несколько лет. В системе работа, которой планируется только на ближайший год, такого вопроса не стоит. Потом возникнет вопрос, как быть с датами выходящими за интервал int — меньше 1970 и больше 2038 годов. Как это починить? Костылем. То есть у вас в системе уже зарыт архитектурный костыль, который вылезет еще 100 раз.
Проекты разные бывают, для нашего этот алгоритм идеально подошёл. У нас — мессенджер мгновенных сообщений. Изложенный алгоритм не претендует на академизм и хорош именно своей простотой и понятностью. Мне вообще очень нравятся простые решения — «в одну строчку».
Где-нибудь вначале можно передать серверное время. На стороне клиента вычислили смещение и подправили даты в нужных спанах.

Согласен. Я тоже всегда так делаю :)
Тут есть еще ряд проблем:

Таким образом для правильного вывода дат (времени) в браузере пользователя нужно вычислить смещение между временем браузера и сервера.

Часы переводят в разных странах в разное время. Таким образом сдвиг будет разным для разного времени.

return two(d.getUTCDate())+'.'+ two(d.getUTCMonth()+1)+'.'+d.getUTCFullYear()+' '+ two(d.getUTCHours())+':'+ two(d.getUTCMinutes());

Можно получить «несуществующее» время для дат, которые были близко к переводу стрелок на час вперед (часы перевели в 02:00 на час вперед, и время 02:15 не будет корректным).
Пример с timestamp сообщения не очень удачный. Имхо, эту информацию сервер и так знает, ее не нужно отправлять от клиента и переводить куда-то. А вот если клиент вводит какое-то время, например, время события в будущем, то важно знать что он имел в виду, т.е. его смещение от GMT.
А почему нельзя хранить в базе все даты в UTC? И на клиенте просто делать Date.UTC(dateFromServer), браузер сам приведёт к локальному времени.
Например такое может быть. На Вашем устройстве отключена сихронизация времени (это часто используется). Вы отправили сообщение в чат в Москве, но часовой пояс на Вашем устройстве стоял для Вьетнама. Потом улетели в США. Опять поменяли время на местное, но часовой пояс опять неверный на устройстве остался. Какое время для отправленого сообщения Вы увидите?
Когда я отправлял сообщение, в базе сохранилось серверное UTC-время. И при любых манипуляциях с настройками устройства и смене поясов я увижу то же самое время конвертированное к моей текущей локали. На моём телефоне сейчас 14:24 и, если я отправил сообщение час назад, то я увижу 13:24, независимо от того, с какого пояса я отправлял.
Надо подумать… Отвечу вечером.
Что-то у меня не получается сходу, то что Вы советуете. (про UTC на сервере и Date.UMC)
Приведите пожалуйста конкретный пример. Как получать время на сервере? И как выводить в браузер?
Если, будет работать Ваш вариант (по идее так всё и должно быть), то всё изложенное мною это изобретение велосипеда и надо будет переписать статью.
Конкретный пример не приведу, но опишу как реализую этот момент (отображение времени получения информации) сам с поправками на то, что описано в статье (неверно установленный часовой пояс, не настроенная синхронизация времени с ntp-сервером или провайдером, ручная установка неправильного времени на клиенте).

В первую очередь разделяем серверную и клиентскую части. Достаточно только того, что клиент знает, что время всех сообщений, пришедших с сервера, задано в UTC и знает точное серверное время (опять же в UTC) на момент загрузки скрипта:

var deltaUTC = ( new Date() ).getTime() - <?=microtime( true );?>;

Серверу же все равно, какое время у клиента, он все сообщения сохраняет в UTC (для php — это date_default_timezone_set('UTC') и date() (или time())).

Выдача сообщений происходит через обработчик на клиенте (JavaScript-интерфейс), где параметр времени преобразовывается в требуемое значение или с помощью prototype, приведу пример с обычной функцией:

var mydate = function ( date ) {
    var time = new Date ( date );
    time.setTime ( time.getTime() + deltaUTC );
    /* в качестве возврата будем использовать значение в привычном для пользователя формате */
    return time.toLocaleDateString() + ' ' + time.toLocaleTimeString();
};

Передача сообщений в обработчик может происходить как посредством AJAX, так и в момент загрузки самой страницы (данные представляют собой JavaScript объект).

Главное помнить, что серверное время является эталоном, и само значение лучше не менять в процессе преобразований.

Такой подход решает проблему, когда в одном и том же чате общаются люди из разных часовых поясов, для каждого из них время относительно их самих. И даже если они переедут в другую часовую зону или перенастроят часы, время на сайте изменится, чтобы остаться относительным для клиента.

Таким образом решается проблема «двух-этапного метода» — на сервер нет необходимости передавать данные о клиентском времени.
У вас, похоже, смешано две проблемы:
  • проблема часовых поясов;
  • проблема не верно установленного времени на клиенте.


Для решения проблемы не верно установленного времени нужно:
  1. сохранение для всех отправленных/полученных сообщений только серверного (эталонного) времени, ибо клиенту доверять нельзя;
  2. вычисление разницы клиентского и эталонного времени перед отображением времени на клиенте;


Проблема обработки часовых поясов для отображения местного времени может решаться только на клиенте путем конвертации UTC (с учетом поправки неверно выставленного времени) в местное время. При этом клиентское приложение должно это делать с учетом системных настроек.

Выполнять эту операцию на сервере очень не тривиально хотя бы потому что:
  1. очень трудно выяснить, какая временная зона установлена на клиенте;
  2. практически нереально убедиться, что установленная на клиенте временная зона имеет актуальные параметры (куча андроидов, к примеру, до сих пор ничего не знает про изменения в зоне Europe/Moscow).
Именно про пункты 1 и 2 (для не верно установленного времени) речь и идёт. То есть в моём алгоритме часовые пояса или зоны, как таковые не имеют значение. У нас это решение используется в мессенджере мгновенных сообщений.
> Вот Вы знаете какой сейчас у нас часовой пояс +3 или +4 часа?
Кого у нас? В дефолтсити?
JavaScript поддерживает стандарт rfc822 / ISO 8601. Правда встречаются баги в разных браузерах при некоторых способах записи (codepen).
Sign up to leave a comment.

Articles