简体   繁体   中英

PostgreSQL: Compute number of hours in the day on daylight savings time

I'm trying to come up with a query that will properly count that there are 25 hours on daylight savings. My table has a column of type timestampz called hourly_timestamp. The incorrect answer I have so far looks like this:

select EXTRACT(epoch FROM tomorrow-today)/3600
from(
  select date_trunc('day', timezone('America/New_York', hourly_timestamp) as today ,  
         date_trunc('day', timezone('America/New_York', hourly_timestamp))) 
                                                        + '1 day'::interval as tomorrow
    )t;

When this query executed during daylight savings time, I still only get 24 hours back and not 25. Any ideas how to do this correctly?

You first have to do the extraction to epoch and then the calculations:

WITH test AS (
    SELECT  '2014-10-26'::timestamptz at time zone 'America/New_York' AS today,
        '2014-10-27'::timestamptz at time zone 'America/New_York' AS tomorrow
)
SELECT  
    extract(epoch from tomorrow)  - extract(epoch from today)       AS seconds,  -- 90000
    (extract(epoch from tomorrow) - extract(epoch from today)) / 3600   AS hours -- 25
FROM    test;

The number of hours varies with the clock.

with hours as (
  select (timestamp with time zone '2014-11-01 00:00:00 America/New_York' + (n || ' hour')::interval) as hourly_timestamp
  from generate_series(0, 72) n
)
select hourly_timestamp
     , hourly_timestamp + interval '1' day as one_day_later
     , hourly_timestamp + interval '1' day - hourly_timestamp as elapsed_time
from hours;
hourly_timestamp         one_day_later            elapsed_time
--
[snip]
2014-11-01 22:00:00-04   2014-11-02 22:00:00-05   1 day 01:00:00
2014-11-01 23:00:00-04   2014-11-02 23:00:00-05   1 day 01:00:00
2014-11-02 00:00:00-04   2014-11-03 00:00:00-05   1 day 01:00:00
2014-11-02 01:00:00-04   2014-11-03 01:00:00-05   1 day 01:00:00
2014-11-02 01:00:00-05   2014-11-03 01:00:00-05   1 day
2014-11-02 02:00:00-05   2014-11-03 02:00:00-05   1 day
2014-11-02 03:00:00-05   2014-11-03 03:00:00-05   1 day
2014-11-02 04:00:00-05   2014-11-03 04:00:00-05   1 day
[snip]

Note that 01:00 repeats, but with a different offset. Daylight savings time ends at 02:00, the clocks fall back and repeat the hour between 01:00 and 02:00, but since daylight savings time has ended, there are now five hours between the UTC and America/New_York time zones.

This similar query displays dates, not timestamps.

with dates as (
  select (timestamp with time zone '2014-11-01 00:00:00 America/New_York' + (n || ' day')::interval) as daily_timestamp
  from generate_series(0, 2) n
)
select daily_timestamp::date
     , (daily_timestamp + interval '1' day)::date as one_day_later
     , daily_timestamp + interval '1' day - daily_timestamp as elapsed_time
from dates;
daily_timestamp  one_day_later  elapsed_time
--
2014-11-01       2014-11-02     1 day
2014-11-02       2014-11-03     1 day 01:00:00
2014-11-03       2014-11-04     1 day

Where did you go wrong? By calculating the elapsed time after you truncated the time information. (Dates don't have time zones associated with them.) If I take the second query and cast "daily_timestamp" to a date in the common table expression, I get 24 hours, too.

with dates as (
  select (timestamp with time zone '2014-11-01 00:00:00 America/New_York' + (n || ' day')::interval)::date as daily_timestamp
  from generate_series(0, 2) n
)
select daily_timestamp::date
     , (daily_timestamp + interval '1' day)::date as one_day_later
     , daily_timestamp + interval '1' day - daily_timestamp as elapsed_time
from dates;
daily_timestamp  one_day_later  elapsed_time
--
2014-11-01       2014-11-02     1 day
2014-11-02       2014-11-03     1 day
2014-11-03       2014-11-04     1 day

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM