An import script was written slightly wrong, leading to timestamps being inserted a factor of 1000 out. Using to_timestamp together with extract() however leads to dates about a month out, even though the intermediate numbers and conversions look to be correct.
1)
select start_time from matches order by match_id limit 1;
| timestamp without time zone
----------------------
1 | 1970-01-16 11:29:18.352
2)
select (extract(epoch from start_time) * 1000)
from matches order by match_id limit 1;
| double precision
----------------------
1 | 1333758352
3)
select to_timestamp(1333758352) at time zone 'UTC';
| timestamp without time zone
----------------------
1 | 2012-04-07 00:25:52
4)
select (to_timestamp(extract(epoch from start_time) * 1000) at time zone 'UTC')
from matches order by match_id limit 1;
| timestamp without time zone
----------------------
1 | 2012-05-18 16:25:52
1) shows the current data in a timestamp column, 2) and 3) shows the correct number and conversion being performed, but put together in one query in 4) the date ends up offset by over a month.
I'm kind of stumped on what the issue could be, as the components work individually, and simple math inside a to_timestamp also works in other situations I tried.
Reproducing (PostgreSQL 9.3.1):
create table test_table ( t timestamp );
insert into test_table VALUES ('1970-01-16 11:29:18.352');
select (extract(epoch from t) * 1000) from test_table;
select to_timestamp(1333758352) at time zone 'UTC';
select (to_timestamp(extract(epoch from t) * 1000) at time zone 'UTC') from test_table;
SqlFiddle: http://sqlfiddle.com/#!15/12f6f/1/0
On SqlFiddle it works on 8.4, but fails on 9.3.
Resolution and cause
The naive approach here hits an edge case where we end up multiplying the timezone offset of one hour along with the original date. So the result is 1000 hours offset (due to my being in GMT). Casting the non UTC time to have a time zone drops that GMT conversion letting the odd multiplication of a date proceed as expected.
So, extract(epoch from t::timestamp with time zone) when doing raw math on the result and then putting it back into a timestamp.