Как хранить повторяющиеся даты, имея в виду Летнее время

Я храню события в своей базе данных. У меня есть время начала и окончания, "tickets_start" и "ticket_end" (когда продажа билетов фактически начинается/заканчивается - в отличие от начала/конца фактического события).

До сих пор я создал методы, которые делают все забавные вещи, такие как преобразование даты/времени в GMT перед сохранением, а затем обратно в соответствующий часовой пояс для отображения.

Я храню часовой пояс в поле varchar со значениями, такими как "America/New_York".

Но теперь мне нужно начать работать, если пользователь хочет разрешить повторять события. Я делал это раньше, и это не такая уж большая сделка, но никогда не в течение нескольких часовых поясов.

Сначала я думал, что это будет неважно, но потом понял, что если начальная дата начала была в июле (как пример), и она повторяется каждый месяц в течение года, в какой-то момент, летнее время сделает так, чтобы преобразование из GMT изменило время по-другому. Один месяц, при преобразовании 12:00, он изменил бы его на -5, а следующий, он изменил бы его на -4 из-за DST.

Моя нынешняя мысль заключается в том, что я буду хранить "dst" tinyint (1) для того, были ли введены даты начала/окончания во время DST, а затем сделать способ изменить время на час, если/когда это необходимо.

Но, полагаю, я бы спросил здесь, надеюсь, может быть, есть "нормальный" для этого или легкий, о котором я не думаю.

(cakephp 2.4.x)

Ответ 1

Во-первых, примите к сведению, что в современной терминологии вы должны указывать UTC вместо GMT. Они в основном эквивалентны, за исключением того, что UTC более точно определен. Зарезервируйте термин GMT для обозначения части часового пояса в Соединенном Королевстве, которая действует в течение зимних месяцев, имеющих смещение UTC + 0.

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

Я недавно писал об этом в другом ответе и является одним из немногих исключений, когда местное время имеет больше смысла, чем UTC. Главный аргумент - "проблема с будильником". Если вы установите будильник по UTC, вы будете просыпаться час раньше или поздно в день перехода DST. Вот почему большинство людей устанавливают свои будильники по местному времени.

Конечно, вы не можете просто сохранить местное время, если работаете с данными со всего мира. Вы должны хранить несколько разных вещей:

  • Местное время повторяющегося события, например "08:00"
  • Временной зоной, в которой выражено местное время, например "Америка/Нью-Йорк"
  • Повторяющийся шаблон в любом формате имеет смысл для вашего приложения, например, ежедневно, раз в две недели или третий четверг месяца и т.д.
  • Следующий немедленный эквивалент даты и времени UTC, чтобы вы могли его проецировать.
  • Возможно, но не всегда, список дат и времени будущего события UTC, проецировал некоторый предопределенный период в будущее (возможно, неделя, возможно, 6 месяцев, возможно, год или два, в зависимости от ваших потребностей).

За последние два, понять, что UTC эквивалент любой локальной даты/времени может измениться, если правительство, ответственное за этот часовой пояс, решит что-то изменить. Поскольку каждый год обновляется несколько обновлений базы данных часовых поясов, вы захотите иметь план подписаться на анонсы обновлений и обновить свою базу данных часовых поясов. Всякий раз, когда вы обновляете данные о часовом поясе, вам необходимо пересчитать эквивалентные времена UTC для всех будущих событий.

Наличие эквивалентов UTC важно, если вы планируете показывать список событий, охватывающий более одного часового пояса. Это те значения, которые вы запросите для создания этого списка.

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

Если вы искали простой ответ - извините, но его нет. Планирование будущих событий по часовым поясам - сложная задача.

Альтернативный подход

У меня было несколько человек, которые показали мне технику, когда они используют время UTC для планирования, то есть они выбирают дату начала в локальное время, конвертируют ее в UTC для хранения и сохраняют идентификатор часового пояса как Что ж. Затем во время выполнения они применяют часовой пояс, чтобы преобразовать исходное время UTC в локальное время, а затем использовать это локальное время для вычисления других повторений, как если бы они были первоначально сохранены, как указано выше.

Хотя этот метод будет работать, недостатки следующие:

  • Если обновление часового пояса, которое изменяет локальное время до запуска первого экземпляра, оно отключит весь график. Это можно смягчить, выбрав время в прошлом для "первого" экземпляра, так что второй экземпляр действительно является первым.

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

  • Он добавляет дополнительную сложность без какой-либо выгоды. Я бы зарезервировал эту технику для ситуаций, когда у вас, возможно, был планировщик только для UTC, который вы пытаетесь модифицировать поддержку часового пояса.