Pull to refresh

Comments 29

При отправке данных на сервер клиент должен преобразовать все в UTC

В сочетании с "клиенту доверять нельзя" (а ему доверять нельзя) вы получаете потерю информации. Вы не знаете, какой часовой пояс был у клиента, и как он его преобразовал в UTC. Более того, если клиент передает время в UTC, не указывая, что это UTC (т.е., тогда, когда вы отказались от контроля за передачей смещения), вы не знаете, передал он UTC, или же свое локальное время.

Намного проще требовать от клиента время с явно указанным смещением — в этом случае вы не теряете никакой информации, но всегда гарантированно можете получить из этого UTC.
Сорри, промахнулся с ответом — он ниже в комментариях.
Там далее в статье написано, что есть требования при которых такой подход не будет работать, и нужно хранить дополнительно смещение часового пояса. Противоречия нет, просто последовательные соображения )
При общении с неизвестным клиентом эти требования есть всегда — поэтому проще и не формулировать правила "клиент должен все преобразовывать в UTC", оно все равно никогда не будет выполняться.

(и, кстати, описанной мной ситуации там нет)
Если под неизвестным клиентом подразумевается, что серверное АПИ будет непонятно кто по итогу использовать, то соглашусь. Такое ограничение для "произвольных" клиентов может оказаться неочевидным и неожиданным. Чуть позже обновлю статью.
Под неизвестным клиентом подразумевается любой, который вами полностью не контролируется. Даже если его код полностью вам подконтролен, но он установлен на неизвестном вам устройстве — это в данном контексте неизвестный вам клиент, потому что вы понятия не имеете, какой там часовой пояс.
Если код полностью подконтролен и нет требований, по которым есть надобность хранить часовой пояс клиента — не вижу проблемы сделать на клиенте преобразование в UTC перед отправкой. Тут абсолютно равноценно — что клиент преобразует сам, что перешлет на сервер локальное время + смещение, для того, чтобы сервер сам уже выполнил это преобразование.
Тут абсолютно равноценно — что клиент преобразует сам, что перешлет на сервер локальное время + смещение, для того, чтобы сервер сам уже выполнил это преобразование.

Неравноценно. Вы потеряли информацию (например, что клиент думал, что у него часовой пояс +4, хотя по всем разумным признакам должен думать, что +3). Лучше всего это работает, если передавать одновременно часовой пояс (как название) и смещение, но это уже требует выхода за рамки стандартов, и потому редко когда реализуемо.

(и да, именно поэтому часовой пояс клиента надо хранить по умолчанию, а не только тогда, когда на это есть явные требования)
Не совсем понял — эта информация о смещении для дальнейшего саппорта? Чтобы была возможность потом посмотреть, откуда всязалась ошибка в смещении? Если так — окей, понял.
Для саппорта, для дополнительной хитрой логики на сервере, для принятия решений… если у вас есть информация, и ее хранение вам не доставляет (существенных) дополнительных усилий — не выбрасывайте ее. Вы не сможете получить ее обратно.
Второй нюанс с получением текущего времени — это то, что клиенту доверять нельзя
идеально, когда клиентское время вообще нигде не фигурирует в БД в качестве метки времени, а везде используется только время БД (для MS SQL — это getUTCDate()).
Но такое не всегда возможно сделать эффективно без лишнего roundtrip сервер-клиент-сервер. В таких случаях я в начале сессии клиент-сервер обычно вместе с каким-нибудь полезным ответом сервера (например, подтверждения авторизации) шлю клиенту UTC-время БД. Клиент в свою очередь, считает на своей стороне разницу между UTC-временем по своим часам и полученным от сервера, считает поправку, и использует эту поправку при отправке на сервер меток времени. Конечно, здесь появляется погрешность на время прохода пакета от клиента к серверу, но это все равно лучше, чем просто отправлять время на клиентской стороне. Такой подход мной использовался, например, в авторизационных токенах с временем жизни порядка минуты.
Сыграем в смешную игру. Давно хотел в нее сыграть, а тут отличный повод выдался.

Менеджер Иванов, находясь в Москве, 2 марта 2016 года выдал поручение инженеру Петрову, находящемуся в командировке в Хабаровске. Сроком исполнения поручения в системе документооборота Ивановым было установлено 11 марта 2016 года без указания времени.

Вопрос — какой срок исполнения поручения должен быть отображен Петрову? При решении задачи учитывать, что:
1. система документооборота, используемая Ивановым и Петровым поддерживает установку времени для поручений;
2. рабочий день Иванова по нормативным документам — с 9 утра до 6 вечера, рабочий день Петрова не нормирован, график — 2/1, 11 марта у него выходной.
Такие вопросы надо выяснять у заказчика и фиксировать в документации, а не играть в подобные игры.
Это я к вопросу о передачи дат без преобразований и часовых поясов. Довольно шаткое положение, как по мне.
Приведенный вами пример не является случаем даты без времени. Если менеджер и вводит только дату, то показываться требуемое время исполнения должно как дата + время, а это уже само по себе противоречит исходному условию. Более того — назначение срока исполнения без конечного времени само по себе выглядит не очень удачным решением. Если я выполню задание в 23:30 1 фераля по московскому времени или 3:30 ночи 2 февраля — может ли так случиться, что для бизнеса по итогу разницы между этими временами нет, хотя дата исполнения стоит 1 февраля?
По московскому времени. В филиалах и командировках, как раз чтобы в такие игры не играть, ориентируются на время головной организации.
А почему нельзя хранить метки в epoch time?
В смысле в количестве секунд, прошедших с 1 января 1970 года?
Это уже давно устаревший подход, он как минимум не позволяет:

  • хранить смещение часового пояса
  • хранить даты ранее 1970 года
  • иметь точность выше секундной
  • сразу визуально понимать при отладке, что за время записано в поле
  • если вам это очень надо для показа пользователю, храните отдельным полем. Преобразовать можно на стороне клиента
  • отрицательные значения
  • хранить ms epoch
  • настройте отображение, если вам надо.
Зачем делать вручную все вышеперечисленное, если уже давно существуют типы, которые все это делают "под капотом"?
Зачем писать свои велосипеды под таймзоны, когда
а) Работа с тамзонами — это боль и унижение. См, например, https://habrahabr.ru/company/mailru/blog/242645/ или https://habrahabr.ru/post/100741/ или http://stackoverflow.com/questions/178704/are-unix-timestamps-the-best-way-to-store-timestamps
б) что вы имеете под "вручную"? Конвертировать human-readable в timestamp и обратно? А ваша контвертация между конечным форматом и форматом для пользователя — это ли не велосипед с ручным приводом?

Кстати, укажите, где написано, что unix timestamp — устаревший подход, а ваш UTC — новый и хороший?
Извините, я не очень понял. Как хранение времени в формате unix timestamp избавляет от отображения этого времени по-разному для разных временных зон? И чем он отличается от того, что вы называете "ваш UTC"?
Никак. "ваш UTC" — то, что описано в статье, хранение данных как UTC время.
Я понял, что такое "ваш UTC". Я хотел узнать, чем ваше решение от него отличается. Начало отсчета unix time ведь находится в UTC, соответственно и сама метка тоже является временем в UTC.
Устаревший ваш подход только в том, что используется целочисленный тип, на замену которого уже давно есть типы "datetime", которые и отображают хорошо при чтении из базы и из дебага, а классы, которые есть в платформах еще и сами всю конверсию умеют делать с помощью утилитных методов.

Не "мой" метод — новый и хороший, это уже давно общая практика. Он по сути говорит о том же, что и вы — что нужно хранить в универсальном времени. Только вы говорите о том, что для этого нужно хранить какое-то там количество секунд, а я говорю о том, что для этого есть готовые типы, которые, более того, умеют еще и смещение таймзоны хранить, если надо. В чем проблема?
Внутри эти готовые типы (по крайней мере "Дата и время" из вашей классификации) скорее всего и хранят unix timestamp, или какую-то подобную метку времени. Просто во многих случаях гораздо удобнее мыслить timestamp-ами, так как они представляют собой просто абсолютный момент времени во вселенной, независимо от временной зоны, тогда не приходится говорить ни о каких конвертациях, вы просто сравниваете числа, передаете числа из одной зоны в другую, применяете ту или иную таймзону для отображения timestamp-а.
Sign up to leave a comment.

Articles