`as.POSIXct` get error with `"%Y-%m-%d %H:%M:%S"` format

Viewed 531
dates <- seq(1626629937,1626629944)

# CORRECT

## #1
as.POSIXct(dates,                    tz="Asia/Shanghai",origin="1970-01-01")
#> [1] "2021-07-19 01:38:57 CST" "2021-07-19 01:38:58 CST" "2021-07-19 01:38:59 CST" "2021-07-19 01:39:00 CST"
#> [5] "2021-07-19 01:39:01 CST" "2021-07-19 01:39:02 CST" "2021-07-19 01:39:03 CST" "2021-07-19 01:39:04 CST"

## #2
as.POSIXct(dates,                    tz="Asia/Shanghai",origin="1970-01-01",optional = FALSE)
#> [1] "2021-07-19 01:38:57 CST" "2021-07-19 01:38:58 CST" "2021-07-19 01:38:59 CST" "2021-07-19 01:39:00 CST"
#> [5] "2021-07-19 01:39:01 CST" "2021-07-19 01:39:02 CST" "2021-07-19 01:39:03 CST" "2021-07-19 01:39:04 CST"


# DIFFERENT RESULT

## #3
as.POSIXct(dates,"%Y-%m-%d %H:%M:%S"                   ,origin="1970-01-01")
#> [1] "2021-07-18 17:38:57" "2021-07-18 17:38:58" "2021-07-18 17:38:59" "2021-07-18 17:39:00" "2021-07-18 17:39:01"
#> [6] "2021-07-18 17:39:02" "2021-07-18 17:39:03" "2021-07-18 17:39:04"


# NAs

## #4
as.POSIXct(dates,"%Y-%m-%d %H:%M:%S",tz="Asia/Shanghai",origin="1970-01-01")
#> [1] NA NA NA NA NA NA NA NA

## #5
as.POSIXct(dates,"%Y-%m-%d %H:%M:%S",tz="Asia/Shanghai",origin="1970-01-01",optional = FALSE)
#> [1] NA NA NA NA NA NA NA NA


# ERROR

## #6
as.POSIXct(dates,"%Y-%m-%d %H:%M:%S"                                       ,optional = FALSE)
#>  Error in as.POSIXct.numeric(as.integer(.), "%Y-%m-%d %H:%M:%S", optional = FALSE) : 
#>   'origin' must be supplied 

As output of above R script,format "%Y-%m-%d %H:%M:%S" with tz,origin,optional parameters cause NA.

Where is the problem?

1 Answers

The easy ones first:

  • optional = FALSE is the default: therefore #1 == #2 and #4 == #5
  • #6 needs no explanation: you need the argument origin = as the error states
  • #3 returns different results because of the time zone (the tz= argument). Therefore, it shows 8 hours before.

Now, the problem is #4 and #5 (which are the same as I stated before):

as.POSIXct(dates,"%Y-%m-%d %H:%M:%S",tz="Asia/Shanghai",origin="1970-01-01")
#> [1] NA NA NA NA NA NA NA NA

To understand how this works you need to look at the function as.POSIXct, which, when called with a numeric x (like in this case), calls the method: as.POSIXct.numeric.

as.POSIXct.numeric

#> function (x, tz = "", origin, ...) 
#> {
#>     if (missing(origin)) {
#>         if (!length(x)) 
#>             return(.POSIXct(numeric(), tz))
#>         if (!any(is.finite(x))) 
#>             return(.POSIXct(x, tz))
#>         stop("'origin' must be supplied")
#>     }
#>     .POSIXct(as.POSIXct(origin, tz = "GMT", ...) + x, tz)
#> }
#> <bytecode: 0x55df7f23b390>
#> <environment: namespace:base>

Focus on this line:

#> .POSIXct(as.POSIXct(origin, tz = "GMT", ...) + x, tz)

In particular:

as.POSIXct(origin, tz = "GMT", ...) + x

As you see, the function transforms origin in datetime and then it sums the numeric input you imputed. Every additional argument you provided falls into ....

The function tries to convert 1970-01-01 to datetime using the format you provided: %Y-%m-%d %H:%M:%S. Since the origin 1970-01-01 has format %Y-%m-%d, the function can't convert the origin from string to POSIX, thus returning NA. (That's where NAs are generated!)

When you convert a numeric to POSIX, the format you add as argument doens't apply to the output (since it will be always a POSIX) nor to the input, rather to the origin. Thus, origin and format need to match.

To solve your problem, you need to use origin with the format %Y-%m-%d %H:%M:%S. Like this:

as.POSIXct(dates,"%Y-%m-%d %H:%M:%S",tz="Asia/Shanghai",origin="1970-01-01 00:00:00")
#> [1] "2021-07-19 01:38:57 CST" "2021-07-19 01:38:58 CST" "2021-07-19 01:38:59 CST" "2021-07-19 01:39:00 CST"
#> [5] "2021-07-19 01:39:01 CST" "2021-07-19 01:39:02 CST" "2021-07-19 01:39:03 CST" "2021-07-19 01:39:04 CST"

Or you need to use this format: %Y-%m-%d Like this:

as.POSIXct(dates,"%Y-%m-%d",tz="Asia/Shanghai",origin="1970-01-01")
#> [1] "2021-07-19 01:38:57 CST" "2021-07-19 01:38:58 CST" "2021-07-19 01:38:59 CST" "2021-07-19 01:39:00 CST"
#> [5] "2021-07-19 01:39:01 CST" "2021-07-19 01:39:02 CST" "2021-07-19 01:39:03 CST" "2021-07-19 01:39:04 CST"

The results are then equal to #1 and #2.

Related