2010-12-11 5 views
0

J'ai besoin de faire des opérations date-heure sur ma base de données qui nécessitent tous les timbres-poste created pour avoir la même année. J'ai essayé avec cette requête:Timestamp manipulations dans PostgreSQL - problème avec les heures/fuseau horaire

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; 

... mais le résultat est que partiellement satisfaisant:

 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) 

Comme vous pouvez le voir, tous les horodateurs sont -1 heures comparant aux valeurs created d'origine. Ce qui est encore plus étrange, c'est que celui d'avant-dernier (2010-12-15 11:07:06) ne l'est pas.

created la colonne est de type timestamp without time zone.

Une idée de ce que j'ai mal fait?

Répondre

1

Le problème avec votre SQL est qu'il ne transforme pas une date en une date dans une autre année. Il calcule les deltas pour created et 2010-01-01 et les convertit en une date. Donc, vous ne supprimez pas la partie de l'année, mais calculez combien il y a longtemps.

Voici une autre façon d'aborder le problème. Il va juste remplacer l'année, laissant le reste de la date intacte.

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; 

Vous pouvez également le faire avec des sous-chaînes. TO_TIMESTAMP() va passer le 2010-02-29 au 2010-03-01, donc il est sûr à utiliser.

1

Ceci est la solution de sous-chaîne jmz mentionné:

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

Merci les gars, à la fois jmz et a_horse_with_no_name solutions fonctionnent très bien.

J'ai également corrigé ma solution. Le problème était avec to_timestamp qui renvoie timestamp with time zone et je m'attendais à timestamp without time zone. Il suffisait d'ajouter un classeur:

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;