Rails 3.1: запрос Postgres для записей в пределах временного диапазона - PullRequest
1 голос
/ 11 декабря 2011

В моем приложении у меня есть Person модель. Каждый Person имеет атрибут time_zone, который указывает часовой пояс по умолчанию. У меня также есть модель Event. Каждая Event имеет метку времени start_time и end_time, сохраненную в базе данных Postgres по времени UTC.

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

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

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

Прямо сейчас я настраиваю:

@today    = Time.now.in_time_zone(@person.time_zone).midnight.to_date
@tomorrow = (@today + 1.day ).to_datetime
@today    = @today.to_datetime

Мой запрос выглядит так:

@todays_activities = @person.marks.where("(start_time >= ? AND start_time < ?) OR (end_time >= ? AND end_time < ?);", @today, @tomorrow, @today, @tomorrow ).order("start_time DESC")

Как мне изменить это так, чтобы я гарантированно получал только результаты с сегодняшнего дня (согласно @person.time_zone в запросе @todays_activities?

1 Ответ

10 голосов
/ 12 декабря 2011

Вы теряете свои часовые пояса, когда звоните to_date, поэтому не делайте этого:

@today    = Time.now.in_time_zone(@person.time_zone).midnight.utc
@tomorrow = @today + 1.day

Когда вы some_date.to_datetime, вы получаете экземпляр DateTime, который находится в UTC, поэтому результат примерно такой:

Time.now.in_time_zone(@person.time_zone).midnight.to_date.to_datetime

будет иметь время суток 00:00:00 и часовой пояс UTC; 00:00:00 - это правильное время суток в @person.time_zone, но не в UTC (если, конечно, @person не находится в +0 часовом поясе).

И вы можете упростить ваш запрос с помощью overlaps:

where(
    '(start_time, end_time) overlaps (timestamp :today, timestamp :tomorrow)',
    :today => @today, :tomorrow => @tomorrow
)

Обратите внимание, что overlaps работает с полуоткрытыми интервалами :

Каждый период времени считается представляющим полуоткрытый интервал start <= time < end, если только начало и конец не равны, в этом случае он представляет этот единственный момент времени.

...