Search code examples
rvectornumericposixctcoercion

Passing POSIXct object to function returns numeric vector


I'm trying to do some manipulation on the POSIXct vector, but when I pass it to a function, the vector changes into a numeric vector, instead of retaining POSIXct class, even when the function itself only returns the object:

# Sample dates from vector and it's class.
> dates <- as.POSIXct(c("2012-02-01 12:32:00", "2012-10-24 17:25:56", "2008-09-26 17:13:31", "2011-08-23 11:11:17,", "2015-09-19 22:28:33"), tz = "America/Los_Angeles")
> dates
[1] "2012-02-01 12:32:00 PST" "2012-10-24 17:25:56 PDT" "2008-09-26 17:13:31 PDT" "2011-08-23 11:11:17 PDT" "2015-09-19 22:28:33 PDT"
> class(dates)
[1] "POSIXct" "POSIXt" 
# Simple subset is retaining original class.
> qq <- dates[1:5]
> qq
[1] "2012-02-01 12:32:00 PST" "2012-10-24 17:25:56 PDT" "2008-09-26 17:13:31 PDT" "2011-08-23 11:11:17 PDT" "2015-09-19 22:28:33 PDT"
> class(qq)
[1] "POSIXct" "POSIXt" 
# sapply on the same subset using simple "return" function changes class to "numeric" - why? How to retain "POSIXct"?
> qq2 <- sapply(dates[1:5], function(x) x)
> qq2
[1] 1328128320 1351124756 1222474411 1314123077 1442726913
> class(qq2)
[1] "numeric"

Why it happens? How can I retain the POSIXct class of the original vector? I know that POSIXct is numeric under the hood, but I want to retain the original class for readability.


Solution

  • We can use lapply instead of sapply as sapply by default has the option simplify = TRUE. So, if the list elements are of the same length, it will simplify it to vector or matrix depending on the length of the list elements and POSIXct is stored as numeric.

    lst <- lapply(dates, function(x) x)
    

    If we need to use sapply, then an option would simplify = FALSE

    lst <- sapply(dates, function(x) x, simplify=FALSE)
    

    After applying the function, if we need as a vector output,

    do.call("c", lst)
    

    Regarding the change of timezone, it is documented in the ?DateTimeClasses

    Using c on "POSIXlt" objects converts them to the current time zone, and on "POSIXct" objects drops any "tzone" attributes (even if they are all marked with the same time zone).

    So, the possible option would be (as mentioned in the comments by @kmo)

    .POSIXct(lst, tz = "America/Los_Angeles")
    #[1] "2012-02-01 12:32:00 PST" "2012-10-24 17:25:56 PDT" "2008-09-26 17:13:31 PDT" "2011-08-23 11:11:17 PDT" "2015-09-19 22:28:33 PDT"
    

    Or as @thelatemail mentioned in the comments

    .POSIXct(sapply(dates,I), attr(dates,"tzone") )