Mysql
 sql >> Base de données >  >> RDS >> Mysql

Quels sont les paramètres du pilote JDBC-mysql pour une gestion saine de DATETIME et TIMESTAMP en UTC ?

La solution consiste à définir le paramètre de connexion JDBC noDatetimeStringSync=true avec useLegacyDatetimeCode=false . En bonus j'ai aussi trouvé sessionVariables=time_zone='-00:00' atténue le besoin de set time_zone explicitement à chaque nouvelle connexion.

Il existe un code de conversion de fuseau horaire "intelligent" qui est activé au plus profond de ResultSet.getString() lorsqu'elle détecte que la colonne est un TIMESTAMP colonne.

Hélas, ce code intelligent a un bogue :TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) renvoie un Timestamp mal étiqueté au fuseau horaire par défaut de la JVM, même lorsque le tz paramètre est défini sur autre chose :

final static Timestamp fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) {
    Calendar cal = (tz == null) ? new GregorianCalendar() : new GregorianCalendar(tz);
    cal.clear();

    // why-oh-why is this different than java.util.date, in the year part, but it still keeps the silly '0' for the start month????
    cal.set(year, month - 1, day, hour, minute, seconds);

    long tsAsMillis = cal.getTimeInMillis();

    Timestamp ts = new Timestamp(tsAsMillis);
    ts.setNanos(secondsPart);

    return ts;
}

Le retour ts serait parfaitement valide sauf lorsque plus haut dans la chaîne d'appel, il est reconverti en une chaîne à l'aide du simple toString() méthode, qui rend le ts sous forme de chaîne représentant ce qu'une horloge afficherait dans le fuseau horaire par défaut de la JVM, au lieu d'une représentation sous forme de chaîne de l'heure en UTC. Dans ResultSetImpl.getStringInternal(int columnIndex, boolean checkDateTypes) :

                case Types.TIMESTAMP:
                    Timestamp ts = getTimestampFromString(columnIndex, null, stringVal, this.getDefaultTimeZone(), false);

                    if (ts == null) {
                        this.wasNullFlag = true;

                        return null;
                    }

                    this.wasNullFlag = false;

                    return ts.toString();

Définition de noDatetimeStringSync=true désactive tout le gâchis d'analyse/désanalyse et renvoie simplement la valeur de chaîne telle qu'elle est reçue de la base de données.

Résultat du test :

0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

Le useLegacyDatetimeCode=false est toujours important car il modifie le comportement de getDefaultTimeZone() pour utiliser le TZ du serveur de base de données.

En poursuivant, j'ai également trouvé la documentation pour useJDBCCompliantTimezoneShift est incorrect, bien que cela ne fasse aucune différence :la documentation indique [Cela fait partie du code date-heure hérité, donc la propriété n'a d'effet que lorsque "useLegacyDatetimeCode=true." ], mais c'est faux, voir ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean) .