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

时间:2016-05-16 16:09:06

标签: r time

我有一个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"

这些日期时间标记为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

我想将这些日期时间转换为适当的UTC - 在某种意义上,面临herehere的反问题 - 但是我很难过。来自第二个链接的解决方案的变体有效:

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)

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

但由于这不是矢量化的,因此在具有50万行的data.frame上速度非常慢。有更优雅,更快捷的方法吗?

2 个答案:

答案 0 :(得分:10)

最具官方的官员&#39;我知道的方式涉及格式化和重新分析;大卫史密斯had a post on this a while ago在REvolutions博客上。

时间序列库,特别是那些具有时区感知能力的库,也可以做到。这是一种使用RcppCCTZ的方法,它是我的CCTZ包装器(由一些Google员工编写但不是官方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"])

这让我们得到了这个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> 

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

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

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

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
}

让我们对data.table执行相同的操作:

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

请注意,这会返回三列而不是一列。

虽然我们正在努力,但让我们进行一场赛马:

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> 

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

答案 1 :(得分:2)

dplyr + lubridate解决方案似乎有效且速度非常快:

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中的datetime_local位于AEST中,可能看起来并不像您期望的那样。我怀疑这是由于R的限制,POSIXct向量中的所有元素必须具有相同的时区。因此datetime_localAEST

强制ungroup