sql >> Databasteknik >  >> RDS >> Mysql

Vilka är JDBC-mysql-drivrutininställningarna för sund hantering av DATETIME och TIMESTAMP i UTC?

Lösningen är att ställa in JDBC-anslutningsparametern noDatetimeStringSync=true med useLegacyDatetimeCode=false . Som en bonus hittade jag också sessionVariables=time_zone='-00:00' lindrar behovet av att set time_zone uttryckligen på varje ny anslutning.

Det finns en "intelligent" kod för tidszonsomvandling som aktiveras djupt inne i ResultSet.getString() metod när den upptäcker att kolumnen är en TIMESTAMP kolumn.

Tyvärr har den här intelligenta koden en bugg:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) returnerar en Timestamp felaktigt taggade till JVM:s standardtidszon, även när tz parametern är inställd på något annat:

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;
}

Returen ts skulle vara helt giltigt förutom när längre upp i anropskedjan, den konverteras tillbaka till en sträng med bara toString() metod, som återger ts som en sträng som representerar vad en klocka skulle visa i JVM-standardtidszonen, istället för en strängrepresentation av tiden i UTC. I 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();

Inställning av noDatetimeStringSync=true inaktiverar hela tolka/avtolka röran och returnerar bara strängvärdet som det tas emot från databasen.

Testutgång:

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

useLegacyDatetimeCode=false är fortfarande viktigt eftersom det ändrar beteendet hos getDefaultTimeZone() för att använda databasserverns TZ.

När jag jagade detta hittade jag också dokumentationen för useJDBCCompliantTimezoneShift är felaktig, även om det inte spelar någon roll:dokumentationen säger [Detta är en del av den äldre datum-tid-koden, så egenskapen har en effekt endast när "useLegacyDatetimeCode=true." ], men det är fel, se ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean) .




  1. Hur Floor() fungerar i PostgreSQL

  2. Vad är ett bra sätt att denormalisera en mysql-databas?

  3. nvarchar-konkatenering / index / nvarchar(max) oförklarligt beteende

  4. Skapa ny tabell i befintlig DB i separat SQLiteOpenHelper-klass