防止意外的时区转换



在R中,我有一堆用GMT测量的日期时间值。我经常遇到这样的意外:某个函数或另一个函数丢失了我的值上的时区,甚至丢失了类名。即使是像c()unlist()这样基本的函数:

> dput(x)
structure(1317830532, class = c("POSIXct", "POSIXt"), tzone = "GMT")
> dput(c(x))
structure(1317830532, class = c("POSIXct", "POSIXt"))
> dput(list(x))
list(structure(1317830532, class = c("POSIXct", "POSIXt"), tzone = "GMT"))
> dput(unlist(list(x)))
1317830532
如果在我最不期望的时候发生这种情况,我觉得我离真正的火星气候轨道器时刻只有一步之遥。有人有什么方法能让约会对象"待在原地"吗?

此行为在?c, ?DateTimeClasses?unlist中有记录:

From ?DateTimeClasses:

在" POSIXlt "对象上使用c将其转换为当前时区,而在" POSIXct "对象上删除任何" tzone "属性(即使它们都标记为相同的时区)。*

From ?c:

c有时用于去除除名称以外的属性的副作用。*


也就是说,我的测试表明,尽管使用cunlist,数据的完整性仍然完好无损。例如:

x <- structure(1317830532, class = c("POSIXct", "POSIXt"), 
                 tzone = "GMT")
y <- structure(1317830532+3600, class = c("POSIXct", "POSIXt"), 
                 tzone = "PST8PDT")
x
[1] "2011-10-05 16:02:12 GMT"
y
[1] "2011-10-05 10:02:12 PDT"
strftime(c(x, y), format="%Y/%m/%d %H:%M:%S", tz="GMT")
[1] "2011/10/05 16:02:12" "2011/10/05 17:02:12"
strftime(c(x, y), format="%Y/%m/%d %H:%M:%S", tz="PST8PDT")
[1] "2011/10/05 09:02:12" "2011/10/05 10:02:12"
strftime(unlist(y), format="%Y/%m/%d %H:%M:%S", tz="PST8PDT")
[1] "2011/10/05 10:02:12"

如果你用R来记录日期,你的火星探测器应该没问题。

为什么不将R会话的时区设置为GMT呢?如果某些内容被转换为"当前"时区,它仍然是正确的。

考虑到这是有文档记录的行为,应该避免这样的函数,或者围绕这样的行为编写防御性代码,那么您需要支持这两种方法的机制。对于这样的事情,我建议你写一篇"穷人的绒毛";有了这样一个绒毛探测器,你就可以恢复正常了。此外,对于绒毛探测,有几种方法可以避免火星极地轨道器坠毁,有些是相互独立的,有些是相互依赖的:

  1. 设置策略&首先,对于您知道会给您带来问题的所有函数,要么决定不使用它们,要么编写一个新的包装器函数,它将按照预期的方式运行,并将设置所需的时区参数。然后,确保使用特殊的包装器而不是底层函数。
  2. 静态分析使用shell脚本,使用您最喜欢的编辑器(例如作为宏)编写搜索函数&GNU findgrep函数,或以其他方式(例如R中的grep),找到那些导致您出现问题的特定函数。当发现时,删除或使用防御性编码方法(例如#1中的包装器)。
  3. Testing使用单元测试,例如Runittestthat,开发测试以确保在使用函数或包时维护时区属性。每次有一个新的错误,创建一个新的测试,以确保错误不会再次出现在发布版本。
  4. 弱类型检查您还可以在整个代码中包含测试,以测试是否指定了时区。最好为这个测试提供自己的函数,而不是编写一段从头到尾重复的代码。通过这种方式,您最终可以扩展检查以包括其他类型的检查,例如时区的持久性,以及对两个或多个对象的操作是否注意到时区差异的测试(可能允许,也可能不允许)。
  5. 将所有内容映射到一个TZ也被称为印第安纳该死的。保留关于时区的各种策略是一项艰巨的工作,并且实质上是处理时间数据时的摩擦。只需映射到一个TZ (UTC),然后让任何本地工作。如果你碰巧有DST不变的本地规则,那么在从UTC转换回来后解决它。

我在其他问题上做了所有的# 1-4,但是,就像它们很容易适应时区检查一样,它们对于许多避免火星轨道的目标来说是相当可重复使用的。我这样做是为了避免给下一个火星轨道飞行器编码。(对于我们所有处理数值数据的人来说,这是一个代价高昂的教训。:))

最新更新