引言

近期团队的个别项目在进行框架升级后,部分时间值存在8小时误差,原因是错误的将数据库中的时间数据理解成了UTC时间(旧版本认为是北京时间)

考虑到未来项目对于时间理解的一致性,我决定将项目统一为使用UTC时间,经调研,形成本文

mysql数据库时区及时间时间类型说明

数据库时区

mysql数据库拥有时区设置,默认使用系统时区

可通过如下语句查询当前时区

show variables like '%time_zone%';

下图为我个人机器上mysql数据库时区设置:

项目线上数据库时区设置如下:

可见数据库使用系统时间CST——China Standard Time UTC+8:00 中国沿海时间(北京时间)

时间类型说明

datetime

实际格式储存(Just stores what you have stored and retrieves the same thing which you have stored.)

与时区无关(It has nothing to deal with the TIMEZONE and Conversion.)

timestamp

值以UTC毫秒数保存( it stores the number of milliseconds)

存储及检索时根据当前时区设置,对时间数值做转换

由于timestamp与时区相关,且线上数据库时区设置为北京时间(即UTC+8:00)。因此,当数据库中使用了timestamp列,若使用不当,统一UTC格式时间改造将很可能会引入错误! 后面详述理由

统一UTC时间改造方案简述

统一时区设定

项目新框架中通过UTCTimeZoneConfiguration类型,在项目初始化时设置当前进程的默认时区

@Configurationpublic class UTCTimeZoneConfiguration implementsServletContextListener{public voidcontextInitialized(ServletContextEvent event) {

System.setProperty("user.timezone", "UTC");

TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

}public voidcontextDestroyed(ServletContextEvent event) {}

}

时间类型Joda DateTime的使用方式

日期时间类型可以使用 java.util.Date,但推荐使用更为方便的joda DateTime,本节介绍joda DateTime 序列化/反序列化使用方式

Joda DateTime 类型用于定义接口输入输出参数,需进行序列化/反序列化操作。与原生的Date类型不同,DateTime需要做一点额外处理

1、Model类型的日期字段使用类型DateTime替代Date

实例代码如下

public classEntity {

@JsonSerialize(using= UTCDateTimeSerializer.class)

@JsonDeserialize(using= UTCDateTimeDeserializer.class)privateDateTime dateTime;publicDateTime getDateTime() {returndateTime;

}public voidsetDateTime(DateTime dateTime) {this.dateTime =dateTime;

}

}

其中UTCDateTimeSerializer与UTCDateTimeDeserializer类的实现见附录

2、Get请求接受时间参数

此时,一种有效的处理方式是使用字符串接受日期参数,如下:

@RequestMapping(value = "/xxx", method =RequestMethod.GET)public CommonResponse getXxx(@RequestParam(value = "beginTime") String beginTimeText,

@RequestParam(value= "endTime") String endTimeText) {

DateTime beginTime=DateTime.parse(beginTimeText).withZone(DateTimeZone.UTC);

DateTime endTime=DateTime.parse(endTimeText).withZone(DateTimeZone.UTC);

...

}

Dao时间操作——针对数据库列为datetime的场景

以Joda DateTime类型举例说明使用方法,某Dao类型中存在的两个方法如下:

public void update(intid, DateTime dateTime) {

String sql= "UPDATE " + TABLE_NAME + " SET datetime = ? WHERE id = ?";

jdbcTemplate.update(sql,newTimestamp(dateTime.getMillis()), id);

}public DateTime getDateTime(intid) {

String sql= "SELECT datetime FROM " + TABLE_NAME + " WHERE id = ?";

List dateTimeList = jdbcTemplate.query(sql, new Object[] {id}, new RowMapper() {

@Overridepublic DateTime mapRow(ResultSet rs, int rowNum) throwsSQLException {return new DateTime(rs.getTimestamp("datetime").getTime());

}

});return dateTimeList.size() > 0 ? dateTimeList.get(0) : null;

}

插入或更新数据,传递的时间参数请使用 new Timestamp(dateTime.getMillis())

读取时间参数,使用new DateTime(rs.getTimestamp("datetime").getTime())

Dao时间操作——针对数据库列为timestamp的场景

数据库timestamp类型适合用来记录数据的最后修改时间

其他场景建议使用datetime或者int

方案一更改会话时区为UTC时间

对timestamp列的操作与datetime列的操作不做区分,此时需要设置数据连接会话的时区,默认为北京时间,需要设置为UTC时间,通过如下语句设置

set time_zone = '+0:00';

实际项目中使用数据库连接池,创建datasource后使用如下方式设置时区,将对所有连接生效

dataSource.setInitSQL("set time_zone = '+0:00'");

经此操作后,时区统一为UTC时间,Dao中时间操作,无需对timestamp做特殊处理

方案二不更改会话时区

由于不更改时区,timestamp类型数据的使用存在一定限制

1、如何更新timestamp数据

对于数据库表中的timestamp列,其值的更新应当由数据库自行维护,在create table时设置,如下:

CREATE TABLEt1 (

tsTIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);

可简写如下

CREATE TABLEt1 (

tsTIMESTAMP);

不允许程序自主更新timstamp列数据

线上数据库时区为北京时间,其接受到的日期数据被视为北京时间,而上层程序业务逻辑统一使用UTC时间,时区不统一。因此避免数据库记录的日期数据理解不一致,不允许程序通过写操作sql语句更新timestamp列

下图数据为本人实测数据,timestamp列由程序进行更新,update_time列则由数据库自动更新

前者显示的是UTC时间,看似合理,实则错误,数据库内部存储时间为UTC-8:00

update_time符合数据库时区设置,返回北京时间,内部实际存储UTC时间

2、如何读取timestamp数据

为避免从数据库中获取时区相关时间(北京时间),强制使用UTC时间,使用函数UNIX_TIMESTAMP获取1970年至今秒数,转换成DateTime时乘以1000转变为毫秒

public DateTime getTimestamp(intid) {

String sql= "SELECT UNIX_TIMESTAMP(update_time) as unix_timestamp FROM " + TABLE_NAME + " WHERE id = ?";

List dateTimeList = jdbcTemplate.query(sql, new Object[] {id}, new RowMapper() {

@Overridepublic DateTime mapRow(ResultSet rs, int rowNum) throwsSQLException {return new DateTime(rs.getLong("unix_timestamp") * 1000);

}

});return dateTimeList.size() > 0 ? dateTimeList.get(0) : null;

}

附录

Mysql时区设置

设置全局时区,需要管理员权限

使用本机系统时区

SET GLOBAL time_zone = SYSTEM;

使用UTC时间

SET GLOBAL time_zone = '+0:00';

使用北京时间

SET GLOBAL time_zone = '+8:00';

设置当前连接会话时区

set time_zone = '+0:00';

UTCDateTimeSerializer与UTCDateTimeDeserializer

UTCDateTimeSerializer 完成DateTime对象到UTC时间字符串的转换,格式为:yyyy-MM-ddTHH:mm:ssZ

UTCDateTimeDeserializer 完成时间字符串到DateTime对象的转换,转换为UTC时区

具体实现如下:

public class UTCDateTimeSerializer extends JsonSerializer{

@Overridepublic voidserialize(DateTime dateTime,

JsonGenerator jsonGenerator,

SerializerProvider provider)throwsIOException {

String dateTimeAsString=dateTime.withZone(DateTimeZone.UTC).toString(BecConstant.DATETIME_FORMAT);

jsonGenerator.writeString(dateTimeAsString);

}

}public class UTCDateTimeDeserializer extends JsonDeserializer{

@OverridepublicDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)throwsIOException {

JsonToken currentToken=jsonParser.getCurrentToken();if (currentToken ==JsonToken.VALUE_STRING) {

String dateTimeAsString=jsonParser.getText().trim();returnDateTime.parse(dateTimeAsString).withZone(DateTimeZone.UTC);

}return null;

}

}

java mysql utc时间_Java项目统一UTC时间方案相关推荐

  1. java mysql重连_java mysql

    关于 java mysql的搜索结果 问题 连接mysql错误,Druid-ConnectionPool-Create-1641320886 16:52:01.163 [Druid-Connectio ...

  2. java后台怎么获取系统时间_Java 后台获取当前时间

    Calendar c = Calendar.getInstance();//可以对每个时间域单独修改 int year = c.get(Calendar.YEAR); int month = c.ge ...

  3. java格林威治时间_JAVA 花样化格林威治时间(Wed Aug 01 00:00:00 CST 2012)花样转换...

    1.若是格林威治时间时间是date类型.(这种花样最简朴) SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); Date ...

  4. maven项目统一管理版本号方案

    共有两种方案: 第一种亲测,靠谱. 方法一.引用版本管理插件 项目的结果如图: 模拟其他微服务目录一致的test2项目 两个项目的pom文件依赖关系: test1的parent.pom继承test1最 ...

  5. java 字符串转utc时间_java - 如何转换UTC日期字符串并删除Java中的T和Z? - 堆栈内存溢出...

    TL;博士 Instant.parse( "2018-05-23T23:18:31.000Z" ) // Parse this String in standard ISO 860 ...

  6. java 微秒 时间_Java中的当前时间(以微秒为单位)

    Java 9和更高版本:捕获当前时刻时,分辨率高达纳秒.那是9位数的小数. Instant.now() 2017-12-23T12:34:56.123456789Z 要限制为微秒,请截断. Insta ...

  7. java日志怎么实现_JAVA项目中怎么实现一个通用日志记录功能

    JAVA项目中怎么实现一个通用日志记录功能 发布时间:2020-11-21 17:04:50 来源:亿速云 阅读:53 作者:Leah 今天就跟大家聊聊有关JAVA项目中怎么实现一个通用日志记录功能, ...

  8. java mysql 插入 乱码_java插入mysql乱码

    java插入mysql乱码 java插入数据到mysql分为三层: ● 前端页面 ● 后台代码 ● 数据库 这三层任意一层乱码都不行,所以我们需要对着三层逐一设置编码格式,保存编码统一就不会乱码了. ...

  9. java redis缓存理解_Java项目中使用Redis缓存案例

    缓存的目的是为了提高系统的性能,缓存中的数据主要有两种: 1.热点数据.我们将经常访问到的数据放在缓存中,降低数据库I/O,同时因为缓存的数据的高速查询,加快整个系统的响应速度,也在一定程度上提高并发 ...

  10. java mysql自动备份_java定时备份数据之二_MySQL

    以mysql为例: BackupDb.java数据库备份类: public class BackupDb { public static boolean sqlDump(String cmd,Stri ...

最新文章

  1. 根据卡号获取银行卡名字
  2. PostgreSQL — 常规操作
  3. golang 字符串分割
  4. 解决Ubuntu Adobe Reader 菜单栏空白
  5. EOS 智能合约源代码解读 (3)asset.hpp
  6. 释疑の语法Unpackpack
  7. redis watch使用场景_redis不得不会的事务玩法
  8. uniapp滑动切换tab标签_Web前端,Tab切换,缓存,页面处理的几种方式
  9. matlab 变调器,关于变声变调的matlab设计 比较详细
  10. Retrofit请求数据对错误以及网络异常的处理
  11. 有凤来仪的意思是什么?成语有凤来仪比喻什么?
  12. android js交互 数组,Android WebView —— Java 与 JavaScript 交互总结
  13. Redis 单数据多源超高并发下的解决方案
  14. java mq5.15,ActiveMQ 5.15.x Release安装和配置--Linux篇
  15. Qt QMake详解
  16. 微信小程序中播放视频 例子
  17. 外贸人寄样品时,怎么样让客户承担快递费用
  18. 怎样在线分解gif图片?如何将gif拆分为静态图片?
  19. python jinja2_Python Jinja2使用方法
  20. No qualifying bean of type报错问题处理

热门文章

  1. qt 表格中插入一行_Qt在表格中加入控件
  2. excel做ns流程图_NS流程图是什么图?用这款软件轻松画NS流程图
  3. signature=af972d07a7b1e8cd17a3d011d31aa690,a00836.html
  4. 常说的七侠五义到底是什么?
  5. MacBook邮件登陆163邮箱,解决无法验证账户名或密码的问题
  6. python 相关性检验怎么计算p值_数据分析---用Python进行相关性分析(兼谈假设检验)...
  7. 量子计算机原理 纠缠,白话量子计算机原理【前面的那个有错误,重新理清了一下思路】...
  8. 手机二维码应用潜力无限
  9. 参加神州英才执行力培训课程感悟
  10. mysql填写数据库_学习MYSQL过程中自己写的数据库操作