2

我正在使用fasttime包的fastPOSIXct功能,它可以非常有效地读取字符日期时间。我的问题是它只能读取以 GMT 表示的字符日期时间。

R) fastPOSIXct("2010-03-15 12:37:17.223",tz="GMT") #very fast
[1] "2010-03-15 12:31:16.223 GMT"
R) as.POSIXct("2010-03-15 12:37:17.223",tz="GMT") #very slow
[1] "2010-03-15 12:31:16.223 GMT"

现在,假设我有一个日期时间以“美国/蒙特利尔”时区表示的文件,计划是加载它们(隐含地假装它们在 GMT 中)并随后修改时区属性而不更改基础值。

如果我使用此功能,请参阅另一篇文章:

forceTZ = function(x,tz){   
    return(as.POSIXct(as.numeric(x), origin=as.POSIXct("1970-01-01",tz=tz), tz=tz))
}

我看到一个错误...

R) forceTZ(as.POSIXct("2010-03-15 12:37:17.223",tz="GMT"),"America/Montreal")
    [1] "2010-03-15 13:37:17.223 EDT"

...因为我希望它是

R) as.POSIXct("2010-03-15 12:37:17.223",format="%Y-%m-%d %H:%M:%OS",tz="America/Montreal")
    [1] "2010-03-15 12:37:17.223 EDT"

有解决方法吗?

编辑:我知道lubridate::force_tz但它太慢了(不再使用fasttime::fastPOSIXct

4

3 回答 3

9

在这里要做的聪明的事情几乎可以肯定是编写可读、易于维护的代码,如果你的代码太慢,就会在问题上投入更多的硬件。

如果您迫切需要代码加速,那么您可以编写自定义时区调整函数。它不漂亮,所以如果你必须在多个时区之间转换,你最终会得到意大利面条代码。这是我从格林威治标准时间转换为蒙特利尔时间的具体情况的解决方案。

首先预先计算夏令时的日期列表。您需要将此扩展到 2010 年之前/2013 年之后,以适应您的数据集。我在这里找到了日期

http://www.timeanddate.com/worldclock/timezone.html?n=165

montreal_tz_data <- cbind(
  start = fastPOSIXct(
    c("2010-03-14 07:00:00", "2011-03-13 07:00:00", "2012-03-11 07:00:00", "2013-03-10 07:00:00")
  ),
  end   = fastPOSIXct(
    c("2010-11-07 06:00:00", "2011-11-06 06:00:00", "2012-11-04 06:00:00", "2013-11-03 06:00:00")
  )
)

对于速度,更改时区的功能将时间视为数字。

to_montreal_tz <- function(x)
{
  x <- as.numeric(x)
  is_dst <- logical(length(x))  #initialise as FALSE
  #Loop over DST periods in each year
  for(row in seq_len(nrow(montreal_tz_data)))
  {
    is_dst[x > montreal_tz_data[row, 1] & x < montreal_tz_data[row, 2]] <- TRUE
  }
  #Hard-coded numbers are 4/5 hours in seconds
  ans <- ifelse(is_dst, x + 14400, x + 18000)
  class(ans) <- c("POSIXct", "POSIXt")
  ans
}

现在,比较时间:

#A million dates
ch <- rep("2010-03-15 12:37:17.223", 1e6)
#The easy way (no conversion of time zones afterwards)
system.time(as.POSIXct(ch, tz="America/Montreal"))
#   user  system elapsed 
#  28.96    0.05   29.00 

#A slight performance gain by specifying the format
system.time(as.POSIXct(ch, format = "%Y-%m-%d %H:%M:%S", tz="America/Montreal"))
#   user  system elapsed 
#  13.77    0.01   13.79 

#Using the fast functions
library(fasttime)
system.time(to_montreal_tz(fastPOSIXct(ch)))    
#    user  system elapsed 
#    0.51    0.02    0.53 

与所有优化技巧一样,您要么获得了 27 倍的加速(耶!),要么您节省了 13 秒的处理时间,但是当您的 DST 表在 2035 年用完时,由于一个晦涩的错误增加了 3 天的代码维护时间(嘘!)。

于 2013-04-10T16:09:55.667 回答
4

这是一个夏令时问题:http ://www.timeanddate.com/time/dst/2010a.html

2010 年,它于 3 月 14 日在加拿大开始,但直到 3 月 28 日才在英国开始。

您可以使用POSIXlt对象直接修改时区:

lt <- as.POSIXlt(as.POSIXct("2010-03-15 12:37:17.223",tz="GMT"))
attr(lt,"tzone") <- "America/Montreal"
as.POSIXct(lt)
[1] "2010-03-15 12:37:17 EDT"

或者您可以使用format转换为字符串并在调用中设置时区as.POSIXct. 因此,您可以修改forceTZ

forceTZ <- function(x,tz)
{
  return(as.POSIXct(format(x),tz=tz))
}


forceTZ(as.POSIXct("2010-03-15 12:37:17.223",tz="GMT"),"America/Montreal")
[1] "2010-03-15 12:37:17 EDT"
于 2013-04-04T16:49:28.320 回答
1

你能不能只添加适当的秒数来纠正与 GMT 的偏移量?

# Original problem
fastPOSIXct("2010-03-15 12:37:17.223",tz="America/Montreal")
# [1] "2010-03-15 08:37:17 EDT"

# Add 4 hours worth of seconds to the data. This should be very quick.
fastPOSIXct("2010-03-15 12:37:17.223",tz="America/Montreal") + 14400
# [1] "2010-03-15 12:37:17 EDT"
于 2013-04-11T13:24:24.927 回答