Get “time with time zone” from “time without time zone” and the time zone name

后端 未结 2 1798
别那么骄傲
别那么骄傲 2020-12-24 12:59

First off, I realize time with time zone is not recommended. I am going to use it because I\'m comparing multiple time with time zone values to my

2条回答
  •  无人及你
    2020-12-24 13:40

    Here is a demo how to calculate the times without casting to text:

    CREATE TEMP TABLE schedule(t time, tz text);
    INSERT INTO schedule values
     ('12:00:00', 'America/Vancouver')
    ,('12:00:00', 'US/Mountain')
    ,('12:00:00', 'America/Regina');
    
    SELECT s.t AT TIME ZONE s.tz
            - p.utc_offset
            + EXTRACT (timezone from now()) * interval '1s'
    FROM   schedule s
    JOIN   pg_timezone_names p ON s.tz = p.name;
    

    Basically you have to subtract the UTC offset and add the offset of your local time zone to arrive at the given time zone.

    You can speed up the calculation by hardcoding your local offset. In your case (America/Regina) that should be:

    SELECT s.t AT TIME ZONE s.tz
            - p.utc_offset
            - interval '6h'
    FROM   schedule s
    JOIN   pg_timezone_names p ON s.tz = p.name;
    

    As pg_timezone_names is a view and not actually a system table, it is rather slow - just like the demonstrated variant with casting to text representation and back.

    I would store the time zone abbreviations and take the double cast via text without joining in pg_timezone_names for optimum performance.


    FAST solution

    The culprit that's slowing you down is pg_timezone_names. After some testing I found that pg_timezone_abbrevs is far superior. Of course, you have to save correct time zone abbreviations instead of time zone names to achieve this. Time zone names take DST into consideration automatically, time zone abbreviations are basically just codes for a time offset. The documentation:

    A time zone abbreviation, for example PST. Such a specification merely defines a particular offset from UTC, in contrast to full time zone names which can imply a set of daylight savings transition-date rules as well.

    Have a look at these test results or try yourself:

    SELECT * FROM  pg_timezone_names;
    

    Total runtime: 541.007 ms

    SELECT * FROM pg_timezone_abbrevs;
    

    Total runtime: 0.523 ms

    Factor 1000. Whether you go with your idea to cast to text and back to timetz or with my method to compute the time is not important. Both methods are very fast. Just don't use pg_timezone_names.

    Actually, as soon as you save time zone abbreviations, you can take the casting route without any additional joins. Use the abbreviation instead of the utc_offset. Results are accurate as per your definition.

    CREATE TEMP TABLE schedule(t time, abbrev text);
    INSERT INTO schedule values
     ('12:00:00', 'PST')  -- 'America/Vancouver'
    ,('12:00:00', 'MST')  -- 'US/Mountain'
    ,('12:00:00', 'CST'); -- 'America/Regina'
    
    -- calculating
    SELECT s.t AT TIME ZONE s.abbrev
         - a.utc_offset
         + EXTRACT (timezone from now()) * interval '1s'
    FROM   schedule s
    JOIN   pg_timezone_abbrevs a USING (abbrev);
    
    -- casting (even faster!)
    SELECT (t::text || abbrev)::timetz
    FROM   schedule s;
    

提交回复
热议问题