web-dev-qa-db-fra.com

Le fuseau horaire Java.sql.Timestamp est-il spécifique?

Je dois stocker UTC dateTime dans DB.
J'ai converti l'heure UTC indiquée dans un fuseau horaire spécifique. pour cela j'ai suivi le code ci-dessous.
La date et l'heure de mon entrée est "20121225 10:00:00 Z" le fuseau horaire est "Asie/Calcutta".
Mon serveur/ma base de données (Oracle) s'exécute dans le même fuseau horaire (IST) "Asie/Calcutta"

récupère 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

La base de données (Oracle) a stocké la même donnée dateTime: "20121225 10:00:00 Pas en UTC.

J'ai confirmé de ci-dessous sql.

     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 "Asie/Calcutta"

Il me donne les apparences ci-dessous

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

ne autre question:

Est-ce que timeStamp.toString() imprimera dans le fuseau horaire local comme le fait Java.util.date? Pas UTC?

73
Kanagavelu Sugumar

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

Définit le paramètre désigné sur la valeur Java.sql.Timestamp Donnée, à l'aide de l'objet Calendar donné. Le pilote utilise l'objet Calendar pour construire une valeur SQL TIMESTAMP qu'il envoie ensuite à la base de données. Avec un objet Calendar, le pilote peut calculer l’horodatage en tenant compte d’un fuseau horaire personnalisé. Si aucun objet Calendar n'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 correspondent à ce qui est stocké dans la base de données. Si la colonne de la base de données ne stocke pas les informations de fuseau horaire, toutes les informations relatives à la zone sont perdues (c’est-à-dire que les applications utilisant la base de données utilisent le toujours le même fuseau horaire ou avec un autre schéma pour distinguer le fuseau horaire (c.-à-d. le stocker dans une colonne séparée).

Par exemple: Votre fuseau horaire local est GMT + 2. Vous stockez "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: vous le récupérez sous le nom "2012-12-25 10:00:00 UTC" (uniquement si vous le récupérez à l'aide de getTimestamp(..)), mais lorsqu'une autre application accède à la base de données dans le fuseau horaire. GMT + 0, l’horodatage sera "2012-12-25 12:00:00 UTC".

Si vous souhaitez le stocker dans un fuseau horaire différent, vous devez utiliser 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 lors de la récupération de valeurs (si vous utilisez un TIMESTAMP sans information de fuseau horaire dans votre base de données).

Donc, en supposant que vous souhaitiez 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 devrait prendre en charge Java.time.LocalDateTime (Et Java.time.LocalTime) Pour TIMESTAMP (et TIME) à get/set/updateObject. Les classes Java.time.Local* Étant dépourvues de fuseau horaire, aucune conversion ne doit être appliquée (bien que cela puisse poser de nouveaux problèmes si votre code supposait un fuseau horaire spécifique).

95
Mark Rotteveel

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

Dans PreparedStatement.setTimestamp (int parametreIndex, Horodatage x, calage du calendrier), le pilote utilise le calendrier pour modifier le fuseau horaire par défaut. Mais l'horodatage a toujours une durée millisecondes en GMT.

L'API ne sait pas exactement comment le pilote JDBC est censé utiliser Calendar. Les fournisseurs semblent être libres de l’interpréter, par exemple. la dernière fois que j'ai travaillé avec MySQL 5.5 Calendar, le pilote n'a tout simplement pas tenu compte de Calendar, tant dans PreparedStatement.setTimestamp que dans ResultSet.getTimestamp.

29
Evgeniy Dorofeev

C'est spécifique de votre pilote. Vous devez fournir un paramètre dans votre programme Java pour lui indiquer le fuseau horaire que vous souhaitez utiliser.

Java -Duser.timezone="America/New_York" GetCurrentDateTimeZone

De plus ceci:

to_char(new_time(sched_start_time, 'CURRENT_TIMEZONE', 'NEW_TIMEZONE'), 'MM/DD/YY HH:MI AM')

Peut également être utile pour gérer correctement la conversion. Tiré de ici

5
Woot4Moo

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

Voici quelques problèmes et limitations connus de MySQL Connector/J: Lorsque Connector/J récupère les horodatages d'un jour de commutation d'heure d'été à l'aide de la méthode getTimeStamp () sur le jeu de résultats, certaines des valeurs renvoyées peuvent être erronées. Les 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 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 seconde 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 retourne: 1461093056000 = 19/04/2016 - 19:10:56 GMT

La seconde 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.

5
Abdelhafid