使用with_tz()将UTC转换为本地时间结果异常的问题排查
UTC时间转本地时间异常问题排查与解决
问题现象
有一个包含UTC时间的DataFrame,使用lubridate::with_tz()函数转换为指定时区的本地时间时出现异常:
- 伦敦时区(Europe/London)的结果比预期提前8小时(正确夏令时应为UTC+1)
- 美国中部时区(US/Central)本该向后调整,结果反而提前
原始数据与代码
初始DataFrame
library(lubridate) df
输出:
localtime utc timezone 1 NA 2022-06-02 19:00:00 CET 2 NA 2022-06-02 14:00:00 Europe/Bucharest 3 NA 2022-06-02 14:30:00 CET 4 NA 2022-06-02 16:00:00 CET 5 NA 2022-06-02 18:00:00 CET 6 NA 2022-06-02 16:30:00 Europe/London 7 NA 2022-06-02 21:00:00 US/Central
转换代码(出现错误的版本)
df$utc = as.POSIXct(df$utc, format="%Y-%m-%d %H:%M:%OS", tz = "UTC") df = df %>% rowwise() %>% mutate(localtime = with_tz(utc, timezone))
错误结果
localtime utc timezone 1 2022-06-03 03:00:00 2022-06-02 19:00:00 CET 2 2022-06-02 22:00:00 2022-06-02 14:00:00 Europe/Bucharest 3 2022-06-02 22:30:00 2022-06-02 14:30:00 CET 4 2022-06-03 00:00:00 2022-06-02 16:00:00 CET 5 2022-06-03 02:00:00 2022-06-02 18:00:00 CET 6 2022-06-03 00:30:00 2022-06-02 16:30:00 Europe/London 7 2022-06-03 05:00:00 2022-06-02 21:00:00 US/Central
dput原始数据
> dput(df) structure(list(localtime = c(NA, NA, NA, NA, NA, NA, NA), utc = c("2022-06-02 19:00:00", "2022-06-02 14:00:00", "2022-06-02 14:30:00", "2022-06-02 16:00:00", "2022-06-02 18:00:00", "2022-06-02 16:30:00", "2022-06-02 21:00:00"), timezone = c("CET", "Europe/Bucharest", "CET", "CET", "CET", "Europe/London", "US/Central")), row.names = 1:7, class = "data.frame")
问题原因
使用rowwise()配合with_tz()时,timezone列的字符值未被正确逐行传递给with_tz()的第二个参数,导致函数误用了当前会话的本地时区(而非指定时区),从而出现偏移错误。
解决方法
改用逐元素映射的方式处理,避免rowwise()的环境问题。以下两种方法均可:
方法1:使用purrr包的map2
library(lubridate) library(purrr) # 确保utc列是带UTC时区的POSIXct类型 df$utc <- as.POSIXct(df$utc, format="%Y-%m-%d %H:%M:%OS", tz = "UTC") # 逐行映射UTC时间和对应时区,生成正确的本地时间 df$localtime <- map2(df$utc, df$timezone, ~with_tz(.x, .y))
方法2:使用base R的mapply
library(lubridate) df$utc <- as.POSIXct(df$utc, format="%Y-%m-%d %H:%M:%OS", tz = "UTC") df$localtime <- mapply(with_tz, df$utc, df$timezone)
正确结果验证
执行后得到的正确localtime列应为:
1 2022-06-02 21:00:00(CET夏令时UTC+2) 2 2022-06-02 16:00:00(Europe/Bucharest夏令时UTC+3) 3 2022-06-02 16:30:00(CET夏令时UTC+2) 4 2022-06-02 18:00:00(CET夏令时UTC+2) 5 2022-06-02 20:00:00(CET夏令时UTC+2) 6 2022-06-02 17:30:00(Europe/London夏令时UTC+1) 7 2022-06-02 16:00:00(US/Central夏令时UTC-5)
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

