web-dev-qa-db-fra.com

Quelle est la méthode la plus recommandée pour stocker du temps dans PostgreSQL en utilisant Java?

Je stocke deux dates dans la base de données PostgreSQL. Premièrement, ce sont les données de visite d'une page Web, et la deuxième date est la date de la dernière modification de la page Web (c'est aussi long).

J'ai quelques doutes sur la meilleure stratégie pour stocker ces valeurs.

Je n'ai besoin que de jour/mois/année et heure: secondes et ce ne sera que pour les propositions statistiques.

Donc, quelques doutes:

  • est-il préférable de stocker aussi longtemps et de convertir lors de la récupération d'informations ou de stocker dans le format de données ci-dessus?
  • est-il préférable de fixer la date de visite sur le logiciel ou dans l'insertion dans la base de données?
  • en Java, comment sont les meilleures classes pour gérer les dates?
48
Renato Dinhani

Toute stratégie de stockage de données de date et d'heure dans PostgreSQL devrait, à mon avis, s'appuyer sur ces deux points:

  • Votre solution doit jamais dépendre du paramètre de fuseau horaire du serveur ou du client.
  • Actuellement, PostgreSQL (comme la plupart des bases de données) n'a pas de type de données pour stocker une date et heure complète avec le fuseau horaire. Vous devez donc choisir entre un type de données Instant ou LocalDateTime .

Ma recette suit.


Si vous voulez enregistrer l'instant physique au moment où un événement particulier s'est produit, (un vrai " horodatage ", généralement un événement de création/modification/suppression), puis utilisez:

(Ne laissez pas les types de données particuliers de PostgreSQL WITH TIMEZONE/WITHOUT TIMEZONE Vous dérouter: aucun d'entre eux ne stocke réellement un fuseau horaire)

Un code standard: ce qui suit suppose que ps est un PreparedStatement, rs un ResultSet et tzUTC est un statique Calendar objet correspondant au fuseau horaire UTC.

public static final Calendar tzUTC = Calendar.getInstance(TimeZone.getTimeZone("UTC"));  

Écrivez Instant dans la base de données TIMESTAMPTZ:

Instant instant = ...;
Timestamp ts = instant != null ? new Timestamp(instant.toEpochMilli()) : null;
ps.setTimestamp(col, ts, tzUTC);   // column is TIMESTAMPTZ!

Lire Instant dans la base de données TIMESTAMPTZ:

Timestamp ts = rs.getTimestamp(col,tzUTC); // column is TIMESTAMPTZ
Instant inst = ts !=null ? Instant.ofEpochMilli(ts.getTime()) : null;

Cela fonctionne en toute sécurité si votre type de PG est TIMESTAMPTZ (dans ce cas, le calendarUTC n'a aucun effet dans ce code; mais il est toujours conseillé de ne pas dépendre des fuseaux horaires par défaut). "En toute sécurité" signifie que le résultat ne dépendra pas du fuseau horaire du serveur ou de la base de données , ni des informations de fuseaux horaires: l'opération est entièrement réversible et quoi qu'il arrive aux paramètres de fuseaux horaires , vous obtiendrez toujours le même "instant de temps" que vous aviez à l'origine du côté Java.


Si, au lieu d'un horodatage (un instant sur la chronologie physique), vous avez affaire à une date-heure locale "civile" (c'est-à-dire ensemble de champs {year-month-day hour:min:sec(:msecs)}), vous utiliseriez:

Lire LocalDateTime dans la base de données TIMESTAMP:

Timestamp ts = rs.getTimestamp(col, tzUTC); //
LocalDateTime localDt = null;
if( ts != null ) 
    localDt =  LocalDateTime.ofInstant(Instant.ofEpochMilli(ts.getTime()), ZoneOffset.UTC);

Écrivez LocalDateTime dans la base de données TIMESTAMP:

  Timestamp ts = null;
  if( localDt != null)    
      ts = new Timestamp(localDt.toInstant(ZoneOffset.UTC).toEpochMilli()), tzUTC);
  ps.setTimestamp(colNum,ts, tzUTC); 

Encore une fois, cette stratégie est sûre et vous pouvez dormir paisiblement: si vous avez stocké 2011-10-30 23:59:30, Vous récupérerez toujours ces champs précis (heure = 23, minute = 59 ... etc), quoi qu'il arrive - même si demain le fuseau horaire de votre serveur (ou client) Postgresql change, ou votre fuseau horaire JVM ou OS, ou si votre pays modifie ses règles DST, etc.


Ajouté: Si vous voulez (cela semble une exigence naturelle) pour stocker la spécification de datetime complète (a ZonedDatetime : l'horodatage avec le fuseau horaire, qui inclut implicitement également les informations de datetime civil complet - plus le fuseau horaire) ... alors j'ai une mauvaise nouvelle pour vous: PostgreSQL n'a pas de type de données pour cela (ni d'autres bases de données, à ma connaissance). Vous devez concevoir votre propre stockage, peut-être dans une paire de champs: pourrait être les deux types ci-dessus (très redondants, bien qu'efficaces pour la récupération et le calcul), ou l'un d'eux plus le décalage temporel (vous perdez les informations de fuseau horaire, certains calculs deviennent difficile, et certains impossible), ou l'un d'eux plus le fuseau horaire (sous forme de chaîne; certains calculs peuvent être extrêmement coûteux).

75
leonbloy

Java.time

Ce n'est pas joli mais c'est ce qui a fonctionné pour moi avec une instance ZonedDateTime utilisant le nouveau framework Java.time dans Java = 8 et versions ultérieures ( Tutorial ):

ZonedDateTime receivedTimestamp = some_ts_value;
Timestamp ts = new Timestamp(receivedTimestamp.toInstant().toEpochMilli());
ps.setTimestamp(
   1, 
   ts, 
   Calendar.getInstance(TimeZone.getTimeZone(receivedTimestamp.getZone()))
); 
3
Pablo Santa Cruz

Utilisation Java.util.Date dans votre Java et timestamp with time zone dans votre base de données.

2
Jonas