由于在时间处理上,北京时间和世界时间相差8小时,如果时间处理不当,会造成程序混乱。所以必需统一时间,建议使用UTC时间,当然使用北京时间也没毛病。
格林威治时间就是时间协调时间(GMT=UTC)
,格林威治时间和UTC时间均用秒数来计算的。本地时间=UTC + 时区差
例如:
北京时间:2019-03-22 12:20:00
UTC时间:2019-03-22 12:20:00 UTC+8:00
GMT时间:2019-03-22 4:20:00 UTC+0:00
参考官方文档
mysql中有4种日期类型
select now();
结果
+---------------------+
| now() |
+---------------------+
| 2019-03-22 10:21:50 |
+---------------------+
1 row in set
select now()+0;
结果
mysql> select now()+0;
+----------------+
| now()+0 |
+----------------+
| 20190322102206 |
+----------------+
1 row in set
sql语句当前时区时间,不能插入UTC时间(比如2019-03-22T02:00:00Z)。这个区别对开发者和用户是透明的,可以忽略
没太大区别,可以忽略这个区别
timestamp有自动初始化和更新,当你update某条记录的时候,该列值会自动更新,这是和datatime最大的区别。
其实2者区别不是特别的大,但timestamp比datetime常用。
查看时区
show variables like "%time_zone%";
返回
mysql> show variables like "%time_zone%";
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| system_time_zone | |
| time_zone | SYSTEM |
+------------------+--------+
2 rows in set
说明time_zone和system_time_zone使用system的时区,而在中国,时区基本上都是东八区,在linux上查看时区date -R
参考java8 LocalDate LocalDateTime等时间类介绍
下面的方法是获得当前时区时间
LocalDate localDate = LocalDate.now();
Period period = Period.between(localDate, localDate.plus(2, ChronoUnit.DAYS));
System.out.println(period.getDays());
使用Instant,这个类只有获得UTC时间。
//获得当前时间
Instant instant = Instant.now();
// 以ISO-8601输出
System.out.println(instant);
//结果:
2019-03-20T16:22:52.966Z
//解析时间
从字符串类型中创建Instant类型的时间
instant = Instant.parse("2019-03-20T16:22:52.966Z");
CURRENT_TIMESTAMP:获得当前时区时间,
UTC_TIMESTAMP:获得当前UTC时间
如果对时间不统一,会造成数据库里时间有的是UTC时间,有的是当前时区的时间。
在javaweb开发时我们经常遇到
Tomcat java使用UTC时区进行处理业务逻辑。从而导致Mysql数据库中lastModifyTime值查询后转到Java Bean,值少了8个小时。
修改方法有很多种
lastModityTime不使用Mysql自动更新方式,而由Java Bean赋值方式。这样是最合理的。
MySQL 类型 | java类型 |
---|---|
datetime | java.sql.Timestamp |
date | java.sql.Date 或java.time.LocalDate |
timestamp | java.sql.Timestamp或java.time. LocalDateTime |
time | java.sql.Time 或java.time.LocalTime |
通过org.apache.ibatis.type包下的类型解析器可得
在org.apache.ibatis.type包下默认设置很多种类型,用于映射这映射关系。
比如挺好感觉Intant的类映射InstantTypeHandler类,类将java.time.intant转换成java.sql.timestamp
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
@UsesJava8
public class InstantTypeHandler extends BaseTypeHandler {
//将Instant转换成Timestamp。注意将instant表示的UTC时间转换成了当前时区的时间
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Instant parameter, JdbcType jdbcType) throws SQLException {
ps.setTimestamp(i, Timestamp.from(parameter));
}
//获得的数据又转换成Instant时间类
private static Instant getInstant(Timestamp timestamp) {
if (timestamp != null) {
return timestamp.toInstant();
}
return null;
}
}
LocalDateTimeTypeHandler类
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import org.apache.ibatis.lang.UsesJava8;
/**
* @since 3.4.5
* @author Tomas Rohovsky
*/
@UsesJava8
public class LocalDateTimeTypeHandler extends BaseTypeHandler {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType)
throws SQLException {
ps.setTimestamp(i, Timestamp.valueOf(parameter));
}
@Override
public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
Timestamp timestamp = rs.getTimestamp(columnName);
return getLocalDateTime(timestamp);
}
@Override
public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
Timestamp timestamp = rs.getTimestamp(columnIndex);
return getLocalDateTime(timestamp);
}
@Override
public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
Timestamp timestamp = cs.getTimestamp(columnIndex);
return getLocalDateTime(timestamp);
}
private static LocalDateTime getLocalDateTime(Timestamp timestamp) {
if (timestamp != null) {
return timestamp.toLocalDateTime();
}
return null;
}
}
其它类省略
使用JDBC连接数据库里5.7版本使用com.mysql.cj.jdbc.Driver 驱动包。在连接连接时要统一时区,否则容易造成混乱。
下面是设置方法
//北京时间东八区
serverTimezone=GMT%2B8
//或者使用上海时间
serverTimezone=Asia/Shanghai
完整例子
UTC时间
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=true
北京时间:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&useSSL=true
以springboot框架开发javaweb项目为便,统一时间设置为UTC时间
字段指定了datetime或timestamp都一样,推荐使用timestamp
timestamp有自动初始化和更新,当你update某条记录的时候,该列值会自动更新,这是和datatime最大的区别。
下面的2种情况其实都会使用到,在混合使用的情况
mybatis的映射mapper文件的sql里使用UTC_TIMESTAMP代替CURRENT_TIMESTAMP。比如
insert into testTable ( create_a) values (UTC_TIMESTAMP )
在连接mysql数据库时统一时区为UTC
jdbc:mysql://127.0.0.1:3306/test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=true
有人说要统一服务器时间,配置如下
@SpringBootApplication
public class Application {
@PostConstruct
void started() {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
但我没有使用也可以,先记录下吧
经过上面2种处理后,数据库时间就统一为UTC时间了,当然,这里涉及了mybatis的typeHandler处理,还是要学习的。
参考SpringBoot 统一时区的方案