grzaks
grzaks

Reputation: 1414

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?

Upvotes: 0

Views: 2249

Answers (3)

grzaks
grzaks

Reputation: 1414

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;

Upvotes: 0

user330315
user330315

Reputation:

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')

Upvotes: 1

jmz
jmz

Reputation: 5479

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.

Upvotes: 1

Related Questions