解決策は、JDBC接続パラメーターnoDatetimeStringSync=true
を設定することです。 useLegacyDatetimeCode=false
を使用 。ボーナスとして、sessionVariables=time_zone='-00:00'
も見つかりました set time_zone
を設定する必要性を軽減します 新しい接続ごとに明示的に。
ResultSet.getString()
の奥深くでアクティブ化される「インテリジェントな」タイムゾーン変換コードがいくつかあります。 列がTIMESTAMP
であることを検出したときのメソッド 列。
残念ながら、このインテリジェントコードにはバグがあります:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart)
Timestamp
を返します tz
の場合でも、JVMのデフォルトのタイムゾーンに誤ってタグ付けされている パラメータが別のものに設定されている:
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;
}
リターンts
呼び出しチェーンのさらに上位にある場合を除いて、完全に有効です。裸のtoString()
を使用して文字列に変換されます。 ts
をレンダリングするメソッド UTCでの時刻の文字列表現ではなく、JVMのデフォルトのタイムゾーンで時計が表示する内容を表す文字列として。 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();
noDatetimeStringSync=true
を設定する 解析/解析解除の混乱全体を無効にし、データベースから受け取ったままの文字列値を返します。
テスト出力:
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
getDefaultTimeZone()
の動作を変更するため、これは依然として重要です。 データベースサーバーのTZを使用します。
これを追いかけていると、useJDBCCompliantTimezoneShift
のドキュメントも見つかりました。 違いはありませんが、正しくありません。ドキュメントには[これは従来の日時コードの一部であるため、プロパティは「useLegacyDatetimeCode=true」の場合にのみ有効です。 ]、しかしそれは間違っています。ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean)
を参照してください。 。