Java夏令时导致的问题
一、查询报错提示
报错提示: HOUR_OF_DAY: 0 -> 1
二、问题原因
通过网上查找资料得知,是中国有一段时间实施过夏令时导致的 1986-1991年 , 夏令时就是会在春夏之交当中某一天把时钟拨快一个小时的做法, 通过排查数据找到了一个有问题的日期** 1987-04-12 00:00:00这个日期是中国夏令时的开始时间,会在晚上把两点手动调成三点, 不同版本下 Asia/Shanghai 时区夏令时起始时间的不同,正是源于这种穷举配置,早期维护者认为中国标准时间的夏令时切换发生在0时,而后来又经证明发生在2时,新版本 JDK 及时修正了这个问题罢了。 所以mybatis返回时间做严格的校验会认为 1987-04-12**这一天不存在0点,应该直接从一点开始所以报错.
ps: 夏令时
夏令时,(Daylight Saving Time:DST
),也叫夏时制,又称“日光节约时制”和“夏令时间”,是一种为节约能源而人为规定地方时间的制度,在这一制度实行期间所采用的统一时间称为“夏令时间”。一般在天亮早的夏季人为将时间调快一小时,可以使人早起早睡,减少照明量,以充分利用光照资源,从而节约照明用电。各个采纳夏时制的国家具体规定不同。全世界有近110个国家每年要实行夏令时。
1986年4月,中国中央有关部门发出“在全国范围内实行夏时制的通知”,具体做法是:每年从四月中旬第一个星期日的凌晨2时整(北京时间),将时钟拨快一小时,即将表针由2时拨至3时,夏令时开始;到九月中旬第一个星期日的凌晨2时整(北京夏令时),再将时钟拨回一小时,即将表针由2时拨至1时,夏令时结束。从1986年到1991年的六个年度,除1986年因是实行夏时制的第一年,从5月4日开始到9月14日结束外,其它年份均按规定的时段施行。在夏令时开始和结束前几天,新闻媒体均刊登有关部门的通告。1992年起,夏令时暂停实行。
优点
高纬度地区由于夏季太阳升起时间明显比冬季早,夏令时确实起到节省照明时间的作用。正在调时间适应夏令时的工人
不少零售商对夏令时持肯定态度。美国的糖果商院集团已经游说美国国会将夏令时延长到11月,因为万圣节是糖果销售最旺的季节,而家长们不希望孩子们在天黑以后还在外面游逛。
有人认为夏令时对患有夜盲症的人大有好处。除了节约了电,也让人们养成了早睡早起的好习惯。
弊端
对低纬度地区,夏令时作用不大。尤其这些地方在夏天十分湿热,夜晚降临时闷热无法入眠,而清晨正是睡眠的好时间。
当夏令时开始和结束时,人们必须将所有计时仪器调快或调慢;当夏令时结束时,某些时间会在当天出现两次,这些都容易构成混乱。并且影响航班的时间。
夏令时违背了设定时区的原意──尽量使中午贴近太阳上中天的时间。
中国实时夏令时开始结束日期
1986年至1991年,每年四月的第2个星期日早上2点,到九月的第2个星期日早上2点之间。
1986年5月4日至9月14日(1986年因是实行夏令时的第一年,从5月4日开始到9月14日结束)
1987年4月12日至9月13日;
1988年4月10日至9月11日;
1989年4月16日至9月17日;
1990年4月15日至9月16日;
1991年4月14日至9月15日。
三、问题修复方案
- 在数据库系统设计时,需要用到精确时间和计算的时候尽量使用 绝对时间, 然后让这个时间在不同的时区和系统中手动转换成本地时间
- 在问题复现的过程中发现
OracleJDK
没有这个问题,OpenJDK
存在这个问题, 开发过程中本地JDK
和线上JDK
版本尽量保持一致 - 在
mybatis
查询映射过程中,尽量使用JDK8
版本的时间工具类LocalDate
不要使用Date (ps: 这种方式不兼容老的系统
) - 在
mybatis
查询映射过程中可以使用string类型来接收时间变量,自己手动转为时间类型,不同国家不同时区对同一日期的标识是不同的 - 可以通过修改数据库链接的时区来解决这个问题 由
serverTimezone=Asia/Shanghai
改为serverTimezone=GMT%2B8
ps: 绝对时间
格林威治时间1970年1月1日0时0分0秒相差的毫秒数