如何正确转换时区

时间:2013-04-04 16:21:58

标签: r posixct

我正在使用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"

现在,假设我有一个日期时间以“America / Montral”时区表示的文件,计划是加载它们(隐含地假装它们在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点了)

3 个答案:

答案 0 :(得分:9)

在这里做的聪明的事情几乎肯定是编写可读,易于维护的代码,如果你的代码太慢,就会抛出更多硬件。

如果您急需代码加速,那么您可以编写自定义时区调整功能。它不漂亮,所以如果你必须在很多时区之间进行转换,你最终会得到意大利面条代码。这是我从GMT转换到蒙特利尔时间的具体案例的解决方案。

首先预先计算夏令时的日期列表。为了适合您的数据集,您需要将其扩展到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倍的加速(yay!)或者您节省了13秒的处理时间,但是当您运行DST表时,从一个不起眼的错误中添加了3天的代码维护时间在2035年(嘘!)。

答案 1 :(得分: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"

答案 2 :(得分: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"