Postgresql:问题与间隔&夏令时

时间:2017-10-30 12:14:40

标签: postgresql

(PostgreSQL 9.4.8)

我写了一个简单的SQL函数来按间隔对记录进行分组(按1天,1小时,63分钟等分组......)

CREATE OR REPLACE FUNCTION public.timestamp_bucket(startdate timestamp with time zone, step interval, datetime timestamp with time zone) RETURNS timestamptz 
AS 
$BODY$ 
SELECT startdate::timestamptz + 
        (
            (
                floor(
                    EXTRACT(EPOCH FROM (datetime - startdate::timestamp)) / 
                        EXTRACT(EPOCH FROM step)
                )::integer * EXTRACT(EPOCH FROM step)
            )  || ' seconds' 
        )::interval;
$BODY$ LANGUAGE sql VOLATILE COST 100;

样品:

由'1day'组成

$ select timestamp_bucket('2017-10-29 00:00:00+02'::timestamptz, '1day'::interval, '2017-10-29 00:01:02'::timestamptz);
    timestamp_bucket    
------------------------
 2017-10-29 00:00:00+02

按“6分钟”分组

$ select timestamp_bucket('2017-09-17 00:00:00+02'::timestamptz, '6minutes'::interval, '2017-09-18 13:10:02'::timestamptz);
timestamp_bucket    
------------------------
 2017-09-18 13:06:00+02

但是当有夏令时它不起作用......

在法国,时区在2017年10月28日至29日的夜晚从'+02'变为'+01' 结果现在是:

select timestamp_bucket('2017-10-29 00:00:00+02'::timestamptz, '1day'::interval, '2017-10-30 09:01:02'::timestamptz);
    timestamp_bucket    
------------------------
 2017-10-29 23:00:00+01

我可以用以下方式检查:

# select '2017-10-29 00:00:00+02'::timestamptz +  '24:00:00'::interval;
        ?column?        
------------------------
 2017-10-29 23:00:00+01

但是

select '2017-10-29 00:00:00+02'::timestamptz +  '1day'::interval;
        ?column?        
------------------------
 2017-10-30 00:00:00+01

好的,29日有25个小时,但有没有办法解决这个问题 timestamp_bucket('2017-10-01 00:00:00+02'::timestamptz, '1day'::interval, '2017-10-30 09:01:02'::timestamptz) = '2017-10-30 00:00:00+01'

1 个答案:

答案 0 :(得分:0)

感谢@ michel.milezzi我发布了解决方案:

CREATE OR REPLACE FUNCTION public.timestamp_bucket(startdate timestamp with time zone, step interval, datetime timestamp with time zone) RETURNS timestamptz AS 
$BODY$ 
SELECT (startdate::timestamp + 
        (
            (
                floor(
                    EXTRACT(EPOCH FROM (datetime::timestamp - startdate::timestamp)) / 
                        EXTRACT(EPOCH FROM step)
                )::integer * EXTRACT(EPOCH FROM step)
            )  || ' seconds' 
        )::interval)::timestamptz;
$BODY$ LANGUAGE sql VOLATILE COST 100;

我也尝试转换24小时'在白天,有类似的东西,但我不喜欢它:

CREATE OR REPLACE FUNCTION public.timestamp_bucket(startdate timestamp with time zone, step interval, datetime timestamp with time zone) RETURNS timestamptz 
AS 
$BODY$ 
WITH partial AS (
    SELECT (
            (
                floor(
                    EXTRACT(EPOCH FROM (datetime - startdate::timestamp)) / 
                        EXTRACT(EPOCH FROM step)
                )::integer * EXTRACT(EPOCH FROM step)
            )  || ' seconds' 
        )::interval AS _interval
)
SELECT startdate::timestamptz + 
    CASE WHEN _interval >= '24:00:00'::interval THEN
        (
            EXTRACT(YEARS FROM _interval) || 'years ' || 
            EXTRACT(MONTHS FROM _interval) || 'months ' ||
            (EXTRACT(DAYS FROM _interval) + (EXTRACT(HOUR FROM _interval) / 24) ) || 'days ' ||
            (EXTRACT(HOUR FROM _interval)::integer % 24) || 'hours ' ||
            EXTRACT(MINUTES FROM _interval) || 'minutes ' ||
            EXTRACT(SECONDS FROM _interval) || 'seconds ' ||
            EXTRACT(MILLISECONDS FROM _interval) || 'millisecond ' ||
            EXTRACT(MICROSECONDS FROM _interval) || 'microseconds'
        )::interval
    ELSE _interval END

FROM partial;
        ;
$BODY$ LANGUAGE sql VOLATILE COST 100;