В общем, наилучшая практика при работе с датами - хранить их в UTC и конвертировать обратно в то, что ожидает пользователь на уровне приложения.
Это не обязательно работает с будущими датами, особенно когда дата/время зависит от пользователя в их часовом поясе.
В моем случае атрибут theres содержит метку времени, содержащую start_time будущего события, по сравнению со всем остальным, что сейчас или в прошлом (включая отметки времени created_at
и updated_at
).
Почему
Это конкретное поле является меткой времени будущего события, где пользователь выбирает время.
Для будущих событий кажется, что наилучшей практикой является не хранить UTC.
Вместо экономии времени в UTC вместе с часовым поясом разработчики могут сохранить то, что пользователь ожидает от нас, чтобы сохранить: время стены.
Когда пользователь выбирает 10 утра, он должен оставаться на уровне 10 утра, даже когда пользователи смещаются от изменений в UTC между созданием и датой события из-за перехода на летнее время.
Итак, в июне 2016 года, если пользователь создаст событие для 1 января 2017 года в полночь в Сиднее, эта метка времени будет сохранена в базе данных как 2017-01-01 00:00
. Смещение в момент создания будет +10: 00, но во время события itd будет +11: 00.. если правительство не решит изменить это тем временем.
Как и мудрый, Id ожидает отдельное событие, которое я создаю в течение 1 января 2016 года в полночь в Брисбене, чтобы оно также хранилось как 2017-01-01 00:00
. Я сохраняю часовой пояс, т.е. Australia/Brisbane
в отдельном поле.
Каков наилучший способ сделать это в Rails?
Ive пробовал множество вариантов без успеха:
1. Пропустить преобразование
Проблема, это только пропускает преобразование при чтении, а не в записи.
self.skip_time_zone_conversion_for_attributes = [:start_time]
2. Измените всю конфигурацию приложения, чтобы использовать config.default_timestamp :local
Для этого я устанавливаю:
конфигурации /application.rb
config.active_record.default_timezone = :local
config.time_zone = 'UTC'
приложение/модель/event.rb
...
self.skip_time_zone_conversion_for_attributes = [:start_time]
before_save :set_timezone_to_location
after_save :set_timezone_to_default
def set_timezone_to_location
Time.zone = location.timezone
end
def set_timezone_to_default
Time.zone = 'UTC'
end
...
Чтобы быть откровенным, я не уверен, что это делает. Но не то, что я хочу.
Я думал, что это работает, поскольку мое событие в Брисбене хранилось как 2017-01-01 00:00
, но когда я создал новое событие для Сиднея, оно было сохранено как 2017-01-01 01:00
, хотя оно отображается как полночь правильно в представлении.
В этом случае я обеспокоен тем, что все еще имеет ту же проблему с событием в Сиднее, которое я пытаюсь избежать.
3. Переопределите геттер и сеттер для сохранения модели как целого
Ive попытался также сохранить событие start_time как целое число в базе данных.
Я пробовал сделать это, обезьяна патчирует класс Time и добавляет обратный вызов before_validates
для преобразования.
конфигурации/инициализаторы/time.rb
class Time
def time_to_i
self.strftime('%Y%m%d%H%M').to_i
end
end
приложение/модель/event.rb
before_validation :change_start_time_to_integer
def change_start_time_to_integer
start_time = start_time.to_time if start_time.is_a? String
start_time = start_time.time_to_i
end
# read value from DB
# TODO: this freaks out with an error currently
def start_time
#take integer YYYYMMDDHHMM and convert it to timestamp
st = self[:start_time]
Time.new(
st / 100000000,
st / 1000000 % 100,
st / 10000 % 100,
st / 100 % 100,
st % 100,
0,
offset(true)
)
end
Идеальное решение
Id нравится иметь возможность хранить временную метку в своем естественном типе данных в базе данных, поэтому запросы не становятся беспорядочными в моих контроллерах, но я не могу понять, как хранить "время стены", которое не конвертирует.
Во-вторых, Id рассчитывается для целочисленного варианта, если мне нужно.
Как другие справляются с этим? Что мне не хватает? В частности, с опцией "целочисленное преобразование" выше, я делаю вещи намного сложнее, чем они должны быть.