Search code examples
rtimeposixlubridate

System date in Posixlt and Posixct


I am trying to get the last minute of yesterday using Sys.Date() in Posix time.

force_tz(as.POSIXlt(Sys.Date()-1), tz = 'America/New_York') + 86399
# [1] "2018-01-12 23:59:59 EST" 

CORRECT

force_tz(as.POSIXct(Sys.Date()-1), tz = 'America/New_York') + 86399
# [1] "2018-01-12 15:59:59 EST"

INCORRECT

Sys.Date()
# [1] "2018-01-13"

Why does as.Posixct and as.Posixlt return two different values using Sys.Date() and why is the difference 8 hours even after applying force_tz from lubridate ?


Solution

  • As ever, debugonce is your friend. Running debugonce(force_tz), you can see that the difference in output comes from when force_tz hits the branches checking first is.POSIXct(time) (in which case the default tzone = "" is applied); in the POSIXlt case, the default branch is hit, where as.POSIXct is applied to time and tz(time) (which comes out as UTC for a POSIXlt object) is used as the time zone.


    This comes down to something subtle happening; from ?as.POSIXlt.Date:

    Dates without times are treated as being at midnight UTC.

    Hence

    tz(as.POSIXlt(Sys.Date()-1))
    # [1] "UTC"
    

    But

    tz(as.POSIXct(Sys.Date()-1))
    # [1] ""
    

    What's peculiar is this can't be overridden -- as.POSIXlt.Date doesn't accept a tz argument:

    formals(as.POSIXlt.Date)
    # $x
    # $...
    

    If you want to use POSIXct, how about the following?

    force_tz(as.POSIXct(sprintf('%s 00:00:00', Sys.Date())), 'America/New_York') - 1L
    # [1] "2018-01-12 23:59:59 EST"