Java 程式讀取Mysql資料庫時間資訊與真實時間相差 13、14 小時、SQLException: HOUR_OF_DAY: 2 -> 3
CST時區引起的異常:
Java 程式讀取Mysql資料庫時間資訊,與真實時間相差 13、14 小時
java.sql.SQLException: HOUR_OF_DAY: 2 -> 3
原因:
Mysql 驅動:mysql-connector-java 升級到8版本後。將資料庫時間解析到java時間,需要獲取資料庫的時區。
java如何資料庫時區:
1、資料庫連線中指明的時區,就用該時區,優先順序最高。datasource.urljdbc:mysql://127.0.0.1:3306/yourDB?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8。
2、通過語句查詢資料時區 select @@time_zone,如果返回SYSTEM,資料庫沒有配置時區,使用系統時區 select @@system_time_zone。
mysql> select @@time_zone;
+-------------+
| @@time_zone |
+-------------+
| SYSTEM |
+-------------+
mysql> select @@system_time_zone;
+--------------------+
| @@system_time_zone |
+--------------------+
| CST |
+--------------------+
如果java獲取資料庫的時區是CST,就會出現以上的問題。
CST是什麼時區?
CST時間有四種解釋:
美國中部時間 Central Standard Time (USA) UTC-06:00/ UTC-05:00
澳大利亞中部時間 Central Standard Time (Australia) UTC+09:30
中國標準時 China Standard Time UTC+08:00
古巴標準時 Cuba Standard Time UTC-04:00
java是美國人的,當然認為CST是美國中部時間。
夏令時:
由於美國有夏令時,CST非夏令時對應 UTC-06:00,夏令時對應 UTC-05:00 。
美國的夏令時,從每年3月第2個星期天凌晨開始,到每年11月第1個星期天凌晨結束。
以2020年為例:
夏令時開始時間調整前:2020年03月08日星期日 02:00:00,時間向前撥一小時.
調整後:2020年03月08日星期日 03:00:00
夏令時結束時間調整前:2020年11月01日星期日 02:00:00,時間往回撥一小時.
調整後:2020年11月01日星期日 01:00:00
這意味這:CST沒有2020-03-08 02:00:00~2020-03-08 03:00:00 這個區間的時間。會有兩個 2020-11-01 01:00:00~2020-11-01 02:00:00區間的時間。
例:
//相差14小時
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CST"), Locale.US);
calendar.setLenient(false);
//2020-03-08 01:02:00
calendar.set(2020, 2, 8, 1, 2, 0);
Date s = new Date(calendar.getTimeInMillis());
SimpleDateFormat f1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(f1.format(s));//2020-03-08 15:02:00
//2020-03-08 01:02:00 非夏令時間,於北京時間相差14小時。
例:
//相差13小時
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CST"), Locale.US);
calendar.setLenient(false);
//2020-03-08 03:02:00
calendar.set(2020, 2, 8, 3, 2, 0);
Date s = new Date(calendar.getTimeInMillis());
SimpleDateFormat f1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(f1.format(s));//2020-03-08 16:02:00
//2020-03-08 03:02:00 夏令時間,於北京時間相差13小時。
例:
//丟擲 Exception: HOUR_OF_DAY: 2 -> 3
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CST"), Locale.US);
calendar.setLenient(false);//嚴格資料校驗
//2020-03-08 02:02:00
calendar.set(2020, 2, 8, 2, 2, 0);
Date s = new Date(calendar.getTimeInMillis());//丟擲異常:java.lang.IllegalArgumentException: HOUR_OF_DAY: 2 -> 3
SimpleDateFormat f1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(f1.format(s));
//2020-03-08 02:02:00 在CST 時區中是一個不存在的時間,因此出現了異常。
解決上面的問題3種方式:
1、修改資料時區。
2、降低mysql 驅動 mysql-connector-java的版本。
3、訪問資料庫連線上加上資料時區。這種方式最好。
mysql-connector-java 原始碼中是如何操作的
public class ConnectionImpl implements JdbcConnection, SessionEventListener, Serializable {
private NativeSession session = null;
protected ResultSetFactory nullStatementResultSetFactory;
public ConnectionImpl(HostInfo hostInfo) throws SQLException {
……
try {
……
//建立ResultSetFactory
this.nullStatementResultSetFactory = new ResultSetFactory(this, null);
//建立session
this.session = new NativeSession(hostInfo, this.propertySet);
……
} catch (CJException e1) {
throw SQLExceptionsMapping.translateException(e1, getExceptionInterceptor());
}
try {
// createNewIO方法 -> connectOneTryOnly方法 -> initializePropsFromServer方法
createNewIO(false);
} catch (SQLException ex) {
}
……
}
private void initializePropsFromServer() throws SQLException {
……
//配置session ,這裡就有獲取資料庫時區的操作
this.session.getProtocol().initServerSession();
……
}
}
public class NativeProtocol extends AbstractProtocol<NativePacketPayload> implements Protocol<NativePacketPayload>, RuntimePropertyListener {
@Override
public void initServerSession() {
//配置時區
configureTimezone();
}
/**
* 配置時區
*/
public void configureTimezone() {
//獲取資料庫時區
String configuredTimeZoneOnServer = this.serverSession.getServerVariable("time_zone");
//獲取資料庫系統時區
if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) {
configuredTimeZoneOnServer = this.serverSession.getServerVariable("system_time_zone");
}
//獲取訪問資料連線中配置的時區
String canonicalTimezone = getPropertySet().getStringProperty(PropertyKey.serverTimezone).getValue();
if (configuredTimeZoneOnServer != null) {
// user can override this with driver properties, so don't detect if that's the case
if (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone)) {
try {
canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, getExceptionInterceptor());
} catch (IllegalArgumentException iae) {
throw ExceptionFactory.createException(WrongArgumentException.class, iae.getMessage(), getExceptionInterceptor());
}
}
}
if (canonicalTimezone != null && canonicalTimezone.length() > 0) {
this.serverSession.setServerTimeZone(TimeZone.getTimeZone(canonicalTimezone));
//
// The Calendar class has the behavior of mapping unknown timezones to 'GMT' instead of throwing an exception, so we must check for this...
//
if (!canonicalTimezone.equalsIgnoreCase("GMT") && this.serverSession.getServerTimeZone().getID().equals("GMT")) {
throw ExceptionFactory.createException(WrongArgumentException.class, Messages.getString("Connection.9", new Object[] { canonicalTimezone }),
getExceptionInterceptor());
}
}
this.serverSession.setDefaultTimeZone(this.serverSession.getServerTimeZone());
}
}
在建立Connect時同樣也建立了ResultSetFactory ,該Factory會建立ResultSetImpl ,ResultSetImpl用於解析資料庫返回的結果集。
public class ResultSetFactory implements ProtocolEntityFactory<ResultSetImpl, NativePacketPayload> {
private JdbcConnection conn;
private StatementImpl stmt;
public ResultSetFactory(JdbcConnection connection, StatementImpl creatorStmt) throws SQLException {
this.conn = connection;
this.stmt = creatorStmt;
}
/**
* 建立ResultSetImpl
*/
public ResultSetImpl createFromResultsetRows(int resultSetConcurrency, int resultSetType, ResultsetRows rows) throws SQLException {
ResultSetImpl rs;
StatementImpl st = this.stmt;
if (rows.getOwner() != null) {
st = ((ResultSetImpl) rows.getOwner()).getOwningStatement();
}
switch (resultSetConcurrency) {
case java.sql.ResultSet.CONCUR_UPDATABLE:
rs = new UpdatableResultSet(rows, this.conn, st);
break;
default:
// CONCUR_READ_ONLY
rs = new ResultSetImpl(rows, this.conn, st);
break;
}
return rs;
}
}
public class ResultSetImpl extends NativeResultset implements ResultSetInternalMethods, WarningListener {
protected volatile JdbcConnection connection;
protected NativeSession session = null;
public ResultSetImpl(ResultsetRows tuples, JdbcConnection conn, StatementImpl creatorStmt) throws SQLException {
this.connection = conn;
this.session = (NativeSession) conn.getSession();
……
this.floatValueFactory = new FloatValueFactory(pset);
this.doubleValueFactory = new DoubleValueFactory(pset);
this.bigDecimalValueFactory = new BigDecimalValueFactory(pset);
this.binaryStreamValueFactory = new BinaryStreamValueFactory(pset);
this.defaultDateValueFactory = new SqlDateValueFactory(pset, null, this.session.getServerSession().getDefaultTimeZone(), this);
this.defaultTimeValueFactory = new SqlTimeValueFactory(pset, null, this.session.getServerSession().getDefaultTimeZone(), this);
//這裡會將session中的時區傳入
this.defaultTimestampValueFactory = new SqlTimestampValueFactory(pset, null, this.session.getServerSession().getDefaultTimeZone());
this.defaultLocalDateValueFactory = new LocalDateValueFactory(pset, this);
this.defaultLocalTimeValueFactory = new LocalTimeValueFactory(pset, this);
……
}
}
public class SqlTimestampValueFactory extends AbstractDateTimeValueFactory<Timestamp> {
private Calendar cal;
public SqlTimestampValueFactory(PropertySet pset, Calendar calendar, TimeZone tz) {
super(pset);
if (calendar != null) {
this.cal = (Calendar) calendar.clone();
} else {
this.cal = Calendar.getInstance(tz, Locale.US);
this.cal.setLenient(false);
@Override
public Timestamp localCreateFromTimestamp(InternalTimestamp its) {
synchronized (this.cal) {
try {
this.cal.set(its.getYear(), its.getMonth() - 1, its.getDay(), its.getHours(), its.getMinutes(), its.getSeconds());
Timestamp ts = new Timestamp(this.cal.getTimeInMillis());
ts.setNanos(its.getNanos());
return ts;
} catch (IllegalArgumentException e) {
}
}
}
}
相關文章
- ORACLE計算2個時間段相差時間小時、分、秒Oracle
- beego orm 時間相差八小時GoORM
- 時區問題導致時間相差8個小時
- Java 獲取本地時間與網路時間Java
- js獲取指定時間日期和當前時間日期的相差多少時間JS
- 帝國cms為什麼釋出時間比實際時間相差8個小時?
- 時間相差秒數_Golang 時間操作大全Golang
- 時間序列資料的定義,讀取與指數平滑(Java)Java
- mysql資料庫時間戳函式MySql資料庫時間戳函式
- MySQL時間戳、時間MySql時間戳
- System.Data.SqlClient.SqlException 超時時間已到SQLclientException
- js獲取兩個時間相差的天數JS
- Laravel 獲取 13 位時間戳Laravel時間戳
- MySql與Java的時間型別MySqlJava型別
- 模型取到的資料庫時間created_at慢8小時模型資料庫
- mysql時區與時間函式MySql函式
- mysql將時間戳轉成常用可讀時間格式MySql時間戳
- 遷移Oracle資料庫時如何減小停機時間AAOracle資料庫
- 【曹工雜談】Mysql-Connector-Java時區問題的一點理解--寫入資料庫的時間總是晚13小時問題MySqlJava資料庫
- 實時獲取當前時間程式碼例項
- 實時資料庫與時序資料庫資料庫
- mysql時間操作(時間差和時間戳和時間字串的互轉)MySql時間戳字串
- MySQL 時間戳的 獲取 & 轉換為特定時間格式MySql時間戳
- mysql 時間MySql
- 獲取資料庫表的資訊(大小,索引大小,建立時間,行數)資料庫索引
- 查詢時若時間為空,開始時間取今天的零點,結束時間取當前時間
- MySQL按時間統計資料MySql
- 一次 JDBC 與 MySQL 因 “CST” 時區協商誤解導致時間差了 14 或 13 小時的排錯經歷JDBCMySql
- java獲取當前時間Java
- java獲取日期和時間Java
- 【AWR】調整AWR資料取樣時間間隔及歷史快照保留時間
- 【MySQL資料型別2之--日期時間型別】MySql資料型別
- 實用小工具——快速獲取資料庫時間寫法資料庫
- oracle 取時間Oracle
- 解決java獲取系統時間差8個小時 專題Java
- mysql 時間型別秒級資料,取分鐘資料方案MySql型別
- PostgreSQL 插入時間與更新時間(qbit)SQL
- c++ 獲取當前時間周初凌晨時間戳(獲取當前時間週一凌晨時間戳)C++時間戳