将当地时间的矢量转换为UTC

Roy*_*lTS 4 time r

我有一个POSIXct稍微误用该格式的向量:

> head(df$datetime)
[1] "2016-03-03 12:30:00 UTC" "2016-03-03 12:00:00 UTC" "2016-02-27 09:00:00 UTC" "2016-03-03 17:30:00 UTC"
[5] "2016-03-03 10:30:00 UTC" "2016-03-03 14:30:00 UTC"
Run Code Online (Sandbox Code Playgroud)

这些日期时间标记为UTC时间,但实际上是各种各样的本地时区:

> df %>% select(datetime, timezone) %>% head
         datetime            timezone
1 2016-03-03 12:30:00 Australia/Melbourne
2 2016-03-03 12:00:00 Europe/Berlin
3 2016-02-27 09:00:00 Europe/Amsterdam
4 2016-03-03 17:30:00 Australia/Brisbane
5 2016-03-03 10:30:00 Europe/Amsterdam
6 2016-03-03 14:30:00 Europe/Berlin
Run Code Online (Sandbox Code Playgroud)

我想将这些日期时间转换为适当的UTC - 从某种意义上说,这里这里面临的逆问题- 但是我很难过.来自第二个链接的解决方案的变体工作:

get_utc_time <- function(timestamp_local, local_tz) {
  l <- lapply(seq(length(timestamp_local)), 
              function(x) {with_tz(force_tz(timestamp_local[x], tzone=local_tz[x]), tzone='UTC')})
  as.POSIXct(combine(l), origin = '1970-01-01 00:00.00', tz = 'UTC')
}

df$datetime_utc <- get_utc_time(df$datetime, df$timezone)
Run Code Online (Sandbox Code Playgroud)

(dplyr::mutate(df, datetime_utc = get_utc_time(datetime, timezone))我认为这是等价的,会引发错误.)

但由于这不是矢量化的,因此在具有50万行的data.frame上速度非常慢.有没有更优雅,更快的方式来做到这一点?

Dir*_*tel 10

我所知道的最"官方"方式涉及格式化和重新分析; 不久前,David Smith 在REvolutions博客上发表了一篇文章.

时间序列库,特别是那些具有时区感知能力的库,也可以做到.这是一种使用RcppCCTZ的方法,它是我的CCTZ包装器(由一些Googler编写但不是官方Google库) - 它计算两个时区之间的差异(默认以小时为单位).

library(RcppCCTZ)  # you need the GitHub version though

# your data
df <- read.csv(text="datetime,timezone
2016-03-03 12:30:00,Australia/Melbourne
2016-03-03 12:00:00,Europe/Berlin
2016-02-27 09:00:00,Europe/Amsterdam
2016-03-03 17:30:00,Australia/Brisbane
2016-03-03 10:30:00,Europe/Amsterdam
2016-03-03 14:30:00,Europe/Berlin", stringsAsFactor=FALSE)

# parse to POSIXct
df[,"pt"] <- as.POSIXct(df[,"datetime"])

# compute difference
for (i in 1:6) 
    df[i,"diff"] <- tzDiff("UTC", df[i,"timezone"], df[i,"pt"])
Run Code Online (Sandbox Code Playgroud)

这让我们得到了这个data.frame:

R> df
             datetime            timezone                  pt diff
1 2016-03-03 12:30:00 Australia/Melbourne 2016-03-03 12:30:00   11
2 2016-03-03 12:00:00       Europe/Berlin 2016-03-03 12:00:00    1
3 2016-02-27 09:00:00    Europe/Amsterdam 2016-02-27 09:00:00    1
4 2016-03-03 17:30:00  Australia/Brisbane 2016-03-03 17:30:00   10
5 2016-03-03 10:30:00    Europe/Amsterdam 2016-03-03 10:30:00    1
6 2016-03-03 14:30:00       Europe/Berlin 2016-03-03 14:30:00    1
R> 
Run Code Online (Sandbox Code Playgroud)

返回解析的Datetime偏移量也很简单,但是小辅助函数tzDiff当前没有这样做.如果你想走这条路,我可以把它作为第二个辅助函数添加...

编辑: 这是一个有趣的问题.我现在已经向RcppCCTZ添加了一些代码来执行此操作,但它(至少)没有矢量化.也就是说,我们可以使用data.table提供更简单,更快速的解决方案.

让我们首先对您的解决方案及其所需的三个包进行编码:

library(lubridate)
library(magrittr)
library(dplyr)
useLubridate <- function(df) {
    df %>%
        group_by(timezone) %>%
        mutate(datetime_local = ymd_hms(datetime, tz=unique(timezone))) %>%
        mutate(datetime_utc = with_tz(datetime_local, tzone = 'UTC')) %>% 
        ungroup %>%
        select(datetime_local) -> df
    df
}
Run Code Online (Sandbox Code Playgroud)

我们对data.table做同样的事情:

library(data.table)
useDataTable <- function(df) {
    dt <- as.data.table(df)
    dt[, pt := as.POSIXct(datetime, tz=timezone[1]), by=timezone] 
    dt[]
}
Run Code Online (Sandbox Code Playgroud)

请注意,这将返回三列而不是一列.

当我们在这里时,让我们做一场赛马:

R> library(microbenchmark)
R> microbenchmark( useDataTable(df), useLubridate(df) )
Unit: milliseconds
             expr     min      lq    mean  median      uq      max neval cld
 useDataTable(df) 1.23148 1.53900 1.61174 1.57635 1.64734  3.85423   100  a 
 useLubridate(df) 7.51158 8.88734 9.10439 9.19390 9.38032 15.27572   100   b
R> 
Run Code Online (Sandbox Code Playgroud)

因此data.table更快,同时还返回更多有用的信息.将第三列整理回data.frame(或相似)将花费更多时间.

  • 如果您查看函数签名,则会将"from"和"to"时区作为日期(时间)对象,以计算差异.也就是我在芝加哥这里正确地确定了每年三个星期,柏林的"你的"夏令时与我的不同.实际上,`example(tzDiff)`显示*恰好*(尽管在纽约和伦敦之间). (2认同)