简体   繁体   中英

Timestamp manipulations in PostgreSQL - problem with hours/time zone

I need to do some date-time operations on my database which require all created timestamps to have the same year. I tried with this query:

select created,cast (created - 
    to_timestamp(''||date_part('year', created), 'YYYY') + 
    timestamp '2010-01-01' as timestamp without time zone) 
    from table where created is not null limit 10;

... but the result is only partially satisfying:

       created       |      timestamp      
---------------------+---------------------
 2010-08-29 12:44:05 | 2010-08-29 11:44:05
 2007-06-21 13:49:22 | 2010-06-21 12:49:22
 2007-06-21 15:02:33 | 2010-06-21 14:02:33
 2007-06-21 15:05:26 | 2010-06-21 14:05:26
 1999-09-30 13:00:00 | 2010-09-30 12:00:00
 1997-06-07 13:00:00 | 2010-06-07 12:00:00
 2010-06-15 20:51:01 | 2010-06-15 19:51:01
 2006-08-26 15:33:02 | 2010-08-26 14:33:02
 2009-12-15 11:07:06 | 2010-12-15 11:07:06
 1997-06-28 13:00:00 | 2010-06-28 12:00:00
(10 rows)

As you can see, all timestamps are -1 hour comparing to original created values. What's even more strange is that the one before last one ( 2010-12-15 11:07:06 ) is not.

created column is timestamp without time zone type.

Any idea what I did wrong?

The problem with your SQL is that it does not turn a date into a date in another year. It calculates deltas for created and 2010-01-01 and converts that to a date. So you're not actually removing the year part, but calculating how long ago it was.

Here's another way to approach the problem. It will just replace the year, leaving the rest of the date untouched.

SELECT TO_TIMESTAMP('2010-' || CAST(EXTRACT(MONTH FROM created) AS VARCHAR) || '-' || CAST(EXTRACT(DAY FROM created) AS VARCHAR) || ' ' || CAST(EXTRACT(HOUR FROM created) AS VARCHAR) ||':'||CAST(EXTRACT(MINUTE FROM created) AS VARCHAR) ||':'|| CAST(EXTRACT(SECONDS FROM created) AS VARCHAR), 'YYYY-MM-dd HH24:MI:SS') AS timestamp FROM table;

You could do this with substrings too. TO_TIMESTAMP() will cast the day 2010-02-29 to 2010-03-01, so it's safe to use.

This is the substring solution jmz mentioned:

select to_timestamp('2010'||substring(to_char(created, 'yyyy-mm-dd hh24:mi:ss.ms'), 5), 'yyyy-mm-dd hh24:mi:ss.ms')

Thanks guys, both jmz and a_horse_with_no_name solutions work fine.

I also fixed my solution. The problem was with to_timestamp which returns timestamp with time zone and I expected timestamp without time zone . It was enough to add a typecast:

select created,cast (created-to_timestamp(''||
    date_part('year', created), 'YYYY')::timestamp without time zone + 
    timestamp '2010-01-01' as timestamp without time zone) 
    from table;

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