【问题标题】:Modifying timezone of a POSIXct object without changing the display修改 POSIXct 对象的时区而不更改显示
【发布时间】:2013-03-12 15:02:24
【问题描述】:

我有一个POSIXct 对象,并且想更改它的tz 属性而不用R 来解释它(解释它意味着更改日期时间在屏幕上的显示方式)。

一些背景知识:我正在使用来自 S.Urbanek 的 fasttime 包,它可以非常快速地将字符串转换为 POSIXct。问题是字符串应该代表“GMT”中的日期时间,而我的数据不是这样。

我最终得到一个带有tz=GMTPOSIXct 对象,如果我用

attr(datetime, "tzone") <- "Europe/Paris";
datetime  <- .POSIXct(datetime,tz="Europe/Paris"); 

那么它将被“显示”为GMT+2(基础值永远不会改变)。

编辑:这是一个例子

datetime=as.POSIXct("2011-01-01 12:32:23.234",tz="GMT")
attributes(datetime)
#$tzone
#[1] "GMT"
datetime
#[1] "2011-01-01 12:32:23.233 GMT"

如何在没有 R 的情况下更改此属性来解释它,也就是如何更改 tzone 并仍然将日期时间显示为 "2011-01-01 12:32:23.233"

编辑/解决方案,@GSee 的解决方案相当快,lubridate::force_tz 非常慢

datetime=rep(as.POSIXct("2011-01-01 12:32:23.234",tz="GMT"),1e5)
f <- function(x,tz) return(as.POSIXct(as.numeric(x), origin="1970-01-01", tz=tz))
> system.time(datetime2 <- f(datetime,"Europe/Paris"))
   user  system elapsed 
   0.01    0.00    0.02 
> system.time(datetime3 <- force_tz(datetime,"Europe/Paris"))
   user  system elapsed 
   5.94    0.02    5.98 
identical(datetime2,datetime3)
[1] TRUE

【问题讨论】:

  • lubridate::force_tz
  • 新用户应注意,您不能在 data.frame 的单个列中拥有多个时区。
  • 请注意,您的函数f 与@GSee 的最新答案不对应,因为起源不一定相同。当我用你的ff(datetime[1], tz="Europe/Paris") 时,我得到2011-01-01 13:32:23 CET。所以在f 我认为你应该有origin = as.POSIXct("1970-01-01", tz=tz)。此外,force_tz 现在似乎更快了。

标签: r timezone posixct


【解决方案1】:

要更改POSIXct 变量的tz 属性,最好先转换为字符或数字,然后再转换回POSIXct。相反,您可以使用 lubridate 包的 force_tz 函数

library(lubridate)

datetime2 <- force_tz(datetime, tzone = "CET")
datetime2
attributes(datetime2)

【讨论】:

  • 不,我的意思是更改 tz 属性而不更改日期时间的显示方式
  • 啊,好吧,我改了答案。使用 lubridate 包非常容易。
  • 该死的太慢了,看我的编辑!
  • lubridate:::force_tz 很慢,因为它至少要调用 7 次 as.POSIXlt!我猜你说转换为数字并返回不是最佳做法,因为looks like a bug。如果那不是错误,那么您可能有道理;否则,POSIXct 对象已经是数字,因此多次转换为 POSIXlt(列表)肯定不是最佳实践。
  • 仔细观察后,force_tz 将输入转换为as.POSIXlt,然后在POSIXlt 对象上进行后续的as.POSIXlt 调用——因此它不会多次从数字转换为列表很好。仍然有很多方法调度开销,但它肯定比我乍一看要好。
【解决方案2】:

已编辑:

我之前的解决方案是将字符值传递给origin(即origin="1970-01-01")。这只适用于这里,因为现在 R-devel 中存在 been fixed 的错误 (#PR14973)。

origin 使用tz 调用的tz 参数被强制转换为POSIXct,而不是"GMT",因为它被记录在案。行为已更改以匹配文档,在这种情况下,这意味着您必须为 originas.POSIXct 调用指定您的时区。

datetime
#[1] "2011-01-01 12:32:23.233 GMT"
as.POSIXct(as.numeric(datetime), origin=as.POSIXct("1970-01-01", tz="Europe/Paris"),
           tz="Europe/Paris")
#[1] "2011-01-01 12:32:23.233 CET"

这也适用于旧版本的 R。

【讨论】:

  • 这会做 OP 想要的吗?时区之间的夏令时差异如何?我可以看到,当转换当时不在 DST 中的不同时区时,一个时区中 DST 中的日期时间会显示一个小时。
  • DST 无关紧要,因为原始时间是 GMT(没有 DST),目标是保持时间不变,假装它是不同的时区,IIUC跨度>
  • 此解决方案适用于“GMT”、“Europe/Paris”对,但不适用于“EST”、“UTC”(R-2.15.3、64 位、Win7)。但是,force_tz 给出了正确的结果。
  • @cryo111 你需要阅读?timezone。 “请注意,其中一些名称可能与您的想法不同:特别是'EST'是加拿大使用的时区没有夏令时,而不是'EST5EDT'或(澳大利亚)东部标准时间。”
  • @cryo111 我明白了。你是对的。我认为这是 lubridate 正在做的另一种方式:x &lt;- as.POSIXlt(datetime); attr(x, "tzone") &lt;- "UTC"; as.POSIXct(x)
【解决方案3】:

lubridate 包的替代方法是通过字符类型之间的转换:

recastTimezone.POSIXct <- function(x, tz) return(
  as.POSIXct(as.character(x), origin = as.POSIXct("1970-01-01"), tz = tz))

(改编自 GSee 的回答)

不知道这是否有效,但它适用于夏令时的时区。

测试代码:

x <- as.POSIXct('2003-01-03 14:00:00', tz = 'Etc/UTC')
x
recastTimezone.POSIXct(x, tz = 'Australia/Melbourne')

输出:

[1] "2003-01-03 14:00:00 UTC"
[1] "2003-01-03 14:00:00 AEDT" # Nothing is changed apart from the time zone.

如果我将 as.character() 替换为 as.numeric()(正如 GSee 所做的那样),则输出:

[1] "2003-01-03 14:00:00 UTC"
[1] "2003-01-03 15:00:00 AEDT" # An hour is added.

【讨论】:

  • 这是此页面上对我来说最好的答案。我最初没有 POSIXct 格式的时间,所以我可以使用类似as.POSIXct(datetime.character.format, format = "%Y%m%d %H:%M", tz = "EST5EDT", origin = "1970-01-01")
猜你喜欢
  • 1970-01-01
  • 2014-11-14
  • 1970-01-01
  • 1970-01-01
  • 2021-09-05
  • 2019-11-07
  • 2014-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多