夏令时引起的数据库存储异常

       对于90后出生的孩子来说,夏令时还是比较陌生的,如果咨询上一辈的人就会有些印象。当我在家里的微信群发出这个疑问时,家里的姨姨舅舅无不吐槽,甚至说是一种自欺欺人的行为(O(∩_∩)O哈哈~)。那么,什么是夏令时呢?

       简单说就是夏天为了珍惜白天的时间,在指定日子把时钟拨快一小时,这样人们就可以早起工作节约用电。举个例子,假如平时是8点上班,夏令时的时候将时钟调快一小时,这样夏令时的8点钟,实际就成了7点去上班。主要是为了晚上可以早一个小时休息,节约用电。

       然而,我们大中华地域辽阔,虽然规定了统一使用东八时区,但是在一些偏远地方,比如新疆、西藏等,实际日出日落时间就不是东八的标准。这样导致我们的夏令时只是一部分地区可以起到目标效果,其他地方反而更不方便。于是在 1986 ~ 1991 年短暂执行了六年后,终于在1992年取消了该政策,至此也为后来的程序行业埋下了小小的雷。

       公司进行系统升级,由原本的Oracle 11g 升级为 Oracle 12c ,所以其对应的JDK、Ojdbc都需要进行相应的调整。一切都很顺理成章,应用测试也很顺利,除了一些业务逻辑的bug,并未发现技术类的bug。

       直到有一天,测试的同事在前台页面发现了异常,展示信息的表单控件中,一个日期格式的文本框竟然夹带了时间,查找数据库中对应的数据感觉非常无厘头,甚至看着有一点好玩。期初根本想不到是夏令时引起的数据异常,都还在傻乎乎的看日志,找对应的DML语句,最后还是我们的架构师一语道破。接下来就是漫长纠错与解决历程了。

以下是我们测试代码,以及数据库生成的数据:

package com.aikes.tools.utility;

import java.sql.*;
import java.text.SimpleDateFormat;

public class TestDate
{
	public static void main(String[] args)
	{
		String tUrl = "jdbc:oracle:thin:Aikes/Aikes@127.0.0.1:1521:aikes";
		PreparedStatement pstmt = null;
		Connection con = null;
		try
		{
			con = DriverManager.getConnection(tUrl);
			pstmt = con.prepareStatement("insert into testdate (makedate) values (?)", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
			java.util.Date utilDate = df.parse("1986-05-04");
			pstmt.setDate(1, new Date(utilDate.getTime()));
			pstmt.executeUpdate();
			pstmt.close();
			con.close();
		}
		catch (Exception e)
		{
			System.out.println(e.getMessage());
		}
		finally {
			try
			{
				pstmt.close();
				con.close();
			}
			catch (SQLException ex)
			{}
		}
	}
}

日期格式出现加一小时

       废话不多说,直接看原因以及我们制定的解决方案。

       首先,出现这种情况的根本是在preparestatement绑定变量插入日期时,遇到夏时令首日底层会进行加一小时的操作,比如:1986-05-04、1987-04-12等等,共计六个日期。引起该问题的初步归结到三个模块,操作系统,JDK,ojdbc。接下来就是问题复现的环境。

       通过控制变量进行迭代测试,我本地已经可以复现该问题(坎坷的不敢回忆~),最终初步确定该问题与JDK无关,当且仅当win7环境下ojdbc驱动版本大于等于7才会出现该问题。win 8、win10 无论驱动版本如何都没有问题;ojdbc 6 及以下版本无论何种操作系统都没问题。

       由于数据库版本是 Oracle 12c ,所以使用 ojdbc 7是必然的,至此,我们可以考虑是服务器的linux系统版本存在异常,需要进行升级维护。但是为了规避升级带来的其他风险,并且通过评估,出问题的日期相对比重很小(只有夏令时首日会出现加一小时的情况),暂时不需要大规模升级,只能想一个折中的办法进行处理。要么落库前修正,要么落库后运维,后期维护成本高且业务场景多变,这样一来第一想到的就是通过oracle 的 trigger 进行数据修正,并且只能使用 before DML 的方式在最终DML操作前对有误的日期变量做处理(Do you know why ? HAHA )。

       至此,问题基本解决。IT 就是这样,解决之后的总结总是云淡风轻,可是问题出现的时候玩命找原因简直是煎熬,尤其是在控制变量做迭代测试的时候,反反复复追代码看问题,JDK底层看的都脑阔疼,不过结局是好的,将这段经历分享给大家,希望可以帮到你们。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aikes902

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值