日期和时间在程序中应用广泛,每种程序开发语言都自带处理日期和时间的相关函数,很多开发者把日期和时间存入数据库中,但是,一旦涉及到跨时区的日期和时间的处理时,大多数开发者根本就不明白如何正确地处理日期和时间。

首先,我们来看大部分的程序都是这么创建当前时间并存入数据库的:

Date date = new Date();
store2db(date);

这么做的问题在于,数据库的DateTime类型没有时区(time zone)信息,因此,存入的是本地时间,并且丢掉了时区信息。如果你把数据库服务器的时区改了,或者把应用服务器的时区改了,读出来的日期和时间就是错误的。如果以Timestamp类型存储,各数据库的实现也不相同,有的进行了内部时区自动转换,而且,存储的时间不超过2037年。

如果应用服务器的时区和数据库服务器的时区不一致,你无法确定数据库驱动程序会不会自动帮你转换。

大多数开发者遇到这个问题不是去探索正确的解决方法,而是自作聪明地在存入数据库之前先来个“调整”,比如把当前时间减掉8小时,在显示的时候遇到不正确的时间时,又来个“调整”,以“负负得正”的方式来掩盖错误。在遇到夏令时的时区时,还需要写更复杂的代码来调整小时。

正确的做法是先理解时间和时区的概念。

时区的概念

之所以有时区的概念是因为住在地球上不同地方的人看到太阳升起的时间是不一样的。我们假设北京人民在早上8:00看到了太阳刚刚升起,而此刻欧洲人民还在夜里,他们还需要再过7个小时才能看到太阳升起,所以,此刻欧洲人民的手表上显示的是凌晨1:00。如果你强迫他们用北京时间那他们每天看到日出的时间就是下午3点。

也就是说,东8区的北京人民的手表显示的8:00和东1区欧洲人民手表显示的1:00是相同的时刻:

"2014-10-14 08:00 +8:00" = "2014-10-14 01:00 +1:00"

这就是本地时间的概念。

但是,在计算机中,如果用本地时间来存储日期和时间,在遇到时区转换的问题上,即便你非常清楚地知道如何转换,也非常麻烦,尤其是矫情的美国人还在采用夏令时。

所以我们需要引入“绝对时间”的概念。绝对时间不需要年月日,而是以秒来计时。当前时间是指从一个基准时间(1970-1-1 00:00:00 +0:00),到现在的秒数,用一个整数表示。

当我们用绝对时间表示日期和时间时,无论服务器在哪个时区,任意时刻,他们生成的时间值都是相等的。所有编程语言都提供了方法来生成这个时间戳,Java和JavaScript输出以毫秒计算的Long型整数,Python等输出标准的Unix时间戳,以秒计算的Float型浮点数,这两者转换只存在1000倍的关系。

实际上,操作系统内部的计时器也是这个标准的时间戳,只有在显示给用户的时候,才转换为字符串格式的本地时间。

正确的存储方式

基于“数据的存储和显示相分离”的设计原则,我们只要把表示绝对时间的时间戳(无论是Long型还是Float)存入数据库,在显示的时候根据用户设置的时区格式化为正确的字符串。

数据的存储和显示相分离是非常基本的设计原则,却常常被大多数开发人员忽略。举个例子,在Excel中编写一个表格,表格的数据可视为数据的存储格式,你可以把表格的数据以柱状图或饼图表示出来,这些不同的图表是数据的不同显示格式,存储数据的时候,我们应该存储表格数据,绝不应该存储柱状图等图片信息。

HTML和CSS也是数据的存储和显示相分离的设计思想。

所以,数据库存储时间和日期时,只需要把Long或者Float表示的时间戳存到BIGINTREAL类型的列中,完全不用管数据库自己提供的DATETIME或TIMESTAMP,也不用担心应用服务器和数据库服务器的时区设置问题,遇到Oracle数据库你不必去理会with timezonewith local timezone到底有啥区别。

读取时间时,读到的是一个Long或Float,只需要按照用户的时区格式化为字符串就能正确地显示出来:

// Java:
long t = System.currentTimeMillis();
System.out.println("long = " + t);// current time zone:
SimpleDateFormat sdf_default = new SimpleDateFormat("yyyy-MM-dd HH:mm");
System.out.println(sdf_default.format(t));// +8:00 time zone:
SimpleDateFormat sdf_8 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_8.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
System.out.println("GMT+8:00 = " + sdf_8.format(t));// +7:00 time zone:
SimpleDateFormat sdf_7 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_7.setTimeZone(TimeZone.getTimeZone("GMT+7:00"));
System.out.println("GMT+7:00 = " + sdf_7.format(t));// -9:00 time zone:
SimpleDateFormat sdf_la = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_la.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println("America/Los_Angeles = " + sdf_la.format(t));

输出:

long = 1413230086802
2014-10-14 03:54
GMT+8:00 = 2014-10-14 03:54
GMT+7:00 = 2014-10-14 02:54
America/Los_Angeles = 2014-10-13 12:54

总结:

基于绝对时间戳的时间存储,从根本上就没有时区的问题。时区只是一个显示问题。额外获得的好处还包括:

  1. 两个时间的比较就是数值的比较,根本不涉及时区问题,极其简单;

  2. 时间的筛选也是两个数值之间筛选,写出SQL就是between(?, ?);

  3. 显示时间时,把Long或Float传到页面,无论用服务端脚本还是用JavaScript都能简单而正确地显示时间。

你唯一需要编写的两个辅助函数就是String->Long和Long->String。String->Long的作用是把用户输入的时间字符串按照用户指定时区转换成Long存进数据库。

唯一的缺点是数据库查询你看到的不是时间字符串,而是类似1413266801750之类的数字。

个人感悟:时区只是一个显示问题(人为造成的,不过也是为了方便显示),但是使用绝对时间的时间戳就不存在时区问题,毕竟无论你在世界的哪个地方你的1s和我的1s的时长是一模一样的,这个是永远不会变的。

正确处理时间和时区问题(java+mysql)相关推荐

  1. mysql tomocat vs2005_C# 制作Java +Mysql+Tomcat 环境安装程序,一键式安装

    要求: JDK.Mysql.Tomcat三者制作成一个安装包, 不能单独安装,安装过程不显示三者的界面, 安装完成要配置好JDK环境.Mysql服务.Tomcat 服务 目的: 解决客户在安装软件的复 ...

  2. java mysql 死锁,java-Spring JPA MySQL和死锁

    我正在研究使用Spring Boot在Java中实现的REST API.我使用了嵌入式内存数据库H2数周,但在某个时候我注意到事务隔离存在问题. 更准确地说,我有一个表,需要在其中跟踪"重复 ...

  3. 基于java+mysql的Swing+MySQL图书管理系统(java+swing+gui+mysql)

    基于java+mysql的Swing+MySQL图书管理系统(java+swing+gui+mysql) 运行环境 Java≥8.MySQL≥5.7 开发工具 eclipse/idea/myeclip ...

  4. 基于Java/Mysql的个人博客网站

    3年前写的一个技术博客...纪念一下. OpenIdea Blog - 开源灵感博客 a personal blog site based on Java/Mysql - 基于Java/Mysql的个 ...

  5. java mysql物联网土壤智能监控web前端+java后台+数据接程序

    博主介绍:✌在职Java研发工程师.专注于程序设计.源码分享.技术交流.专注于Java技术领域和毕业设计✌ 项目名称 java mysql物联网土壤智能监控web前端+java后台+数据接程序 视频效 ...

  6. 基于javaweb的慢病报销管理信息系统(java+mysql+jdbc+servlet+jsp)

    基于javaweb的慢病报销管理信息系统(java+mysql+jdbc+servlet+jsp) 运行环境 Java≥8.MySQL≥5.7.Tomcat≥8 开发工具 eclipse/idea/m ...

  7. 基于javaweb+jsp的生病慢病报销管理信息系统(java+MySQL+Jdbc+Servlet+Jsp)

    基于javaweb+jsp的生病慢病报销管理信息系统(java+MySQL+Jdbc+Servlet+Jsp) 一.项目简述 功能: 慢病管理,医疗机构管理,家庭管理,费用交纳,费用报销,报表统计等等 ...

  8. java mysql基于SSM宠物寄养中心系统源码+文档

    活动地址:毕业季·进击的技术er 博主介绍:✌在职Java研发工程师.专注于程序设计.源码分享.技术交流.专注于Java技术领域和毕业设计✌ 项目名称 java mysql基于SSM宠物寄养中心系统源 ...

  9. Lerx开源网站内容管理系统(CMS) v6.5 以Java+MySQL进行开发的内容管理系统源码

    介绍 Lerx 开源网站内容管理系统(CMS)是一个以Java+MySQL进行开发的内容管理系统源码. Lerx 开源网站内容管理系统(CMS)特点: 1.跨平台设计,能无差别运行于Windows.L ...

最新文章

  1. 区块链基础:理论和术语
  2. Android --- 怎么设置 EditText 控件中光标默认位置,当 EditText 里有文字的时候,光标跑到了最前面
  3. asp.net+mysq 数据库操作类
  4. java indexof 忽略大小写_javascript的indexOf如何才能忽略大小写
  5. 5.贝叶斯算法、单词拼写错误案例
  6. 吴恩达作业11:残差网络实现手势数字的识别(基于 keras)+tensorbord显示loss值和acc值
  7. 推特超2K赞,DeepMind强化学习综述:她可以很快,但快从慢中来
  8. 在线预览(pptx、ppt、pps、docx、doc、xlsx、xls)
  9. go语言:一些环境变量
  10. 浅谈人工智能的工作原理
  11. Kettle JAVA代码表达式
  12. linux timeout 格式,Linux内核API wait_for_completion_timeout
  13. Loadrunner12实现手机APP压力测试
  14. 【Spring】SpringAOP切面类
  15. 什么是参数化设计,通过实操了解一下? | SOLIDWORKS 操作视频
  16. Layer2 DAO基础协议Metis与IDO平台Paid Network达成战略合作
  17. 每日一字:biáng
  18. 如何将任意两张图片合并成一张图片并将合并后的图片导出为EPS高精度图片?
  19. 【微信小程序】一文带你吃透开发中的常用组件
  20. C++高阶 类型转换函数最透彻的一篇文章

热门文章

  1. execve系统调用_execve系统调用分析
  2. Web端算法部署+流媒体服务器算法部署+Flask+AI健身+Python-web实时检测效果显示
  3. 网站403错误解决方案
  4. 李昀飞:兴业数金金融行业云 中小银行转型重要引擎
  5. 去除WinRAR 5.01(32位) NAG窗口
  6. 如何让SQL Server数据库自动备份并压缩
  7. JAVA 二叉树 常见操作合集(前中后序递归非递归遍历 层序遍历 求深度宽度 判断兄弟结点 堂兄弟节点)
  8. Oracle数据库全备份和增量备份Windows
  9. windows键盘事件处理
  10. python numpy安装教程_python3.6下Numpy库下载与安装图文教程