R 如何正确转换时区

R 如何正确转换时区,r,posixct,R,Posixct,我使用该软件包的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 GM

我使用该软件包的
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"
现在,假设我有一个datetimes以“America/Montral”时区表示的文件,计划是加载它们(隐式地假装它们以GMT表示),然后在不更改基础值的情况下修改timezone属性

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

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
了)

这是一个夏令时问题:

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"

这里要做的明智之举几乎可以肯定是编写可读、易于维护的代码,如果代码太慢,就投入更多硬件解决问题

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

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


montreal_tz_data您是否可以添加适当的秒数来更正与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"

所以,你有这样的
字符
输入:“2010-03-15 12:31:16.223 GMT”,你想要一个像什么的POSIXct对象?不,我在一个文件中有一个
字符
,看起来像
“2010-03-15 12:37:17.223”<代码> >我知道时区是“代码>美国/蒙特利尔< /代码>,但我想加载它并将其转换为<代码> POSIXCT/<代码> <代码> FASTIME>/代码>,这个包认为字符串代表“GMT”中的一个DATE时间,所以我必须告诉R,所得到的POSIXCT对象不是“GMT”,而是“美国/蒙特利尔”,您希望输出什么?i、 e.上面显示的
as.POSIXct
解决方案有什么问题?@Gsee:
as.POSIXct
很好,但问题是用更快的
fastPOSIXct
替换它我看到了你删除并更新函数的评论,但结果对我来说是一样的…@statquant:1。这个
fasttime
功能在哪里?请给包裹命名,以便我们能找到它。2.我猜你的意思是“美国/蒙特利尔”
。3.你看过
lubridate
中的
force_tz
了吗?@statquant它看起来像
格式
也可以转换为
POSIXlt
。我认为如果没有一些文本分析,你是无法做到这一点的,否则,如果您想使用仅使用
POSIXct
的纯数字解决方案,则需要计算并更正时区偏移量和DST更改。我们是否同意我唯一需要更改的是
timezone
属性,但如果我在POSIXct对象上访问它,则基础值会更改,如果我在POSIXlt对象上访问它,然后。。。我必须投哪一个是昂贵的?你可以用
attr
更改
POSIXct
tzone
。问题在于,当使用这个新时区显示时,它会重新计算时间,以及从unix纪元开始经过的秒数。在所讨论的日期,时间会跳跃1小时,因此许多秒后会有所不同,再加上tz偏移的因素。您还必须修改这些偏移量存储的秒数,以获得正确的数字。@statquant您还需要考虑夏令时吗?
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 
# 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"