MySQL与Java中的日期API

相关问题:

问题:

Java系统中采用了localDateTime类型的数据,MySQL数据库中采用了DateTime类型。但是存储之后再取出来发现数据不一致,即秒之后的毫秒部分变成了000000。

原因:

MySQL的DateTime没有设置长度,则默认为不包含小数部分。即只存储’YYYY-MM-DD hh:mm:ss’,小数部分是不会被存储的,小数部分会被丢弃。但原始的localDateTime是包含小数部分的,即到微秒部分:10^-6s。所以存储后取出来的数据就损失了小于秒部分,所以小数点后都为0,与原来的不相等。

解决办法:

  1. 只对比秒之前的部分,即无视损失的部分。
  2. 将MySQL数据库的DateTime的长度设置为6(最长),则不会造成损失。

MySQL

MySQL有5种日期类型:

  • DATE
  • TIME
  • DATETIME
  • TIMESTAMP
  • YEAR

但是在介绍具体的类型之前,需要先看MySQL官方的几条关于日期的提醒:

  • MySQL会尝试各种格式来解析传入的值,但最好是按照标准的格式来进行传递,否则可能会有不可预知的行为出现。

    其具体格式简单介绍在后文附加。

  • 虽然MySQL会尝试各种格式,但是date部分总是应该按照year-month-day的顺序来传递。

  • 并不推荐用两个字表示的年份,起具有歧义,但MySQL仍然是接受的。其解析行为如下:

    • 如果是70-99则解析为1970-1999
    • 如果是00-69则解析为2000-2069
  • 默认情况下,当MySQL遇到一个超出date或者time类型范围的值时,会默认将其转换为该类型的”0“值。

  • MySQL允许存储“0”值0000-00-00作为填充值,有时这比null更方便。可以通过开启NO_ZERO_IN_DATE来禁止零值。

下面的表展示了各种类型的零值:

Data Type Zero Value
DATE '0000-00-00'
TIME '00:00:00'
DATETIME '0000-00-00 00:00:00'
TIMESTAMP '0000-00-00 00:00:00'
YEAR 0000

DATE, DATETIME, TIMESTAMP

DATE类型用来存储不含时间的日期值。MySQL以’YYYY-MM-DD’格式检索和显示DATE值。支持的范围是’1000-01-01’到’9999-12-31’。

DATETIME类型用来存储包含时间的日期值。MySQL以’YYYY-MM-DD hh:mm:ss’的格式检索和显示DATETIME值。支持的范围为’1000-01-01 00:00:00”到“9999-12-31 23:59:59’。

TIMESTAMP也用于存储包含时间的日期值。TIMESTAMP的范围是’1970-01-01 00:00:01’ UTC到’2038-01-19 03:14:07’ UTC。

DATETIME或TIMESTAMP值可以包含尾随的小数秒部分,精度最高可达微秒(6位)。

(注意默认是没有小数部分,需要将长度改为6才有所有的小数部分)

而DATETIME包含小数的范围为:’1000-01-01 00:00:00.000000’to‘9999-12-31 23:59:59.999999’

TIMESTAMP包含小数的范围为: '1970-01-01 00:00:01.000000' to '2038-01-19 03:14:07.999999'

MySQL将TIMESTAMP值从当前时区转换为UTC来存储,然后从UTC转换回当前时区以进行检索。(这不会发生在其他类型,如DATETIME。)

默认情况下,每个连接的当前时区都是服务器的时间。

时区可以在每个连接的基础上设置(通过time_zone=xxx)。只要时区设置保持不变,就会得到存储的相同值。如果存储了一个TIMESTAMP值,然后更改时区并检索该值,则检索到的值与存储的值不同。这是因为在两个方向上的转换没有使用相同的时区。

无效值得处理:

  • 非严格模式下,无效的DATE、DATETIME或TIMESTAMP值将被转换为适当类型的“零”值,例如10:45:15会被转化为0000-00-00,因为’45’不是一个合法的月份。
  • 严格模式下,不仅仅只限制月份在1-12之间、天在1-31之间,而是会限制大月小月的区别,如’4-32’就不合法,因为4越没有31号。

(这里的严格模式是指启用STRICT_TRANS_TABLESSTRICT_ALL_TABLES中的一个或两个模式。)

TIME

MySQL以’hh:mm:ss’的格式检索和显示时间值。其范围从’-838:59:59’ 到’838:59:59’。

小时的部分可能是如此之大,因为可以使用时间类型不仅代表一个时间点(必须小于24小时),也可以代表运行时间或两个事件之间的时间间隔(可能是远远大于24小时,甚至负)。

TIME也可以包含小数部分来表示更小的时间单位,最小可以到达小数点后6位,即微秒。此时其范围为:'-838:59:59.000000' to '838:59:59.000000'

对于简写,其规则如下:

  • 如果包含冒号,如’11:12’,则按从小时开始解析,即’11:12’被解析为’11:12:00’。
  • 如果不含冒号,如’1112’,则按从秒开始解析,即’1112’被解析为’00:11:12’,’12’被解析为’00:00:12’。

注意:TIME的零值’00:00:00’本身也是一个合法的TIME值,所以如果数据库里存储的是’00:00:00’,则并不能识别其到底是零值还是正确的值。

YEAR

YEAR类型是一种1字节类型,用于表示年份值。

注意

从MySQL 8.0.19开始,带有显式显示宽度的YEAR(4)数据类型已经被弃用,MySQL在未来版本中将会将其移除。直接使用YEAR而不使用显示宽度,其具有相同的含义。

MySQL 8.0不再支持2位YEAR(2)数据类型。

MySQL以YYYY格式显示YEAR值,取值范围为1901 ~ 2155和0000。

YEAR字段接受以下格式的值:

  • 四个数字的字符串,范围在’1901’到’2155’之间。
  • 四个数字的数字类型,范围在1901-2155之间。
  • 两个数字的字符串,与前面DATE一样,范围在’0’-‘99’之间,’0’-‘69’表示2000-1069年。’70’-‘99’表示1970到1999年。
  • 两个数字的数字类型,遇上面一样,只是传递的是数字。
  • 作为返回在YEAR上下文中可接受的值的函数的结果,例如NOW()。

Java

Java的日期API可以看另一篇专门介绍的文章。java核心技术-II-6-日期和时间API

只能说Java的日期API设计经过了这么多轮的迭代,比较混乱,要想完全掌握,还确实需要时间。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2019 - 2024 My Wonderland All Rights Reserved.

UV : | PV :