PostgreSQL: выбор строк, которые происходят в определенный день недели, в определенном часовом поясе

79
9

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


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


<model>.where("EXTRACT(dow FROM (time + interval '#{time_zone_offset} hours')) = ?", Time.zone.now.wday)
.where('time BETWEEN ? AND ?', 5.weeks.ago.end_of_week, 1.week.ago.end_of_week)
.select("organisation_id, date_trunc('hour', time) as grouped_time, avg(staff) as staff")
.group("grouped_time").order("grouped_time")

И это переводит на этот SQL:


SELECT date_trunc('hour', time) as grouped_time, avg(staff) as staff
FROM <model>
WHERE (EXTRACT(dow FROM (time + interval '10 hours')) = 0)
AND (time BETWEEN '2013-09-22 13:59:59.999999' AND '2013-10-20 13:59:59.999999')
GROUP BY grouped_time
ORDER BY grouped_time

В этом случае time_zone_offset будет отличаться в зависимости от пользователя, с которым я выполняю поиск:


def time_zone_offset
@tzoffset ||= ActiveSupport::TimeZone[current_user.organisation.time_zone].utc_offset / 60 / 60
end

(Текущий часовой пояс также задается в round_filter, так что 1.week.ago и т.д. находятся в правильной зоне.)


Часовой пояс моей базы данных - UTC (set TIME ZONE 'UTC').


Это работает, но я уверен, что есть лучший способ найти записи в определенный день недели, а затем вручную манипулировать interval. Я также не думаю, что это будет работать с DST в часовых поясах, где это применимо. Я знаю, что PostgreSQL способен преобразовать time with time zone в определенный часовой пояс, но это не включает дату, поэтому я не могу выяснить день недели! Другие предложения WHERE, которые я пробовал:


    EXTRACT (dow from time') = 0 - это дает мне снимки между 10:00 по воскресенье и 10:00 понедельника.


    EXTRACT (dow from time at time zone 'Brisbane/Australia') = 0 - это дает мне действия с 8 вечера по воскресенье и 8 вечера в понедельник. Я понимаю, что это происходит потому, что Postgres обрабатывает поле time, как если бы оно было в Брисбене, вместо того, чтобы преобразовывать его из UTC в время Брисбена.


Поэтому мне бы хотелось узнать, есть ли что-то, что я должен сделать, чтобы этот запрос работал.

спросил(а) 2021-01-19T15:45:30+03:00 8 месяцев назад
1
Решение
91

AT TIME ZONE, когда применяется к timestamp without timezone, получается a timestamp with timezone (и наоборот). И этот timestamp with timezone интерпретируется в вашем часовом поясе сеанса (который в вашем случае вынужден UTC).


Таким образом, выражение EXTRACT (dow from time at time zone 'Brisbane/Australia') не извлекает день в Брисбене в time (UTC), он извлекает день, соответствующий преобразованному time, с точки зрения человека, фактически живущего в часовом поясе UTC.


В качестве примера, когда я набираю это, если вы претендуете на UTC:



=> set timezone to 'UTC';
=> select now(),now() at time zone 'Australia/Brisbane';
now | timezone
------------------------------+---------------------------
2013-10-27 18:01:03.15286+00 | 2013-10-28 04:01:03.15286

Хорошо, это воскресенье 18:01 в UTC и в понедельник 04:01 в Брисбене


Но если применить смещение часового пояса к a timestamp without timezone:


select now(),now()::timestamp at time zone 'Australia/Brisbane';
now | timezone
-------------------------------+-------------------------------
2013-10-27 18:01:57.878541+00 | 2013-10-27 08:01:57.878541+00

Обратите внимание, как второй столбец отличается от предыдущего. Это на самом деле 20 часов от Брисбена, выраженное в UTC: это, по-видимому, технически правильный ответ на вопрос, который не имеет большого смысла.


Предположительно вы хотите:


EXTRACT (dow from (time AT TIME ZONE 'UTC') at time zone 'Brisbane/Australia')=0

на который следует ответить: соответствует ли дата и время time измеренным в UTC воскресным в Брисбене?

ответил(а) 2021-01-19T15:45:30+03:00 8 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема