R: if_else 和时区强制

R: if_else and timezone forcing

我有一个奇怪的问题(也许我遗漏了什么?),试图在 if_else 内强制时区(因为 ifelse 似乎不能很好地处理 POSIXct) .好像只在TRUE时才强制,FALSE时才转换。为什么?如何解决?

library(lubridate)
library(dplyr)
some_date = ymd_hm("2020-06-01 17:45", tz = "America/New_York")

if_else(TRUE, force_tz(some_date, tz = "GMT"), force_tz(some_date, tz = "Singapore"))
[1] "2020-06-01 17:45:00 GMT"

if_else(FALSE, force_tz(some_date, tz = "GMT"), force_tz(some_date, tz = "Singapore"))
[1] "2020-06-01 09:45:00 GMT"

我希望得到与 运行 force_tz 相同的结果:

# if TRUE
force_tz(some_date, tz = "GMT")
[1] "2020-06-01 17:45:00 GMT"

# if FALSE
force_tz(some_date, tz = "Singapore")
[1] "2020-06-01 17:45:00 +08"

谢谢!

罪魁祸首在于dplyr::if_else如何进行调整。

首先,我最初关于向量和 TZ 的评论仍然有效,并且仍然是这个问题的核心。备案:

When you're dealing with POSIXt in a vector, the TZ is an attribute of the whole vector, not each independent element. This means that either (a) you must accept that all timestamps within a vector will have the same TZ; or (b) you need to adapt your process to deal with a list of timestamps, in which case each time can have its own TZ.

如果你看if_else

function (condition, true, false, missing = NULL) 
{
    if (!is.logical(condition)) {
        bad_args("condition", "must be a logical vector, not {friendly_type_of(condition)}")
    }
    out <- true[rep(NA_integer_, length(condition))]

用第一个 ("true") 向量的 NA 变体预填充 out 向量。 (这是必要的,因为R确实有至少6种NA:逻辑(NA),整数(NA_integer_),real/float(NA_real_),字符串 (NA_character_)、日期 (c.Date(NA)) 和时间 (c.POSIXct(NA));因此 如何 形成一个向量 NA很重要。)但是,一旦 NAs 的向量被预填充,就会意识到这是基于第一个向量,所以它的属性被带入 out 向量。

Sys.time()
# [1] "2020-06-01 09:02:06 PDT"
now <- Sys.time()
attr(now, "tzone") <- "GMT"
dput(now)
# structure(1591027335.41804, class = c("POSIXct", "POSIXt"), tzone = "GMT")
dput(now[NA])
# structure(NA_real_, class = c("POSIXct", "POSIXt"), tzone = "GMT")

(看看tzone=怎么还是一样)。这意味着输出向量(在 POSIXt 个向量上运行时)will always carry forward the TZ of thetrueargument toif_else`.

从这里开始,if_else 替换 中工作(使用其内部 replace_with,实际上只是 out[condition] <- false[condition])。替换 不会 影响 TZ;事实上,false 时间的数字等价物在不考虑其 TZ 的情况下被同化。当然,false 向量的 "absolute time in the world" 被保留。

唯一的解决方法是更改​​您的工作流程以处理 POSIXtlist 而不是向量。 if_else 仍然在那里工作。

now
# [1] "2020-06-01 16:02:15 GMT"
now1 <- list(now, now+1) ; now2 <- list(now+86400, now+86401)
now1
# [[1]]
# [1] "2020-06-01 16:02:15 GMT"
# [[2]]
# [1] "2020-06-01 16:02:16 GMT"
now2
# [[1]]
# [1] "2020-06-02 16:02:15 GMT"
# [[2]]
# [1] "2020-06-02 16:02:16 GMT"
attr(now1[[2]], "tzone") <- "Singapore"
attr(now2[[2]], "tzone") <- "US/Pacific"
now1
# [[1]]
# [1] "2020-06-01 16:02:15 GMT"
# [[2]]
# [1] "2020-06-02 00:02:16 +08"
now2
# [[1]]
# [1] "2020-06-02 16:02:15 GMT"
# [[2]]
# [1] "2020-06-02 09:02:16 PDT"
if_else(c(TRUE, FALSE), now1, now2)
# [[1]]
# [1] "2020-06-01 16:02:15 GMT"
# [[2]]
# [1] "2020-06-02 09:02:16 PDT"