Pull to refresh

Comments 16

Зачем все эти сложности, когда проще на бэкенде хранить все в UTC, а на клиенте уже вычислять с учетом локального часового пояса?
Так и стараемся работать по возможности в UTC, но полностью работать в UTC не получается даже тогда, когда бэкенд и фронтенд раздельные (т.е. API и клиент) — всё равно нужно знать, когда же именно у данного конкретного пользователя полночь, просто чтобы отделить завтрашние котлеты от сегодняшних мух.
Когда же речь идёт о классическом сайте, который рендерит готовый html на сервере — это всё нужно. И пользователь в datetimepicker'е угадайте какое время вводит? Своё, родное…
На самом деле это выглядит как костыль, который лень сделать нормально.
Я в основном про смену зоны в around_action. У нас если и вычисляется для конкректного юзера то только в фоновых задачах и при постановке в расписание, и то, но результат всегда в UTC.

В чем проблема на клиенте переводить данные из дэйтпикера в UTC при отправке и обратно в локальную зону при получении?
Присоединяюсь к комментарию.
В чем проблема работать с таймзонами только на фронтэнде?
На бэкенде проще только с UTC работать, единственное, что нужно — сохранить таймзону в аттрибуте юзера, и потом при рендеринге добавить что-нибудь типа
<body data-user-timezone="<%= {name: current_user.timezone.name, offset: current_user.timezone.utc_offset}.as_json %>">...<body>

Потом в JS эту таймзону прочитать — и уже с ней работать.
Мы примерно так и работаем — идентификатор таймзоны прилетает при логине пользователя и мы его используем в javascript'е. И всё время отображается в этом часовом поясе. Метки времени передаются между фронтендом и бекендом в ISO8601 и на самом деле не важно, передаются ли они в UTC (с суффиксом Z) или в локальном времени (с суффиксом +0300, например), главное, чтобы смещение от UTC было и всё будет работать.

Но и бэкенд у нас не глупый — у нас хватает различных валидаций и генераций, завязанных на часовой пояс пользователя. Что-нибудь в стиле «проверить, что изменённый пользователем номер заявки за день не дублируется». А день начинается не в полночь. Ну то есть это и не день совсем, а вообще рабочая смена. И начинается: получи таймстампы начала и конца той смены, спроси у базы, нет ли заявки с таким номером в этот промежуток. Аналогично нужно и выдавать новые номера заявкам. И не только за день, но ещё и за год. А год тоже начинается не в полночь, и уж тем более не по UTC. Не получается только с UTC работать, ну никак.
Проблема возникает в тот самый момент, когда нужно полноценно работать во временем, в самом простом случае и в 99% приложений все эти сложности не нужны. Если нужны прям конкретные примеры — то один из сценариев описан тут: habrahabr.ru/company/mailru/blog/242645 (Cmf+F «Работаем со временем»), остальные — в статье по ссылке.
Вы говорите про API-приложение + клиент. Для них around_action не так актуален (хотя дебажить проще, когда таймстампы в локальном времени, а не в UTC).

А вот для «классического» приложения, в котором весь HTML генерённый на сервере и логики в JS нет, around_action — то, что доктор прописал. Всё сразу начинает работать как надо. Да и дэйтпикеры ещё допиливать надо, чтоб работали, как надо.

Я нашёл у around_action только один минус — мешается в stacktrace'ах — часто показывается, что ошибка в `block in the around_action`, а до настоящего виновника эксепшена, надо глазами ещё добежать.
Нет, не только API. На веб морде нпример у нас moment.js подключенный давным-давно минут за 10.

Сейчас вообще без логики на JS довольно уныло, нафиг так жить?)
Как насчёт портировать на руби $jin.time с добавлением таймзон?
В Ruby on Rails (т.е. в ActiveSupport) с временами и таймзонами всё и так достаточно хорошо. Почему это не портируют в стандартную библиотеку Ruby, в которой всё плохо — вопрос открытый.
Да вот судя по тому, что я вижу — не очень хорошо:
1. Внутри время хранится как таймштамп, а не покомпонентно.
2. Избыточный, неконсистентный апи.
3. Нет поддержки диапазонов и продолжительностей.
4. Мутабельные объекты.
5. Не мнемоничные паттерны вида "%Y-%m-%d %H:%i"

Вы статью-то читали? :-)
Конечно не читал, ведь чукча — писатель :-)

Пункты 1, 2, 4, 5 — это всё «обратная совместимость» и прочие корни, уходящие в Perl и C. Вылечат ли это когда-нибудь в стандартной библиотеке — неизвестно.
1. Разве для времени это критично, покомпонентно оно хранится или одним числом? Какие тут возможны «подводные камни?
3:
Для диапазонов используется класс Range (ему вообще пофиг, что у него на концах — числа, даты или таймстампы, пока у них есть методы сравнения и метод succ) — для наших задач пока хватает, но ограничения есть, про них мы в курсе.
Для продолжительностей есть ActiveSupport::Duration (это вот эта магия типа Date.current + 2.years), к 4-й версии рельс его уже сделали правильно, но у меня есть для него парочка Pull Request'ов, правда они висят уже год мёртвым грузом, что меня огорчает: #16917 и #16919
В крайнем случае есть и сторонние гемы, типа ISO8601, но для работы с ними нужно ваять свой адаптер для ActiveRecord'а. Пока такой необходимости не возникало — стандартная библиотека, несмотря на всё её несовершенство, вполне устраивает.
Оно критично, когда хранить нужно не «число миллисекунд от 1970», а, например, «январь 2015» (целиком январь, в любом часовом поясе) или «промежуток с 9:00 до 18:00» (без учёта секунд, в любой день). Там в статье есть ссылки на примеры, когда из-за сдвига часовых поясов у людей календарики ехали. А казалось бы, какое дело календарю до часовых поясов?
Хранить всё в UTC и конвертировать в него-же при обращении к БД не вариант? Тем более что current_user.time_zone это айди зоны из tzinfo.
Ruby on Rails именно так делает по умолчанию (в статье написано). И мы именно так делаем хотя бы просто потому, что нафиг не сдалось лезть в недра ActiveRecord и эти умолчания менять (где-то в исходниках я видел крутилку, да).

Вопрос именно в том, как с этими всеми временами обращаться уже в коде приложения. Всегда работать в UTC — не вариант (писал в комментариях выше).
Sign up to leave a comment.