`

(转)如何正确地处理时间

 
阅读更多
from:
http://www.liaoxuefeng.com/article/0014132675721847f569c3514034f099477472c73b5dee2000


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

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

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表示的时间戳存到BIGINT或REAL类型的列中,完全不用管数据库自己提供的DATETIME或TIMESTAMP,也不用担心应用服务器和数据库服务器的时区设置问题,遇到Oracle数据库你不必去理会with timezone和with 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
基于绝对时间戳的时间存储,从根本上就没有时区的问题。时区只是一个显示问题。额外获得的好处还包括:

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

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

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

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

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

相关推荐

    复杂地区煤田三维地震勘查资料数字处理技术研究

    通过山东某煤田勘查实例介绍,指出三维地震勘查资料数字处理以达到...在时间剖面上,目的层反射波TQ+N、T3上层次齐全,信噪比高,分辨率强,能量适中,同相轴连续性好,表明资料处理参数选择正确,资料处理流程合理,质量较高。

    如何将PPT转word

    PDF是Adobe公司开发的作为全世界可移植电子文档的通用格式,它能够正确保存源文件的字体、格式、颜色和图片,使文件的交流可以轻易跨越应用程序和系统平台的限制。但是,一些文档的特殊操作(如提取PDF文档中的一段...

    SQL沉思-技术手册

    此外,本书还讨论了关于 SQL编程中查找表、视图、辅助表、虚拟表的应用,并独到地阐明了如何在 SQL系统中正确地处理时间值以及 SQL编程中的其他技术难点。 本书适合广大数据库编程人员和 SQL程序员学习参考。

    测试规章制度

    1.无法正确地处理数据,影响系统正常运行 2.非统计分析功能的每次操作后等待时间超过5秒 3.统计分析功能的每次操作后用户等待时间超过10秒 中: 1.有系统报错信息出现,但不影响系统正常运行 2.非统计分析功能的...

    哨兵雷达卫星干涉处理

    故本文针对哨兵卫星TOPS模式影像干涉处理理论和方法做全面地论述。 鉴于哨兵卫星影像质量和卫星辅助参数均达到了很高的精度,论文优先考虑基于影像几何的初配准。但是该方法依赖于精密轨道。考虑实时处理的需要,...

    pomodoro:使用简单的番茄技巧助手正确地管理您的时间

    使用简单的Web应用程序正确管理您的time 内置 :hot_beverage: 由 目录 科技栈 前端-React.js,MaterialUI,Apollo客户端,图表 后端-Node.js,Express.js,GraphQL,Apollo服务器 数据库-MongoDB,猫鼬 特征 :...

    sub2xml:Python脚本将Premiere Pro的字幕转换为标题时间线

    可以将FCP-XML导入Premiere Pro中,从而给出一个带有所有字幕作为标题对象的时间线,并正确地放置在时间线上。 听起来不错吗? 有几个限制,请参见下文。 导入后,保存项目以嵌入prtl文件。 [1] [2] [3] 局限...

    SrtEdit2012 V6.2

    特别是某些简体字往往对应多个繁体字),而是在此基础上以词汇表为依据,根据上下文正确地实现简繁体的转换。  中外文双语字幕编辑制作。  SrtEdit 2012 支持 ssa 和 ass 字幕特效编辑,并可即时预览特效效果。...

    混沌时间序列的小波神经网络预测方法及其优化研究(高清)

    自从小波神 经网络被提出以后,它在非线性函数或信号逼近、信 号表示和分 类、系统辨识和动态建模、非平稳时间序列预测与分 析等许多领域 中被较为广泛地应用。尽管如此,将小波和人工神经 网络理论应 用到预测还有...

    IBM WebSphere HomePage Builder 2001-web图像处理-4

    如果您使用了8位的显示颜色(256色),则有些颜色不能正确显示。 不支持单色显示。 Windows 95 可能无法区分出带有 .htm 和 .html 扩展名的 HTML 文件。 使用HTML 4.0 标签的网面需要 Netscape Navigator 4.0 或 ...

    内存工作原理之内存寻址 内存传输 存取时间 内存延迟

    CPU都会为这些读取或写入的资料编上地址(也就是我们所说的十字寻址方式),这个时候,CPU会通过地址总线(Address Bus)将地址送到内存,然后数据总线(Data Bus)就会把对应的正确数据送往微处理器,传回去给CPU...

    基于图像处理的回转窑物料休止角检测方法 (2009年)

    为验证该方法的可行性,对某氧化铝回转窑图像序列进行 Matlab计算机仿真实验,测量出了休止角随时间变化的动态曲线,该曲线的周期特性正确地反映了物料在窑内的运动状况。研究结果为回转窑工况的判断提供了一种新的...

    如何混音处理

    50hz,这是我们常用的最低频段,这个频段就是你在的厅外听到的强劲的地鼓声的最重要的频段,也是能够让人为之起舞的频点。通过对它适当的提升,你将得到令人振奋的地鼓声音。但是,一定要将人声里面所有的50hz左右的...

    阿里巴巴java开发手册

    4. **并发控制**:在多线程环境下,要正确地处理并发访问共享资源的问题,避免出现死锁、竞态条件等并发问题。 5. **性能优化**:优化代码以提高性能,包括减少内存占用、降低时间复杂度等方面的优化。 6. **安全性*...

    如何使用正确的数据采集模式

    这些采集模式能让你高效地使用板载内存,减少两次采集之间的死区时间。模块化数字转换器经常用于采集来自低占空比信号的数据,例如回波测距(包括雷达、声纳、激光雷达和超声波),以及瞬态数据采集应用(如飞行时间...

    用Chat GPT写论文的正确打开方式

    随着人工智能技术的不断发展,自然语言处理模型的应用也愈加广泛,其中最具代表性的就是GPT系列模型。而现在,使用Chat GPT来写论文也成为了一种趋势。 它可以模仿人类写作风格来产生高质量的文章,帮助我们更快地...

    数据转换/信号处理中的DC/DC转换器中电阻式反馈分压器设计

    但是,人们常常错误地认为,它是一种简单地通过将电压调低至某个基准电压来实现输出电压调节的电路。在计算得到正确的分压器分压比以后,在选择实际电阻值时电源设计人员还必须沉思熟虑,因为它们会影响转换器的总体...

    元器件应用中的正确地使用电解电容器

    其中一条铝箔表面经过处理生成氧化膜介质,作为正极,另一条没有氧化膜的铝箔作为负极。正、负极用引线引出,在两条铝箔中夹有一条饱含电解糊的毛纸或纱布之类的纤维材料。这种电容器的正极接正电、负极接负电时,...

    最新软件狗时间复制工具

    “蓝芯金盾加密锁”可以为有特殊需要的用户编写专门的加密函数,更安全地将用户要求融入加密锁中,用户使用加密锁不是去判断加密锁是否正确,而是由加密锁实现用户软件的特定功能。 实际上加密锁的好坏仅仅是一个...

    基于51单片机和18B20的温度计设计

    51单片机是一种常用的微控制器,而18B20是一种数字温度传感器。通过将18B20连接到51单片机的IO口,可以实现温度的测量和监控...在编写代码时,可以参考18B20的数据手册和51单片机的开发文档,以确保正确地读取温度值。

Global site tag (gtag.js) - Google Analytics