你确信你了解时间吗?
你还记得“软件真的好难做”中的那个有意思的例子吗?那个例子告诉我们软件开发中假设可能会是致命的事。今天,我又在StackOverflow上看到一个关于时间的问题——为什么1927年12月31日的午夜时间这么奇怪?提问题的这个人给了下面的一段java代码(我做一些修改,保证让你可以copy过去就可以编译运行)
我在其中高亮了几行,这个程序就是想比较一下“1927-12-31 23:54:07” 和 “1927-12-31 23:54:08” 差几秒,很明显,是差一秒。但是程序的输出却不是这样的。
import java.text.SimpleDateFormat; import java.text.ParseException; import java.util.Date; import java.util.TimeZone; class time{ public static void main(String[] args) throws ParseException { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); String str3 = "1927-12-31 23:54:07"; String str4 = "1927-12-31 23:54:08"; Date sDt3 = sf.parse(str3); Date sDt4 = sf.parse(str4); long ld3 = sDt3.getTime() /1000; long ld4 = sDt4.getTime() /1000; System.out.println(ld3); System.out.println(ld4); System.out.println(ld4-ld3); } }
下面,让我们来看看程序的输出:(是的,差出353秒钟来)
-1325491905
-1325491552
353
Stackoverflow真的很强大,在大家要求发问者给出时区(中国上海)的15分钟内就解决了这个问题。相当的令人惊叹。原因是什么呢?大家需要围观一下这个网页。(为了怕被墙或是被和谐,我已习惯了抓屏保存,如果有人能开发一个软件能随看随抓,然后如果源被删了可以P2P的从已下载了的人那里获取,那么这个软件应该会很有国内市场。蛋扯远了,Sorry)
从上图中我们可以看到—— 在1927年12月31日23:59:59时,往后面的一秒应该是1928年1月1日 0:0:0,但是这个时间被往后调整了5分52秒,而成了,1927年12月31日的,23:54:08,于是,完成了352秒的穿越。于是我们的Java程序出了这样的一个问题,这真是一个奇迹。
为什么会有这个调整呢?我居然Google不到,不过,我在这个timeanddate.com上查看了一下北京的时间,发现北京的时间只到1970年,于是我猜想,中国近代历史乱七八糟的政权交替可能是这个原因。于是我看 了一下北京和上海物理时差,果然,北京上海的时差在5分50秒左右。因此,我觉得这个时间的变化应该是从上海(南京)时间变成了北京时间。至于你信不信,反正我是信了。
从这个事,我得到下面的一些启示:
- Java在的时区实现相当的强大啊。这种细节都能考虑到。
- 本地时间的完全就是一锅粥,应该尽量不用。
- 如果你要开发和时区有关系的程序,你的系统里一定要使用GMT标准时间,仅在显示的时候才转成本地时间。
(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)
《你确信你了解时间吗?》的相关评论
压力真的很大
Java的处理真是细致。
但JVM的程序员是如何想到的呢?不得不膜拜一下写这段VM的程序员!
1928年1月1日,上海时间由地方平时(Local Mean Time)改为北京时间(中国标准时间)。应该是国民政府的命令吧。
非程序员表示压力很大。
其他语言应该也是这样
相当有压力!
想试试C#的,发现Windows XP没有上海这个时区…
—为了怕被墙或是被和谐,我已习惯了抓屏保存,如果有人能开发一个软件能随看随抓,然后如果源被删了可以P2P的从已下载了的人那里获取,那么这个软件应该会很有国内市场。
evernote就可以随时保存抓屏,国内应该还有一些免费的同类产品
建议看看 http://zh.wikipedia.org/wiki/%E4%B8%AD%E5%9C%8B%E6%99%82%E5%8D%80
煎蛋上看到一样的文章
又撞车了。煎蛋 也面对程序员了?
试了下python和C/C++,发现不支持负数的 time_t @_@
@Nogard
是改用中原标准时间,那时候还没有北京时间的概念
这里面好像没看到解释这352秒的问题呀
这个Fall_Ark已经在煎蛋上发过了,而且他也讲得很清楚,当时上海用的时间属于地方平时,在那天改成了北京标准时。
再次说明了,有些东西不像表面上看到的那么简单。
而且我怎么觉得煎蛋虽然是个娱乐网站,关于程序方面回帖的质量比这里还要高些呢?是我错觉么。。。
煎蛋网址:http://jandan.net/2011/07/30/1927-java-time.html
@xt
JDK 用的都是icu的数据, 跟vm没关系
时区的选择,应该按经度加减就行了吧。
像本例,运行环境自动把时区与行政关联,应该是猫捉老鼠了吧
哦,是狗拿耗子了
应用如果真的与行政关联,应该是应用开发者自己要关心、考虑、增减的
@hscui
煎蛋人多,程序员也少不了.
不是JVM的开发者考虑得细,linux和windows的时间库里都存了很多类似这种细节的东西,我也被类似的问题郁闷到过,http://blog.loudly.me/2009/05/daylight_saving_in_1986
@haitao
不是与行政关联。是原来用的是上海地方平时,定义上与行政有点关系,后来改为东经120°的地方平时。也就是原来的定义是不够标准的,在文中提到的时间改成标准的了。在计算跨越标准定义变化的时段,程序真实的把这个定义变化显示了出来。这是对的,因为程序是对真实世界的模拟,真实世界中当时人们是有调钟这个动作的。
程序始终遵从定义,先前的定义与行政有关系,程序也没有办法。
看完现象,我就想是不是因为地球自转公转的原因,话说自转24小时总不是那么精确的,有时候可能需要补时间?
压力山大, 但愿国内的标准时间不要再改了.
@autoxbc
我觉得,程序应该只是存储GMT时间,用户的时区不同,也只要把他的时间增减一个时区经度导致的偏移量就行了
至于是夏令时还是行政时区标准变换、甚至是公转的零头秒数,不应该由运行环境去决定
而应该由应用去决定,这就是做应用的要熟悉业务的体现
做运行环境的去熟悉业务,反而是狗拿耗子了
应该是上海的LMT(地方平时)比东经120度标准时快5分52秒吧。上海市中心(比如外滩,在1927年应该也是市中心吧)大约是东经121.4x度,5分52秒相当于1.47度,应该正好是上海LMT和东经120度标准时之差。
试了一下lua,也不支持负time_t………………@xijiao
记GMT时间的话,如果发生时区切换怎么办?
真正的深入阅读,常见的字符串、数字和日期 处理在国际化前不堪一击,
http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx
这里的评论渐渐开始有技术含量了,不错,我也学到了很多。
那如果计算 1927-12-31 23:55:07” 和 “1927-12-31 23:55:08” 差几秒怎么算? 这5分多钟里的时间算是用哪个时区来算呢?算是+8:05:52时区还是+8的时区?
@haitao
显然不对了,时间是度量衡的一部分,根本不是你说的业务的一部分。你写的如果不是专门处理时间时期的程序,里面包含如此具体的细节,那个才叫狗拿耗子。
作者有一句话说的不够清楚,”我觉得这个时间的变化应该是从上海时间变成了北京时间”,这句话不理解这个问题的人还是不能看懂,应该具体的说是上海时区的标准时间的定义,从上海的地方平时东经 120°28’,变成了新的北京时间的定义东经 120°,这个新的北京时间也不是北京平时东经 116° 。
@warking
这里还有一点不够清楚,程序计算的始终是人类定义的时间的间隔,这个间隔在那个时刻就是 5分52秒,这个时间应该叫名义时间;如果你真的想计算物理时间间隔,应该先把输入的时间转换为 UTC,分割点前的按照老的定义转换,分割点后的按照新的定义,这个相关的库会自动处理,得到的结果也不会错误。
所以这里的问题其实是程序写的有问题,不是时区的定义有问题,陈皓说的”如果你要开发和时区有关系的程序,你的系统里一定要使用GMT标准时间,仅在显示的时候才转成本地时间”就是这个意思。
-1325462753
-1325462752
1
其实这事完全不用大惊小怪的。关于时间处理这里。可以看一下这个命令输出:
前提您是linux
zdump -v Asia/Shanghai | less
所有在你处理类似时间问题时,都会自动转换。只是java这事,jvm处理。不归系统来处理。
@autoxbc
是3个时区的问题了:
1、上海LMT(+08:05:52),1927年前的某段时间才有效
2、上海(+08:00:00),1927年后才有效
3、x,程序当前运行使用的时区
显然,程序是希望使用不变的同一个时区的
但是运行环境自作主张,把1927年前的x算成1,1927年后的x算成2了。这样就导致了跳变
如果真的是时间敏感的,应用程序会由程序员加上附注(GMT什么时间,当地是什么时区;或按当地是什么时间,当地是什么时区;就像执行夏令时的地方,火车时刻表要标注是否按夏令时)
这也是中国的夏令时最终被废弃的主要原因:任何时间敏感的信息,都需要附注是不是按夏令时
而每年切换夏令时的附近,这种歧义会造成很多人耽误事
所以,宁可少节约能量也不要这种混乱了
作为程序,相当于是一种“客观”描述者,肯定是需要一种连续的通用时间,只是需要注明经度(决定日落日出的参照)
GMT已经是很通用的“客观”时间了,使用它肯定没问题
使用不变的同一时区的时间,也没问题,只是需要注明经度(如+08:00:00),这样也不会出现本例的跳变了
习惯性的 一个月来这里看一次
@haitao
这仍然不属于你说的业务范畴,他给出了两个本地时间,而没有指定这个时间对应的时区,属于程序本身的缺陷。在排除这个缺陷后,一个计算两个本地时间间隔的程序员仍然不需要考虑时区定义变化这个细节,仍然应该由库来处理。
另外有一点没看到有人写,似乎大家都认为本例是时区定义变换导致的计算错误,我觉得程序写的有问题,但库的计算没有错(与真实情况一致),你们真的觉得在确定了时区的情况下,任何两个名义上间隔 1 秒的时刻的物理间隔必须是 1 秒么,在这个例子里,如果你有一块按照标准时定义的表,真的需要等上 5 分 53 秒指针才能走到那个位置。
@陈皓
简单也会有一些geek的文章。
简单地说,在1927年末的最后那一个午夜,时钟被往回拨了5分52秒。所以“1927-12-31 23:54:08”这一秒,事实上,发生了两次,而看起来在计算当地时间时,Java将其视为了后面那一个时间点,于是就产生了这一差别。
我感觉煎蛋的解释更清晰啊
@陈浩
ftp://elsie.nci.nih.gov/pub/tzdata2011h.tar.gz
如果想了解时间,看看这里面的东西就知道了。
虽然很多号称“标准”的计算机系统都用它作为时区划分的参考,但它并不完备,尤其是对于中国等信息比较封闭的地区。像是早期的 5 时区划分,相信是因为那时台湾的国民政府是中国在国际上的代表,而采取了与当地实际时区不符的数据。看起来,那些贡献者只知道 1949 年的中国政权变动,不了解上世纪70-80年代,国共政府国际地位的变动。
时区这种东西,被政治干扰太严重,不是计算机科学能解决的。我个人比较支持将中国划分为东部、中部、西部3个时区。如果考虑到交流问题,只分为中东部和西部两个也可以。
PS:我只是从计算机和实用的角度谈,不要扯到政治上去。
我党太有意思了……
—为了怕被墙或是被和谐,我已习惯了抓屏保存,如果有人能开发一个软件能随看随抓,然后如果源被删了可以P2P的从已下载了的人那里获取,那么这个软件应该会很有国内市场。
这个很有意思,如果有一个类似 archive.org 这样的可供在线查询的快照服务就好了
弄这个东东的程序员是要多细心啊,geek啊
jvm默认不是上海的网友别忘了加一句:
sf.setTimeZone(TimeZone.getTimeZone(“Asia/shanghai”));
奇怪。我尝试这段代码的时候它的显示是这样的:
-1325462753
-1325462752
1
顺便说一句,我用的是oracle jdk 1.7 hotspot.
在我的 Ubuntu 11.04 操作系统中运行也是只差一秒:
ben@ben-m4000t:~/work/08$ java -version
java version “1.6.0_22”
OpenJDK Runtime Environment (IcedTea6 1.10.2) (6b22-1.10.2-0ubuntu1~11.04.1)
OpenJDK 64-Bit Server VM (build 20.0-b11, mixed mode)
ben@ben-m4000t:~/work/08$ javac -version
javac 1.6.0_22
ben@ben-m4000t:~/work/08$ javac time.java
ben@ben-m4000t:~/work/08$ java time
-1325462753
-1325462752
1
ben@ben-m4000t:~/work/08$
确实令人汗颜!
时区方面的信息都是操作系统提供的,Java只是简单地做了映射。所以在不同OS上得到不同结果是可能的。
@skyivben
也许得把 “Asia/shanghai” 改成 “Asia/Shanghai”
这个问题。。。是不是在制造时光机器的时候,才需要考虑。
鸭梨相当大……