11.4 时区

从编程的角度来看,时区往往可怕而复杂。很多国家常有好几个时区,而且当一些(但不是全部)国家切换到夏令时需要改变边界。许多时区都有缩写名称,但它们又不是唯一的。例如,“EST”可以是美国、加拿大以及澳大利亚的“东部标准时间”。

在解析日期字符串时(使用strptime),你可以指定一个时区;当你格式化它时(使用strftime),可以再次改变它。在解析过程中,如果不指定时区(默认为“”),R会给日期以默认时区。这个值是Sys.timezone返回的,它会从你的操作系统区域设置猜到该值。你可以使用Sys.getlocale("LC_TIME")来查看操作系统的日期时间设定。

要避免这种时区的混乱,最简单的方法是随时记录,然后以UTC时区分析你的时间。如果你能做到这一点,恭喜!你很幸运。对于其他人,例如那些需要处理他人数据的人,最容易理解和方便的指定时区的方法是使用Olson形式,即“洲/市”或类似的方式:

  1. strftime(now_ct, tz = "America/Los_Angeles")
  2. ## [1] "2013-07-17 14:47:01"
  3. strftime(now_ct, tz = "Africa/Brazzaville")
  4. ## [1] "2013-07-17 22:47:01"
  5. strftime(now_ct, tz = "Asia/Kolkata")
  6. ## [1] "2013-07-18 03:17:01"
  7. strftime(now_ct, tz = "Australia/Adelaide")
  8. ## [1] "2013-07-18 07:17:01"

使用file.path(R.home("share"), "zoneinfo", "zone.tab")能查找出R中所有可能的Olson时区列表(这是名为zone.tab的文件位于zoneinfo文件夹中,此文件夹就在你安装R的共享目录内)。本章后文将介绍lubridate包,它将告诉你如何快速访问此文件。

另一个可靠的方法是给UTC手动添加一个偏移量,格式为"UTC"+n""UTC"-n"。负的时间在UTC的东边,正的在西边。手动操作至少让你很清楚时间是如何被改变的,但同时你必须手动修正夏令时,因此这种方法须谨慎使用。最近版本的R会警告时区是未知的,但仍会正确执行此偏移操作:

  1. strftime(now_ct, tz = "UTC-5")
  2. ## Warning: unknown timezone 'UTC-5'
  3. ## [1] "2013-07-18 02:47:01"
  4. strftime(now_ct, tz = "GMT-5") #同样的结果
  5. ## Warning: unknown timezone 'GMT-5'
  6. ## [1] "2013-07-18 02:47:01"
  7. strftime(now_ct, tz = "-5") # 如果操作系统支持,结果也是一样
  8. ## Warning: unknown timezone '-5'
  9. ## [1] "2013-07-18 02:47:01"
  10. strftime(now_ct, tz = "UTC+2:30")
  11. ## Warning: unknown timezone 'UTC+2:30'
  12. ## [1] "2013-07-17 19:17:01"

指定时区的第三个方法是使用缩写,可以是三个字母或三个字母加一个数字再加三个字母的方式。这种方法不到最后不要使用,原因有三个:首先,缩写难以阅读,更容易出错;其次,正如前面提到的,它们不是唯一的,所以给出的时区可能不是你所要的;最后,不同的操作系统支持不同的缩写集。尤其对于Windows操作系统来说,它对时区缩写的处理比较别扭:

  1. strftime(now_ct, tz = "EST") # 加拿大东部时间
  2. ## [1] "2013-07-17 16:47:01"
  3. strftime(now_ct, tz = "PST8PDT") # 太平洋标准夏令时时间
  4. ## [1] "2013-07-17 14:47:01"

最后的关于时区的警告是:strftime将忽略POSIXlt类型的时区变更。最好在打印之前就显式把日期转换成POSIXct类型:

  1. strftime(now_ct, tz = "Asia/Tokyo")
  2. ## [1] "2013-07-18 06:47:01"
  3. strftime(now_lt, tz = "Asia/Tokyo") # 时区没有变化!
  4. ## [1] "2013-07-17 22:47:01"
  5. strftime(as.POSIXct(now_lt), tz = "Asia/Tokyo")
  6. ## [1] "2013-07-18 06:47:01"

还有一个最后警告(真的是最后一个啦!):如果你调用带有POSIXlt参数的连接函数c,它会把时区更改为当地时区。相反,如果把c函数作用于POSIXct参数上,将彻底去除其时区属性。(大多数其他的函数都将假定日期是本地时区的,但要小心!)