我正在研究一个执行一些时间序列操作的软件。我最近发现了一个严重的问题在R脚本方面,我已经开发;意外行为被隔离在具有Europe/Moscow
语言环境的特定机器上。问题归结为以下代码片段:
strange_days <- c("2/1/1984", "3/1/1984", "4/1/1984", "5/1/1984", "6/1/1984")
Sys.setenv(TZ='Europe/Moscow')
d <- strptime(strange_days, '%m/%d/%Y')
d
[1] "1984-02-01 MSK" "1984-03-01 MSK" "1984-04-01" "1984-05-01 MSD" "1984-06-01 MSD"
所有似乎都能被正确识别。我认为,由于这是日常数据,时区属性没有太大区别;痛苦的错误:
as.numeric(d)
[1] 444430800 446936400 NA 452203200 454881600
在转换为xts
对象时明显失败。
当前的修复是通过strptime(strange_days, '%m/%d/%Y', tz='GMT')
甚至Sys.setenv(TZ='GMT')
强制所有时区为GMT;问题也随之消失了。
这是一个好的做法吗?代码在所有情况下都是可靠的吗?你会推荐什么技巧来避免类似的问题?
1984年4月1日发生了什么特别的事?
编辑:这个和这个问题表明这可能是夏令时导致的问题。
sessionInfo()
R version 3.1.0 (2014-04-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
locale:
[1] LC_COLLATE=English_United Kingdom.1252 LC_CTYPE=English_United Kingdom.1252 LC_MONETARY=English_United Kingdom.1252
[4] LC_NUMERIC=C LC_TIME=English_United Kingdom.1252
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] tools_3.1.0
编辑2:问题显然是windows特有的,而不是在linux上复制这些规格:
R version 3.1.0 (2014-04-10)
Platform: i686-pc-linux-gnu (32-bit)
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
loaded via a namespace (and not attached):
[1] tools_3.1.0
在本例中,由于您对时间不感兴趣,而只对日期感兴趣,因此可以使用as.Date
:
> as.Date(strange_days,"%m/%d/%Y")
[1] "1984-02-01" "1984-03-01" "1984-04-01" "1984-05-01" "1984-06-01"
你遇到的错误(正如你已经注意到的)很可能是由于夏令时:碰巧俄罗斯的DST在1984年特别开始于4月1日(来源)。
也就是说,在Mac OSX 10.7.5和R 2.14.2运行(是的,有点过时)这个错误是不可重复的:
> strange_days <- c("2/1/1984", "3/1/1984", "4/1/1984", "5/1/1984", "6/1/1984")
> Sys.setenv(TZ='Europe/Moscow')
> d <- strptime(strange_days, '%m/%d/%Y')
> d
[1] "1984-02-01 MSK" "1984-03-01 MSK" "1984-04-01 MSD" "1984-05-01 MSD" "1984-06-01 MSD"
> as.numeric(d)
[1] 444430800 446936400 449611200 452203200 454881600
这表明在R版本2.14.2和3.1.0之间对strptime
所做的一个更改修改了此行为。我目前正在Changelogs中寻找它,但我还没有明确的证据。另一种可能是它是特定于平台的。
此外,这里是?strptime
的摘录:
请记住,在大多数时区中,有些时间不会发生,有些时间会发生两次是因为夏令时的转换。Strptime没有验证这些时间(它不假设特定的时区),但是转换为as.POSIXct)将会这样做。通过strftime和格式化/打印使用操作系统的功能和可能(在Windows上也是如此)
在DST转换时返回不存在时间的无意义结果。