我正在使用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
点了)
答案 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"