103 votes

Est-ce que java.sql.Timestamp est spécifique au fuseau horaire ?

Je dois stocker la date et l'heure UTC dans la base de données.
J'ai converti le dateTime donné dans un fuseau horaire spécifique en UTC. Pour cela, j'ai suivi le code ci-dessous.
Mon entrée dateTime est "20121225 10:00:00 Z" timezone est "Asia/Calcutta".
Mon serveur/DB (oracle) fonctionne dans le même fuseau horaire (IST) "Asie/Calcutta".

Obtenir l'objet Date dans ce fuseau horaire spécifique

        String date = "20121225 10:00:00 Z";
        String timeZoneId = "Asia/Calcutta";
        TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);

        DateFormat dateFormatLocal = new SimpleDateFormat("yyyyMMdd HH:mm:ss z");
                    //This date object is given time and given timezone
        java.util.Date parsedDate = dateFormatLocal.parse(date + " "  
                         + timeZone.getDisplayName(false, TimeZone.SHORT));

        if (timeZone.inDaylightTime(parsedDate)) {
            // We need to re-parse because we don't know if the date
            // is DST until it is parsed...
            parsedDate = dateFormatLocal.parse(date + " "
                    + timeZone.getDisplayName(true, TimeZone.SHORT));
        }

       //assigning to the java.sql.TimeStamp instace variable
        obj.setTsSchedStartTime(new java.sql.Timestamp(parsedDate.getTime()));

Stocker dans la base de données

        if (tsSchedStartTime != null) {
            stmt.setTimestamp(11, tsSchedStartTime);
        } else {
            stmt.setNull(11, java.sql.Types.DATE);
        }

SORTIE

DB (oracle) a stocké la même donnée dateTime: "20121225 10:00:00 pas en UTC.

J'ai confirmé à partir du sql ci-dessous.

     select to_char(sched_start_time, 'yyyy/mm/dd hh24:mi:ss') from myTable

Mon serveur de base de données fonctionne également sur le même fuseau horaire "Asia/Calcutta".

Il me donne les apparitions ci-dessous

  1. Date.getTime() n'est pas en UTC
  2. Ou le timestamp a un impact sur le fuseau horaire lors du stockage dans la base de données. Qu'est-ce que je fais de mal ici ?

Une dernière question :

Will timeStamp.toString() imprimer dans le fuseau horaire local comme java.util.date fait ? Pas l'UTC ?

121voto

Mark Rotteveel Points 20766

Bien que cela ne soit pas explicitement spécifié pour setTimestamp(int parameterIndex, Timestamp x) les conducteurs doivent suivre les règles établies par la setTimestamp(int parameterIndex, Timestamp x, Calendar cal) javadoc :

Définit le paramètre désigné à la valeur donnée java.sql.Timestamp en utilisant la valeur Calendar objet. Le pilote utilise le Calendar pour construire un objet SQL TIMESTAMP que le pilote envoie ensuite à la base de données. Avec un Calendar le pilote peut calculer l'horodatage en tenant compte d'un fuseau horaire personnalisé. Si aucun Calendar est spécifié, le pilote utilise le fuseau horaire par défaut, qui est celui de la machine virtuelle exécutant l'application.

Lorsque vous appelez avec setTimestamp(int parameterIndex, Timestamp x) le pilote JDBC utilise le fuseau horaire de la machine virtuelle pour calculer la date et l'heure de l'horodatage dans ce fuseau horaire. Cette date et cette heure sont stockées dans la base de données. Si la colonne de la base de données ne stocke pas d'informations sur le fuseau horaire, toute information sur le fuseau est perdue (ce qui signifie que c'est à l'application ou aux applications utilisant la base de données d'utiliser le même fuseau horaire de manière cohérente ou de trouver un autre système pour discerner le fuseau horaire (c'est-à-dire le stocker dans une colonne séparée).

Par exemple : Votre fuseau horaire local est GMT+2. Vous enregistrez "2012-12-25 10:00:00 UTC". La valeur réelle stockée dans la base de données est "2012-12-25 12:00:00". Vous le récupérez à nouveau : il s'agit de "2012-12-25 10:00:00 UTC" (mais seulement si vous le récupérez en utilisant la méthode suivante getTimestamp(..) ), mais lorsqu'une autre application accède à la base de données dans le fuseau horaire GMT+0, elle récupère l'horodatage sous la forme "2012-12-25 12:00:00 UTC".

Si vous voulez le stocker dans un fuseau horaire différent, vous devez utiliser la fonction setTimestamp(int parameterIndex, Timestamp x, Calendar cal) avec une instance de calendrier dans le fuseau horaire requis. Assurez-vous simplement que vous utilisez également le getter équivalent avec le même fuseau horaire lorsque vous récupérez des valeurs (si vous utilisez une instance de TIMESTAMP sans informations sur le fuseau horaire dans votre base de données).

Donc, en supposant que vous voulez stocker le fuseau horaire GMT réel, vous devez utiliser :

Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
stmt.setTimestamp(11, tsSchedStartTime, cal);

Avec JDBC 4.2, un pilote conforme doit prendre en charge les éléments suivants java.time.LocalDateTime (y java.time.LocalTime ) pour TIMESTAMP (y TIME ) par get/set/updateObject . En java.time.Local* n'ont pas de fuseau horaire, il n'est donc pas nécessaire d'appliquer une conversion (bien que cela puisse ouvrir une nouvelle série de problèmes si votre code suppose un fuseau horaire spécifique).

41voto

Evgeniy Dorofeev Points 52031

Je pense que la réponse correcte devrait être java.sql.Timestamp n'est PAS spécifique au fuseau horaire. Timestamp est un composite de java.util.Date et d'une valeur séparée en nanosecondes. Il n'y a pas d'information sur le fuseau horaire dans cette classe. Ainsi, tout comme Date, cette classe contient simplement le nombre de millisecondes depuis le 1er janvier 1970, 00:00:00 GMT + nanos.

Dans PreparedStatement.setTimestamp(int parameterIndex, Timestamp x, Calendar cal) Calendar est utilisé par le pilote pour changer le fuseau horaire par défaut. Mais Timestamp contient toujours les millisecondes en GMT.

L'API n'est pas claire sur la façon dont le pilote JDBC est censé utiliser Calendar. Les fournisseurs semblent se sentir libres de l'interpréter, par exemple, la dernière fois que j'ai travaillé avec MySQL 5.5 Calendar, le pilote a tout simplement ignoré Calendar dans PreparedStatement.setTimestamp et ResultSet.getTimestamp.

12voto

Douglas Surber Points 143

La réponse est que java.sql.Timestamp est un gâchis et doit être évité. Utilisez java.time.LocalDateTime à la place.

Alors pourquoi est-ce un gâchis ? De la java.sql.Timestamp JavaDoc, un java.sql.Timestamp est une "enveloppe mince autour de java.util.Date qui permet à l'API JDBC de l'identifier comme une valeur SQL TIMESTAMP". Dans le java.util.Date JavaDoc, "le Date est destinée à refléter le temps universel coordonné (UTC)". Dans la spécification ISO SQL, un TIMESTAMP WITHOUT TIME ZONE "est un type de données qui est une date sans fuseau horaire". TIMESTAMP est un nom court pour TIMESTAMP WITHOUT TIME ZONE. Ainsi, un java.sql.Timestamp "reflète" UTC alors que SQL TIMESTAMP est "sans fuseau horaire".

Parce que java.sql.Timestamp reflète UTC ses méthodes appliquent des conversions. Cela entraîne une confusion sans fin. Du point de vue de SQL, il n'est pas logique de convertir une valeur TIMESTAMP SQL dans un autre fuseau horaire, car un TIMESTAMP n'a pas de fuseau horaire à convertir. Que signifie la conversion de 42 en Fahrenheit ? Cela ne veut rien dire car 42 n'a pas d'unités de température. Il s'agit simplement d'un nombre simple. De même, vous ne pouvez pas convertir un TIMESTAMP de 2020-07-22T10:38:00 en Americas/Los Angeles parce que 2020-07-22T10:30:00 ne se trouve dans aucun fuseau horaire. Il n'est pas en UTC, GMT ou autre. Il s'agit d'une simple date-heure.

java.time.LocalDateTime est également un temps de date nue. Elle ne possède pas de fuseau horaire, exactement comme SQL TIMESTAMP. Aucune de ses méthodes n'applique une quelconque conversion de fuseau horaire, ce qui rend son comportement beaucoup plus facile à prévoir et à comprendre. N'utilisez donc pas java.sql.Timestamp . Utilisez java.time.LocalDateTime .

LocalDateTime ldt = rs.getObject(col, LocalDateTime.class);
ps.setObject(param, ldt, JDBCType.TIMESTAMP);

9voto

Ayush Kumar Points 165

Vous pouvez utiliser la méthode ci-dessous pour stocker l'horodatage dans la base de données spécifique à votre zone/identification de zone souhaitée.

ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/Calcutta")) ;
Timestamp timestamp = Timestamp.valueOf(zdt.toLocalDateTime());

Une erreur commune que les gens font est d'utiliser LocaleDateTime pour obtenir l'horodatage de cet instant qui écarte toute information spécifique à votre zone même si vous essayez de la convertir plus tard. Il ne comprend pas la zone.

Veuillez noter Timestamp est de la classe java.sql.Timestamp .

6voto

Abdelhafid Points 219

Pour Mysql, nous avons une limitation. Dans le pilote Mysql doc nous avons :

Voici quelques problèmes et limitations connus pour MySQL Connector/J : Lorsque Connector/J récupère les horodatages pour un jour de changement d'heure jour de passage à l'heure d'été en utilisant la méthode getTimeStamp() sur le jeu de résultats. ensemble de résultats, certaines des valeurs renvoyées peuvent être erronées. Ces erreurs peuvent erreurs peuvent être évitées en utilisant les options de connexion suivantes lors de la connexion à une base de données :

useTimezone=true
useLegacyDatetimeCode=false
serverTimezone=UTC

Ainsi, lorsque nous n'utilisons pas ces paramètres et que nous appelons setTimestamp or getTimestamp avec calendrier ou sans calendrier, nous avons l'horodatage dans le fuseau horaire jvm.

Exemple :

Le fuseau horaire de jvm est GMT+2. Dans la base de données, nous avons un horodatage : 1461100256 = 19/04/16 21:10:56,000000000 GMT

Properties props = new Properties();
props.setProperty("user", "root");
props.setProperty("password", "");
props.setProperty("useTimezone", "true");
props.setProperty("useLegacyDatetimeCode", "false");
props.setProperty("serverTimezone", "UTC");
Connection con = DriverManager.getConnection(conString, props);
......
Calendar nowGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
Calendar nowGMTPlus4 = Calendar.getInstance(TimeZone.getTimeZone("GMT+4"));
......
rs.getTimestamp("timestampColumn");//Oracle driver convert date to jvm timezone and Mysql convert date to GMT (specified in the parameter)
rs.getTimestamp("timestampColumn", nowGMT);//convert date to GMT 
rs.getTimestamp("timestampColumn", nowGMTPlus4);//convert date to GMT+4 timezone

La première méthode renvoie : 1461100256000 = 19/04/2016 - 21:10:56 GMT

La deuxième méthode renvoie : 1461100256000 = 19/04/2016 - 21:10:56 GMT

La troisième méthode renvoie : 1461085856000 = 19/04/2016 - 17:10:56 GMT

Au lieu d'Oracle, lorsque nous utilisons les mêmes appels, nous avons :

La première méthode renvoie : 1461093056000 = 19/04/2016 - 19:10:56 GMT

La deuxième méthode renvoie : 1461100256000 = 19/04/2016 - 21:10:56 GMT

La troisième méthode renvoie : 1461085856000 = 19/04/2016 - 17:10:56 GMT

NB : Il n'est pas nécessaire de spécifier les paramètres pour Oracle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X