#datetime #google-bigquery #dst
#datetime #google-bigquery #летнее время
Вопрос:
У меня есть данные в Google BigQuery, которые выглядят следующим образом:
sample_date_time_UTC time_zone milliseconds_between_samples
-------- --------- ----------------------------
2019-03-31 01:06:03 UTC Europe/Paris 60000
2019-03-31 01:16:03 UTC Europe/Paris 60000
...
Выборки данных ожидаются через регулярные промежутки времени, указанные значением milliseconds_between_samples
поля:
Это time_zone
строка, представляющая значение часового пояса, поддерживаемого Google Cloud
Затем я проверяю соотношение фактического количества выборок по сравнению с ожидаемым числом за любой конкретный день для любого диапазона одного дня (выраженного как локальная дата для данного time_zone
):
with data as
(
select
-- convert sample_date_time_UTC to equivalent local datetime for the timezone
DATETIME(sample_date_time_UTC,time_zone) as localised_sample_date_time,
milliseconds_between_samples
from `mytable`
where sample_date_time between '2019-03-31 00:00:00.000000 01:00' and '2019-04-01 00:00:00.000000 02:00'
)
select date(localised_sample_date_time) as localised_date, count(*)/(86400000/avg(milliseconds_between_samples)) as ratio_of_daily_sample_count_to_expected
from data
group by localised_date
order by localised_date
Проблема в том, что в этом есть ошибка, поскольку я жестко запрограммировал ожидаемое количество миллисекунд в день 86400000
. Это неверно, поскольку, когда переход на летнее время начинается в указанном time_zone
( Europe/Paris
), день становится на 1 час короче. Когда переход на летнее время заканчивается, день увеличивается на 1 час.
Итак, приведенный выше запрос неверен. Он запрашивает данные за 31 марта этого года в Europe/Paris
часовом поясе (когда в этом часовом поясе начался переход на летнее время). Миллисекунды в этот день должны быть 82800000
.
Как я могу получить правильное количество миллисекунд для указанного запроса localised_date
?
Обновить:
Я попытался сделать это, чтобы посмотреть, что он возвращает:
select DATETIME_DIFF(DATETIME('2019-04-01 00:00:00.000000 02:00', 'Europe/Paris'), DATETIME('2019-03-31 00:00:00.000000 01:00', 'Europe/Paris'), MILLISECOND)
Это не сработало — я получаю 86400000
Ответ №1:
Вы можете получить разницу в миллисекундах для двух временных меток, удалив 01:00
и 02:00
. Обратите внимание, что это дает разницу между временными метками в UTC: 90000000
, что не совпадает с фактическими прошедшими миллисекундами.
Вы можете сделать что-то вроде этого, чтобы получить миллисекунды за один день:
select 86400000 (86400000 - DATETIME_DIFF(DATETIME('2019-04-01 00:00:00.000000', 'Europe/Paris'), DATETIME('2019-03-31 00:00:00.000000', 'Europe/Paris'), MILLISECOND))
Ответ №2:
Спасибо @Juta, за подсказку об использовании времени UTC для расчета. Поскольку я группирую свои данные за каждый день по локализованной дате, я выяснил, что могу вычислять миллисекунды для каждого дня, получая начальную и конечную дату и время (в UTC) для моей «локализованной» даты, используя следующую логику:
-- get UTC start datetime for localised date
-- get UTC end datetime for localised date
-- this then gives the milliseconds for that localised date:
datetime_diff(utc_end_datetime, utc_start_datetime, MILLISECOND);
Итак, мой полный запрос становится:
with daily_sample_count as (
with data as
(
select
-- get the date in the local timezone, for sample_date_time_UTC
DATE(sample_date_time_UTC,time_zone) as localised_date,
milliseconds_between_samples
from `mytable`
where sample_date_time between '2019-03-31 00:00:00.000000 01:00' and '2019-04-01 00:00:00.000000 02:00'
)
select
localised_date,
count(*) as daily_record_count,
avg(milliseconds_between_samples) as daily_avg_millis_between_samples,
datetime(timestamp(localised_date, time_zone)) as utc_start_datetime,
datetime(timestamp(date_add(localised_date, interval 1 day), time_zone)) as utc_end_datetime
from data
)
select
localised_date,
-- apply calculation for ratio_of_daily_sample_count_to_expected
-- based on the actual vs expected number of samples for the day
-- no. of milliseconds in the day changes, when transitioning in/out of daylight saving - so we calculate milliseconds in the day
daily_record_count/(datetime_diff(utc_end_datetime, utc_start_datetime, MILLISECOND)/daily_avg_millis_between_samples) as ratio_of_daily_sample_count_to_expected
from
daily_sample_count