Giải pháp là đặt tham số kết nối JDBC noDatetimeStringSync=true
với useLegacyDatetimeCode=false
. Như một phần thưởng, tôi cũng tìm thấy sessionVariables=time_zone='-00:00'
giảm bớt sự cần thiết phải set time_zone
rõ ràng trên mọi kết nối mới.
Có một số mã chuyển đổi múi giờ "thông minh" được kích hoạt sâu bên trong ResultSet.getString()
khi phát hiện cột là TIMESTAMP
cột.
Than ôi, mã thông minh này có một lỗi:TimeUtil.fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart)
trả về Timestamp
được gắn thẻ sai vào múi giờ mặc định của JVM, ngay cả khi tz
tham số được đặt thành một cái gì đó khác:
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
trả về sẽ hoàn toàn hợp lệ ngoại trừ khi tiếp tục lên trong chuỗi cuộc gọi, nó được chuyển đổi trở lại thành chuỗi bằng cách sử dụng toString()
trần phương thức này hiển thị ts
dưới dạng Chuỗi đại diện cho đồng hồ sẽ hiển thị trong múi giờ mặc định của JVM, thay vì chuỗi biểu thị thời gian theo UTC. Trong 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();
Đặt noDatetimeStringSync=true
vô hiệu hóa toàn bộ mớ hỗn độn phân tích cú pháp / hủy phân tích cú pháp và chỉ trả về giá trị chuỗi như-được nhận từ cơ sở dữ liệu.
Đầu ra thử nghiệm:
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
vẫn quan trọng vì nó thay đổi hành vi của getDefaultTimeZone()
để sử dụng TZ của máy chủ cơ sở dữ liệu.
Trong khi tìm kiếm điều này, tôi cũng tìm thấy tài liệu cho useJDBCCompliantTimezoneShift
là không chính xác, mặc dù nó không có gì khác biệt:tài liệu cho biết [ Đây là một phần của mã ngày-giờ kế thừa, do đó thuộc tính chỉ có hiệu lực khi "useLegacyDatetimeCode =true". ], nhưng sai rồi, hãy xem ResultSetImpl.getNativeTimestampViaParseConversion(int, Calendar, TimeZone, boolean)
.