在R中检查时区是否有效

6
我正在阅读一个包含时间戳和时区规范的文件。我想要检测该文件中给定的时区是否被R识别,并在其未被识别时提供自己的默认值。
然而,似乎当给定无效的时区时,as.POSIXct会默默地回退到UTC,没有错误或警告可供捕获和处理。请注意保留html标记。
> as.POSIXct("1970-01-01", tz="blah")
[1] "1970-01-01 UTC"

在R中,检查给定时区是否被识别的“正确”方法是什么?


1
根据?as.POSIXct的说明,请参考?"时区" - GSee
特别是 example('时区') - Matthew Plourde
4个回答

4
您可以使用一个具有时区知识的时区库。这是来自RcppBDT的SVN版本:
R> tz <- new(bdtTz, "America/Chicago")     
R> cat("tz object initialized as:       ", format(tz), "\n")    
tz object initialized as:        America/Chicago   
R>     tzBAD <- new(bdtTz, "blah")     
Error in new_CppObject_xp(fields$.module, fields$.pointer, ...) :    
  Unknown region supplied, no tz object created       
R>  

通常,时区支持取决于操作系统。因此,为了提供便携式解决方案,您需要从某个地方提供有效时区列表...
值得一提的是,我正在使用Boost源代码中的csv文件。那个时区文件的副本在这里 GitHub 上。

4

help("time zones")详细解释了许多有关时区的问题,值得一读。

结果会因您的操作系统不同而异,但如果您的操作系统存在zone.tab文件,则example("time zones")显示了如何读取该文件。

tzfile <- "/usr/share/zoneinfo/zone.tab"
tzones <- read.delim(tzfile, row.names = NULL, header = FALSE,
                     col.names = c("country", "coords", "name", "comments"),
                     as.is = TRUE, fill = TRUE, comment.char = "#")
str(tzones$name)
#chr [1:415] "Europe/Andorra" "Asia/Dubai" "Asia/Kabul" "America/Antigua" "America/Anguilla" ...

NROW(tzones)
#[1] 415

head(tzones)
#  country      coords             name comments
#1      AD +4230+00131   Europe/Andorra         
#2      AE +2518+05518       Asia/Dubai         
#3      AF +3431+06912       Asia/Kabul         
#4      AG +1703-06148  America/Antigua         
#5      AI +1812-06304 America/Anguilla         
#6      AL +4120+01950    Europe/Tirane   

2
Windows用户可以在R_HOME/share/zoneinfo/zone.tab找到zone.tab。 - Matthew Plourde

4

我刚好遇到了这个问题,因为我也在寻找相同的答案。结果发现可以使用以下方法。我将这个方法留下来,以便任何可能遇到这个问题的人参考。

is.valid.timezone <- function(timezone) {
  return(timezone %in% (OlsonNames()))
}

谢谢Denis!我认为这是大多数用户的最佳答案,因为它可以在baseR中完成,无需使用外部库或调用操作系统资源。 - Keegan Smith

1
你可以使用 Rmetrics 包的 timeDate 包来检查时区。
require(timeDate)

timeDate("1970-01-01", zone = "Africa/Dakar")
## [1] [1970-01-01]

timeDate("1970-01-01", zone = "blah")
## Error in .formatFinCenterNum(unclass(ct), zone, type = "any2gmt") : 
##  'blah' is not a valid FinCenter.

2
我认为只需使用 timeDate::listFinCenter() 即可,但它可能不是最新的,因为它只是存储在包中的向量。 - GSee
可能是这样,但我们没有任何证据表明它没有更新。因此,在Unix系统中带有时区的ASCII文件会更可靠。 - dickoa
a) 因为发行版可能会更新它。 b) 答案仅对运行此设置的子集机器有效,不包括其他Linux系统、Windows、OS X等。总之,这就是为什么R在其源代码中也提供了Olson数据库的副本的原因... - Dirk Eddelbuettel
@dickoa 目前 listFinCenter() 函数有397个时区,而 example('time zones') 函数有415个(至少在我的发行版中)。 - GSee
@GSee,能否将示例添加为答案? - Matthew Plourde
@GSee 我看到你是对的。我检查了一下,在我的发行版(Arch)中,我有418。谢谢。 - dickoa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接