data.table::hour()函数是否存在不稳定问题?
data.table的
hour()函数处理2014年3月31日时间戳异常问题分析与解决 问题原因
这个异常和夏令时切换直接相关。2014年欧盟地区的夏令时从3月30日开始,时钟在凌晨2点直接跳至3点,也就是说2014-03-31 03:08:07这个时间在默认时区(如CET/CEST)是不存在的无效时间戳。
在早期data.table版本中,单个处理无效时间时hour()会返回0;当向量中混入无效时间戳时,整个向量化计算逻辑会被破坏,导致所有结果返回0,这属于版本bug。
复现代码
library(data.table) someTimestamps = c("2014-03-30 03:08:07", "2014-03-30 09:08:07", "2014-03-31 03:08:07", # 无效时间戳 "2014-03-31 09:08:07", "2014-04-01 03:08:07", "2014-04-01 09:08:07") # 单个处理时,无效时间返回0 for (i in 1:6) print(hour(someTimestamps[i])) # 输出: # [1] 3 # [1] 9 # [1] 0 # [1] 9 # [1] 3 # [1] 9 # 向量处理时全部返回0 print(hour(someTimestamps)) # 输出 [1] 0 0 0 0 0 0 # 移除无效时间后恢复正常 print(hour(someTimestamps[c(1:2, 4:6)])) # 输出 [1] 3 9 9 3 9
解决方案
- 升级data.table版本:后续版本已修复该bug,升级后向量化计算不会完全失效,无效时间会返回
NA而非0,且不影响其他有效时间的结果。 - 指定无夏令时时区:转换时间戳时使用UTC时区(无夏令时切换问题):
ts_utc = as.POSIXct(someTimestamps, tz = "UTC") hour(ts_utc) # 所有时间正常返回对应小时 - 提前过滤无效时间:先验证时间有效性,只处理合法时间戳:
ts = as.POSIXct(someTimestamps) valid_ts = !is.na(ts) hour(ts[valid_ts]) # 仅处理有效时间 - 字符串提取替代:如果无法升级包,可通过字符串提取获取小时(适合格式固定的时间戳):
sub(".* (\\d{2}):.*", "\\1", someTimestamps) |> as.integer()
补充说明
minute()等其他时间提取函数也会出现类似问题,本质都是无效时间戳触发的内部逻辑异常。更换年份后功能正常,是因为其他年份的夏令时切换日期不同,该时间点在对应年份属于有效时间。
内容的提问来源于stack exchange,提问作者GerasimosPanagiotakopoulos
相关产品推荐
相关产品推荐

