22

程序员如何处理好时区问题

 5 years ago
source link: https://www.owenzhang.net/blog/197.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

写国际化的程序,经常会遇到两种问题:字符编码、时间问题。今天我们就聊聊程序中如何处理时间问题。

首先,要了解一些基本的概念,只有对概念有清晰的掌握,才能明白解决方法。

基本概念

GMT时间:格林尼治标准时间(英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台当地的平太阳时,因为本初子午线被定义为通过那里的经线。

由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治平时基于天文观测本身的缺陷,已经被原子钟报时的协调世界时(UTC)所取代。

UTC时间:协调世界时(英语:Coordinated Universal Time,法语:Temps Universel Coordonné,简称UTC)是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林尼治标准时间。

对于大多数用途来说,UTC时间被认为能与GMT时间互换,但GMT时间已不再被科学界所确定。

UNIX时间戳:Unix time又叫POSIX time或UNIX Epoch time,是从UTC时间1970年1月1日起到现在的秒数,不考虑闰秒,一天有86400秒。

时区:时区是地球上的区域使用同一个时间定义。世界各个国家位于地球不同位置上,因此不同国家特别是东西跨度大的国家日出、日落时间必定有所偏差。这些偏差就是所谓的时差。

闰秒:闰秒是在协调世界时(UTC)中增加或减少一秒,使它与平太阳时贴近所做调整。在UTC时间中,有时会出现一分钟有59秒或61秒。

夏令时:美国原本于每年4月的第一个星期日凌晨2时起至10月的最后一个星期日凌晨2时实施夏时制;但经美国国会2005年通过的能源法案,自2007年起延长夏时制,开始日期从每年4月的第一个星期日,提前到3月的第二个星期日,结束日期从每年10月的最后一个星期日,延后到11月的第一个星期日。美国夏时制实行与否,完全由各州各郡自己决定。

时间格式的标准:参考ISO_8601日期格式标准 https://zh.wikipedia.org/wiki/ISO_8601。例如:2004-05-03T17:30:08+08:00 在时间前面加上大些字母T,要标明偏移的时区时间。

概念解读

通过上面的概念介绍了解到,GMT就是0时区的时间,以前是标准,但现在国际上已经用UTC取代他了。在写程序时,可以认为UTC和GMT是等价的。为了严谨只需要关心UTC时间。

UNIX时间戳是程序中最常用的,他的特点是和UTC时间的1970年1月1日到现在的秒数, 和时区无关,无论在地球上的那个角落,同一时刻,UNIX时间戳都是一样的 。是一个通用的时间偏移度量,计算每个时区当地时间时,都可以用时间戳推算出来。

不同时区的时间,都用UTC时间的偏移来计算。例如北京是东八区,比UTC时间快8个小时,所以计算北京时间,就在UTC时间的基础上加8个小时实现。

我们在调用系统函数展示时间时,底层是根据UNIX时间戳转换为UTC时间,再加上偏移的小时数,就得出了程序要用的当地时间。

UNIX时间戳可以映射到每个时区的当地时间,如果程序涉及到两个时区的时间转换,最好的方法是存储UNIX时间戳,在使用的时候再做转换。

在各种语言的函数库中,都已经定义了时间时区转换的函数。在使用时,还有一点要注意「时区偏移(time offset)」和「时区地区(time zone)」是两个不同的概念。

偏移是一个数学上的值,直接能计算出时间。时区地区,会根据当地的法律规则,来得出最终的时间,混入了人为的规则。

例如:

在夏令时时,北京和纽约时差是12个小时,但是当夏令时结束时,北京和纽约的时差是11个小时。如果一直用固定的时间偏移,就会计算出错。如果用指定的地区当参数,就会根据当地规则返回正确时间。

具体例子见代码:

<?php                                                                              

date_default_timezone_set('Asia/Shanghai');                                        
$d=strtotime("2018-11-04 13:00:00");                                               
echo "Beijing " . date("Y-m-d h:i:sa", $d) . "\n";                                 

date_default_timezone_set('America/New_York');                                     
echo "NewYork " . date("Y-m-d h:i:sa", $d) . "\n";                                 

echo "\n";                                                                         

date_default_timezone_set('Asia/Shanghai');                                        
$d=strtotime("2018-11-04 14:00:00");                                               
echo "Beijing " . date("Y-m-d h:i:sa", $d) . "\n";                                 

date_default_timezone_set('America/New_York');                                     
echo "NewYork " . date("Y-m-d h:i:sa", $d) . "\n";                                 

echo "\n";                                                                                                                                          

date_default_timezone_set('Asia/Shanghai');                                        
$d=strtotime("2018-11-04 15:00:00");                                               
echo "Beijing " . date("Y-m-d h:i:sa", $d) . "\n";                                 

date_default_timezone_set('America/New_York');                                     
echo "NewYork " . date("Y-m-d h:i:sa", $d) . "\n";

输出结果

Beijing 2018-11-04 01:00:00pm   //没结束夏令时时,时差12个小时
NewYork 2018-11-04 01:00:00am

Beijing 2018-11-04 02:00:00pm   //夏令时切换,时差为11个小时
NewYork 2018-11-04 01:00:00am

Beijing 2018-11-04 03:00:00pm
NewYork 2018-11-04 02:00:00am

总结

  1. 涉及到多个时区的转换,统一使用unix时间戳存储或交互,或者使用带有时区信息的字符串。
  2. 尽量在上层的代码层面修改时区配置,不要修改系统或软件的配置,防止其他程序因为修改受到影响。

本质:时区概念是上层人为转换的概念,程序的逻辑不要依赖于他,要有个统一的时刻值概念来衡量真实的时间(例如UNIX时间戳),然后在上层做转换。

参考

https://zh.wikipedia.org/wiki/%E6%A0%BC%E6%9E%97%E5%B0%BC%E6%B2%BB%E6%A8%99%E6%BA%96%E6%99%82%E9%96%93

https://en.wikipedia.org/wiki/Unix_time

https://zh.wikipedia.org/wiki/%E5%8D%8F%E8%B0%83%E4%B8%96%E7%95%8C%E6%97%B6

https://zh.wikipedia.org/wiki/%E6%97%B6%E5%8C%BA

https://www.cnblogs.com/zihanxing/articles/6224263.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK