Как хранить повторяющиеся даты с учетом перехода на летнее время
Я храню события в моей базе данных. У меня есть даты начала и окончания, tickets_start и tickets_end (для случаев, когда продажи билетов действительно начинаются / заканчиваются - в отличие от начала / конца фактического события).
До сих пор я создал методы, которые делают все самое интересное, например, преобразование даты / времени в GMT перед сохранением, а затем обратно в соответствующий часовой пояс для отображения.
Я храню часовой пояс в поле varchar со значениями, такими как "America/New_York".
Но - теперь мне нужно начать иметь дело, если пользователь хочет разрешить повторение событий. Я делал это раньше, и это не так уж важно, но никогда в разных часовых поясах.
Сначала я думал, что это не будет большой проблемой, но потом понял, что - если начальная дата начала была в июле (как пример), и она повторяется каждый месяц в течение года, в какой-то момент переход на летнее время сделает это так что преобразование из GMT изменит время по-другому. Один месяц, при преобразовании 12:00, он изменит его на -5, а в следующий - на -4 из-за летнего времени.
Моя текущая мысль заключается в том, что я сохраню "dst" tinyint(1) для того, были ли введены даты начала / окончания во время летнего времени, а затем создаю метод для изменения времени на час, если / когда это необходимо.
Но - подумал я бы спросил здесь в надежде, может быть, есть "нормальное" для этого или легкое что-то, о чем я не думаю.
(cakephp 2.4.x)
1 ответ
Во-первых, пожалуйста, поймите, что в современной терминологии вы должны говорить UTC вместо GMT. Они в основном эквивалентны, за исключением того, что UTC более точно определено. Зарезервируйте термин GMT, чтобы обозначить часть часового пояса в Великобритании, которая действует в течение зимних месяцев со смещением UTC+0.
Теперь к вашему вопросу. UTC не обязательно лучший способ хранить все значения даты и времени. Это работает особенно хорошо для прошлых событий или для будущих абсолютных событий, но это не так хорошо работает для будущих локальных событий - особенно будущих повторяющихся событий.
Недавно я написал об этом в другом ответе, и это один из немногих исключительных случаев, когда местное время имеет больше смысла, чем UTC. Основным аргументом является "проблема будильника". Если вы установите будильник по UTC, вы проснетесь на час раньше или позже в день перехода на летнее время. Вот почему большинство людей устанавливают свои будильники по местному времени.
Конечно, вы не можете просто хранить местное время, если работаете с данными со всего мира. Вы должны хранить несколько разных вещей:
- Местное время повторяющегося события, например, "08:00".
- Часовой пояс, в котором выражено местное время, например "Америка / Нью-Йорк"
- Шаблон повторения в любом формате, который имеет смысл для вашего приложения, например, ежедневно, раз в две недели или в третий четверг месяца и т. Д.
- Следующая ближайшая дата и время в формате UTC, в лучшем случае, вы можете спроецировать ее.
- Возможно, но не всегда, список даты и времени UTC для будущего события, спроецированный на определенный заранее определенный период в будущем (возможно, неделю, возможно, 6 месяцев, возможно, год или два, в зависимости от ваших потребностей).
В отношении последних двух следует понимать, что UTC-эквивалент любой локальной даты / времени может измениться, если правительство, ответственное за этот часовой пояс, решит что-либо изменить. Поскольку ежегодно обновляется несколько баз данных часовых поясов, вам нужно будет иметь план подписки на объявления об обновлениях и регулярного обновления базы данных часовых поясов. Всякий раз, когда вы обновляете данные своего часового пояса, вам необходимо пересчитать UTC-эквивалентное время всех будущих событий.
Наличие UTC-эквивалентов важно, если вы планируете показывать любые виды событий, охватывающих более одного часового пояса. Это те значения, которые вы запросите для создания этого списка.
Еще один момент, на который следует обратить внимание: если событие запланировано на местное время, которое происходит во время аварийного перехода DST, вам нужно будет решить, происходит ли событие в первом экземпляре (обычно) или во втором (иногда), или оба (редко), и встроите в свое приложение механизм, гарантирующий, что событие не сработает дважды, если вы этого не хотите.
Если вы искали простой ответ - извините, но его нет. Планирование будущих событий в разных часовых поясах является сложной задачей.
Альтернативный подход
У меня было несколько человек, которые показали мне технику, в которой они используют время UTC для составления расписания, а именно: они выбирают начальную дату по местному времени, конвертируют ее в UTC для сохранения и сохраняют идентификатор часового пояса. Затем во время выполнения они применяют часовой пояс для преобразования исходного времени UTC обратно в местное время, а затем используют это местное время для вычисления других повторений, как если бы оно было первоначально сохраненным, как указано выше.
Пока этот метод будет работать, недостатки:
Если есть обновление часового пояса, которое меняет местное время перед запуском первого экземпляра, оно отключит весь график. Это можно смягчить, выбрав время в прошлом для "первого" экземпляра, чтобы второй экземпляр действительно был первым.
Если время действительно является "плавающим временем", которое должно следовать за пользователем (например, в будильнике на сотовом телефоне), вам все равно придется хранить информацию о часовом поясе для зоны, в которой он был изначально создан - даже если это не та зона, в которой вы хотите работать.
Это добавляет дополнительную сложность без какой-либо выгоды. Я бы зарезервировал этот метод для ситуаций, когда у вас может быть планировщик только для UTC, в который вы пытаетесь установить поддержку часового пояса.