You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, clock assumes that Date is UTC, because it seemed like this is also what base R assumed. I have now learned it is not quite that simple, and base R is actually inconsistent, treating Date as UTC or naive depending on the conversion being done
Date -> POSIXct/lt, Date is treated as UTC (i.e. sys-time)
POSIXct/lt -> Date, Date is treated as naive-time
Sys.Date(), Date is treated as naive-time
lubridate makes all of the same assumptions as base R, just with more intuitive defaults on a case-by-case basis. I still think it could be improved.
References:
?as.POSIXct.Date states "Dates without times are treated as being at midnight UTC"
?as.Date.POSIXct "(POSIXct and POSIXlt are) converted to days by ignoring the time after midnight in the representation of the time in specified time zone, default UTC"
?Sys.Date states "Sys.Date returns the current day in the current time zone."
Potential date_cast(x) and date_time_cast(x, zone, ..., nonexistent = , ambiguous =) to be more consistent replacements for as.Date() and as.POSIXct()
It seems like it may be more intuitive if Date was treated as naive-time at all times.
# Date -> POSIXct# - Date is treated as UTC# - Resulting POSIXct uses the local time zone# - No `tz` argumentx<- as.Date("2019-01-01")
x#> [1] "2019-01-01"# Date was treated as UTC, result shown in local time
as.POSIXct(x)
#> [1] "2018-12-31 19:00:00 EST"clock::date_set_zone(as.POSIXct(x), "UTC")
#> [1] "2019-01-01 UTC"as.POSIXct.Date#> function (x, ...) #> .POSIXct(unclass(x) * 86400)#> <bytecode: 0x7f8d54999538>#> <environment: namespace:base># Date was treated as UTC, defaults to showing UTC time# (same assumptions as R, just smarter defaults)lubridate::as_datetime(x)
#> [1] "2019-01-01 UTC"lubridate::as_datetime(x, tz="America/New_York")
#> [1] "2018-12-31 19:00:00 EST"# I'd argue that the above is probably still confusing,# the user probably wanted to assume `x` was a naive-timelubridate::force_tz(x, "America/New_York")
#> [1] "2019-01-01 EST"
# Date -> POSIXlt# - Date is treated as UTC# - Resulting POSIXlt uses UTC# - No `tz` argumentx<- as.Date("2019-01-01")
x#> [1] "2019-01-01"
as.POSIXlt(x)
#> [1] "2019-01-01 UTC"as.POSIXlt.Date#> function (x, ...) #> .Internal(Date2POSIXlt(x))#> <bytecode: 0x7f8d14cbf2f8>#> <environment: namespace:base>
# POSIXct -> Date# - Date is treated as naive# - The current printed time of the POSIXct is converted to match `tz` without# changing the underlying duration (defaulting `tz` to UTC)# - The new printed time is converted to a Date using `as.Date.POSIXlt`# - `tz = "UTC"` by defaultx<- as.POSIXct("2019-01-01 23:00:00", tz="America/Los_Angeles")
x#> [1] "2019-01-01 23:00:00 PST"# - `tz = "UTC"` by default# - Printed time converted from America/Los_Angeles->UTC, then that is converted# to a Date, making the assumption that Date is a naive time
as.Date(x)
#> [1] "2019-01-02"# it is like doing:clock::date_set_zone(x, "UTC")
#> [1] "2019-01-02 07:00:00 UTC"
as.Date(clock::date_set_zone(x, "UTC"))
#> [1] "2019-01-02"# This is generally what the user probably was expecting. # - Keep the printed time of the original POSIXct# - Just drop the time part and return a Date# - Let Date be a naive type
as.Date(x, tz=clock::date_zone(x))
#> [1] "2019-01-01"as.Date.POSIXct#> function (x, tz = "UTC", ...) #> {#> if (tz == "UTC") {#> z <- floor(unclass(x)/86400)#> attr(z, "tzone") <- NULL#> .Date(z)#> }#> else as.Date(as.POSIXlt(x, tz = tz))#> }#> <bytecode: 0x7f8d1497c248>#> <environment: namespace:base># Uses `as.Date()` from above, but defaults to the time zone of `x`, as the# user probably expected# (same assumptions as R, just smarter defaults)lubridate::as_date(x)
#> [1] "2019-01-01"lubridate::as_date(x, tz="UTC")
#> [1] "2019-01-02"# I think that lubridate `as_date()` is right here, it:# - Treats Date as naive-time# - Uses the time zone of `x` by default when converting# However, I don't think the `tz` arg should be here,# as it is probably more confusing than helpful
# POSIXlt -> Date# - Date is treated as naive# - The printed time of `x` is retained, with the time being dropped# - No `tz` argumentx<- as.POSIXlt("2019-01-01 23:00:00", tz="America/Los_Angeles")
x#> [1] "2019-01-01 23:00:00 PST"
as.Date(x)
#> [1] "2019-01-01"as.Date.POSIXlt#> function (x, ...) #> .Internal(POSIXlt2Date(x))#> <bytecode: 0x7f8d14cbc4b0>#> <environment: namespace:base>
Currently, clock assumes that Date is UTC, because it seemed like this is also what base R assumed. I have now learned it is not quite that simple, and base R is actually inconsistent, treating Date as UTC or naive depending on the conversion being done
Sys.Date(), Date is treated as naive-timeReferences:
?as.POSIXct.Datestates "Dates without times are treated as being at midnight UTC"?as.Date.POSIXct"(POSIXct and POSIXlt are) converted to days by ignoring the time after midnight in the representation of the time in specified time zone, default UTC"?Sys.Datestates "Sys.Date returns the current day in the current time zone."This begs the question, what is most useful for clock? A few places this comes up:
as.Date.clock_zoned_timeandas_zoned_time.Datedate_today(zone = )(Computed the current zoned-time, then convert to a Date, must assume naive or UTC) (Implementdate_now()anddate_today()#197)date_cast(x)anddate_time_cast(x, zone, ..., nonexistent = , ambiguous =)to be more consistent replacements foras.Date()andas.POSIXct()It seems like it may be more intuitive if Date was treated as naive-time at all times.
Created on 2021-04-06 by the reprex package (v1.0.0)