mysql-connector-java版本升级出现的一次问题。涉及到了时间精度的截取和四舍五入。

首先了解一点,timestamp,datetime如果不指定精度,默认的精度是秒。

当mysql-connector-java版本<=5.1.22时,db的客户端会将Datetime,Timestamp秒以下的精度丢弃。版本>5.1.22后,秒以下的值将不会截断

db的server端会对超出精度位数的数据进行四舍五入!!

举个例子:在db建表时没指定精度时,插入精确到毫秒级别的日期

如果使用mysql-connector-java版本<=5.1.22,在客户端用'2018-04-02 23:59:59.999'插入日期,精度会在客户端被截取到秒,插入db里是'2018-04-02 23:59:59'

如果升级版本,在db的客户端用'2018-04-02 23:59:59.999'插入日期,精度在客户端不会被截断,db的server端会对超出精度位数的数据进行四舍五入,即插入db里是'2018-04-03 00:00:00 '

所以说mysql-connector-java版本升级就带了时间与原本不一致的问题,结合具体业务逻辑上的使用,可能会造成不同大小的影响。

要想证实这个观点,可以分两步:

  • server端是否会四舍五入
  • 客户端代码不同版本对精度是否有不同的处理方式

来实际测一下server会不会四舍五入:

CREATE TABLE `time_test` (
`id` int(11) NOT NULL AUTO_INCREMENT ,
`create_time` timestamp NOT NULL DEFAULT '1971-01-01 00:00:00' ,
`end_time` timestamp NOT NULL DEFAULT '1971-01-01 00:00:00' ,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; insert into time_test (create_time,end_time) values('2018-04-02 23:59:59','2018-04-02 23:59:59.999'); select * from time_test;

  

看一下记录:
+----+---------------------+---------------------+
| id | create_time         | end_time            |
+----+---------------------+---------------------+
|  2  2018-04-02 23:59:59    2018-04-03 00:00:00   |
+----+---------------------+---------------------+
 
 

可以看出db的server端果然会进行四舍五入。

再看一下mysql驱动里是怎么写的,是否真的是截断精度了。

Mysql对于时间精度的处理在com.mysql.jdbc.PreparedStatement#setTimestampInternal这个方法中

翻一下5.1.21的源码看一下:

private void setTimestampInternal(int parameterIndex,
Timestamp x, Calendar targetCalendar,
TimeZone tz, boolean rollForward) throws SQLException {
synchronized (checkClosed()) {
if (x == null) {
setNull(parameterIndex, java.sql.Types.TIMESTAMP);
} else {
checkClosed(); if (!this.useLegacyDatetimeCode) {
newSetTimestampInternal(parameterIndex, x, targetCalendar);
} else {
String timestampString = null; Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
this.connection.getUtcCalendar() :
getCalendarInstanceForSessionOrNew(); synchronized (sessionCalendar) {
x = TimeUtil.changeTimezone(this.connection,
sessionCalendar,
targetCalendar,
x, tz, this.connection
.getServerTimezoneTZ(), rollForward);
} if (this.connection.getUseSSPSCompatibleTimezoneShift()) {
doSSPSCompatibleTimezoneShift(parameterIndex, x, sessionCalendar);
} else {
synchronized (this) {
if (this.tsdf == null) {
//这里,截断秒以下的精度
this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''", Locale.US); //$NON-NLS-1$
} timestampString = this.tsdf.format(x); //这里永远不会执行添加秒以下的精度
if (false) { // not so long as Bug#50774 is around
StringBuffer buf = new StringBuffer();
buf.append(timestampString);
int nanos = x.getNanos(); if (nanos != 0) {
buf.append('.');
buf.append(formatNanos(nanos));
} buf.append('\'');
} setInternal(parameterIndex, timestampString); // SimpleDateFormat is not
// thread-safe
}
}
} this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TIMESTAMP;
}
}
}

  

再看下5.1.32的实现:

private void setTimestampInternal(int parameterIndex,
Timestamp x, Calendar targetCalendar,
TimeZone tz, boolean rollForward) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
if (x == null) {
setNull(parameterIndex, java.sql.Types.TIMESTAMP);
} else {
checkClosed(); if (!this.useLegacyDatetimeCode) {
newSetTimestampInternal(parameterIndex, x, targetCalendar);
} else {
Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
this.connection.getUtcCalendar() :
getCalendarInstanceForSessionOrNew(); synchronized (sessionCalendar) {
x = TimeUtil.changeTimezone(this.connection,
sessionCalendar,
targetCalendar,
x, tz, this.connection
.getServerTimezoneTZ(), rollForward);
} if (this.connection.getUseSSPSCompatibleTimezoneShift()) {
doSSPSCompatibleTimezoneShift(parameterIndex, x, sessionCalendar);
} else {
synchronized (this) {
//同样截断精度
if (this.tsdf == null) {
this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss", Locale.US); //$NON-NLS-1$
} StringBuffer buf = new StringBuffer();
buf.append(this.tsdf.format(x)); //这里,如果server支持fractional seconds的话,就加上毫秒的精度
if (this.serverSupportsFracSecs) {
int nanos = x.getNanos(); if (nanos != 0) {
buf.append('.');
buf.append(TimeUtil.formatNanos(nanos, this.serverSupportsFracSecs, true));
}
} buf.append('\''); setInternal(parameterIndex, buf.toString()); // SimpleDateFormat is not
// thread-safe
}
}
} this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = Types.TIMESTAMP;
}
}
}

  

看来果然是个bug...看一下mysql官网的描述:参见bugFix第三条

Mysql 时间类型精度截取的bug的更多相关文章

  1. MySQL日期数据类型、MySQL时间类型使用总结

    MySQL:MySQL日期数据类型.MySQL时间类型使用总结 MySQL 日期类型:日期格式.所占存储空间.日期范围 比较. 日期类型 存储空间 日期格式 日期范围 ------------ --- ...

  2. mysql 时间类型分类

    MySQL:MySQL日期数据类型.MySQL时间类型使用总结 MySQL 日期类型:日期格式.所占存储空间.日期范围 比较. 日期类型 存储空间 日期格式 日期范围------------ ---- ...

  3. mysql 时间类型datetime与timestamp区别比较

    mysql 时间类型datetime与timestamp区别比较 相同点: 显示宽度和格式相同,显示宽度固定在19字符,格式为YYYY-MM-DD HH:MM:SS. 不同点: (1)时间范围不同: ...

  4. MySQL 时间类型字段的分析

    日期类型                存储空间               日期格式                                           日期范围---------- ...

  5. MySQL:MySQL日期数据类型、MySQL时间类型使用总结

    MySQL 日期类型:日期格式.所占存储空间.日期范围 比较. 日期类型        存储空间      日期格式                日期范围------------  -------- ...

  6. mysql时间类型和格式转换

    内容目录 简介mysql时间类型DATE_FORMAT()函数 简介 今天开发中,做一个功能需要对历史数据进行补充,相信大家也遇到过这样的情况,这个历史数据需要按月份和人的id进行区分,于是想到了my ...

  7. Mysql时间类型处理

    关于Mysql中时间的处理 最近在读<人类简史>,读第二遍.只有晚上睡觉之前读一点点,有时候觉得一天可以抽出一个专门的时间来看书了,效率应该能高不少. 另外分享个网址可以随心创作 这里有一 ...

  8. mysql 时间类型精确到毫秒、微秒及其处理

    一.MySQL 获得毫秒.微秒及对毫秒.微秒的处理 MySQL 较新的版本中(MySQL 6.0.5),也还没有产生微秒的函数,now() 只能精确到秒. MySQL 中也没有存储带有毫秒.微秒的日期 ...

  9. MySQL时间类型及获取、展示处理

    MySQL时间格式 mysql所支持的日期时间类型有:DATETIME. TIMESTAMP.DATE.TIME.YEAR. 几种类型比较如下: 日期时间类型 占用空间 日期格式 最小值 最大值 零值 ...

随机推荐

  1. 在SELECT DISTINCT 状况下使用 Order BY Newid() 随机数选出记录

    在日常作业中,有时候可能是一些活动要抽出得奖人或选出抽查的一些名单, 就常常会使用到 Order BY Newid() 的方式来做随机数选出, 但有可能的状况需是要搭配到 DISTINCT 来选出,这 ...

  2. git切换分支(自记)

    git fetch git checkout feature/A4-page

  3. <转>Python: and、or以及and-or

    转自 http://www.cnblogs.com/BeginMan/p/3197123.html 一.and: 在Python 中,and 和 or 执行布尔逻辑演算,如你所期待的一样,但是它们并不 ...

  4. 《转》Python学习(18)-python函数(二)

    转自 http://www.cnblogs.com/BeginMan/p/3173328.html 一.装饰器(decorators) 装饰器的语法以@开头,接着是装饰器函数的名字.可选参数. 紧跟装 ...

  5. 老徐FrankXuLei 受邀为花旗银行讲授《微软WCF服务分布式开发与SOA架构设计课程》

    老徐FrankXuLei 受邀为花旗银行上海研发中心讲授<微软WCF服务分布式开发与SOA架构设计课程> 受邀为花旗银行上海研发中心讲授<微软WCF服务分布式开发与SOA架构设计课程 ...

  6. 【数据库系列】MySql中的select的锁表范围

    由于InnoDB预设的是Row-Level Lock,只有明确指定主键的时候MySql才会执行Row lock,否则MySql将会执行Table Lock. 1.明确指定主键则是行锁 2.明确指定主键 ...

  7. Android NDK学习(2)Windows下NDK开发环境配置

    转:http://www.cnblogs.com/fww330666557/archive/2012/12/14/2817386.html 一.配置好Android开发环境 二.下载安装安卓NDK   ...

  8. 外网电脑配置8G运行内存,运行Android Studio,速度很轻松

    Win 7系统 之前RAM是 4 G,运行Android studio ,再运行浏览器或办公软件时卡的一比.再插入一个 4G内存条,总共8G时,速度嗖的一下就上来了.

  9. 题目1447:最短路(Floyd算法)

    题目链接:http://ac.jobdu.com/problem.php?pid=1447 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...

  10. Mysql命令行导入sql数据

    mysqldump  是在  操作系统命令行下运行的,不是在 MySQL 命令行下运行的. 登陆数据库: 登陆本地mysql : mysql -h localhost -u root -p123456 ...