根據業務要求,不同的國家設置jvm參數,來確定當前時區。
// -Duser.timezone=Asia/Kolkata 印度加爾各答 GMT+05:30
// -Duser.timezone=Asia/Bangkok 泰國曼谷 GMT+07:00
// -Duser.timezone=Asia/Shangha 中國上海 GMT+08:00
由於各個國家上線項目不完全相同,部分功能在不同的項目有不同實現方式。數據同步時,發生了跨時區同步數據的情況。
例如:
在某個國家部署A服務,在當地時間執行同步任務,可以同步到正確的業務數據,其中業務數據類型使用java.util.Date。
另一個國家部署B服務,同樣業務數據類型使用java.util.Date,在當地時間執行同步任務,不可以同步到正確的業務數據,只好改寫爲java.lang.String類型。
調查原因,發現B服務的jdbc的jar包版本太低,不支持自動時區轉換,升級版本爲6.0.3,可以正確同步數據。
- JdbcTimestampValueFactory
public Timestamp createFromTimestamp(int year, int month, int day, int hours, int minutes, int seconds, int nanos) {
if (year == 0 && month == 0 && day == 0) {
throw new DataReadException(Messages.getString("ResultSet.InvalidZeroDate"));
} else {
synchronized(this.cal) {
this.cal.set(year, month - 1, day, hours, minutes, seconds);
Timestamp ts = new Timestamp(this.cal.getTimeInMillis());
ts.setNanos(nanos);
return ts;
}
}
}
但同時發現A服務不需要指定jdbc.url中的serverTimezone=Asia/Shanghai。
跟蹤代碼MysqlaSession.configureTimezone(),在不指定serverTimezone時,跟蹤獲取數據庫服務器的時區的代碼,獲取的確實爲CST,但在將轉換爲對應時區時,A服務將CST轉爲了Asia/Shanghai,但B服務仍然爲CST=Asia/Kolkata。
- MysqlaSession
public void configureTimezone() {
String configuredTimeZoneOnServer = this.getServerVariable("time_zone");
if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
configuredTimeZoneOnServer = this.getServerVariable("system_time_zone");
}
String canonicalTimezone = (String)this.getPropertySet().getStringReadableProperty("serverTimezone").getValue();
if (configuredTimeZoneOnServer != null && (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone))) {
try {
canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, this.getExceptionInterceptor());
} catch (IllegalArgumentException var4) {
throw (WrongArgumentException)ExceptionFactory.createException(WrongArgumentException.class, var4.getMessage(), this.getExceptionInterceptor());
}
}
if (canonicalTimezone != null && canonicalTimezone.length() > 0) {
this.serverTimezoneTZ = TimeZone.getTimeZone(canonicalTimezone);
if (!canonicalTimezone.equalsIgnoreCase("GMT") && this.serverTimezoneTZ.getID().equals("GMT")) {
throw (WrongArgumentException)ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("Connection.9", new Object[]{canonicalTimezone}), this.getExceptionInterceptor());
}
}
this.defaultTimeZone = this.serverTimezoneTZ;
}
繼續跟進轉換代碼,在com.mysql.cj.jdbc.util.TimeUtil#loadTimeZoneMappings()中,會加載/com/mysql/cj/jdbc/util/TimeZoneMapping.properties文件,在A服務的資源文件中,有個TimeZoneMapping.properties,並且包含CST=Asia/Shanghai
。Git代碼提交記錄備註爲CST TimeZone point to Asia/Shanghai
。
- TimeUtil
private static void loadTimeZoneMappings(ExceptionInterceptor exceptionInterceptor) {
timeZoneMappings = new Properties();
try {
timeZoneMappings.load(TimeUtil.class.getResourceAsStream("/com/mysql/cj/jdbc/util/TimeZoneMapping.properties"));
} catch (IOException var5) {
throw ExceptionFactory.createException(Messages.getString("TimeUtil.LoadTimeZoneMappingError"), exceptionInterceptor);
}
String[] var1 = TimeZone.getAvailableIDs();
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String tz = var1[var3];
if (!timeZoneMappings.containsKey(tz)) {
timeZoneMappings.put(tz, tz);
}
}
}
``
重要參考鏈接:
- https://www.cnblogs.com/sogeisetsu/p/12324161.html
- https://blog.csdn.net/weixin_37015554/article/details/105198428
- https://blog.csdn.net/weixin_41917635/article/details/103719481