As.Posixct with Datetimes Including Midnight

R posixct dates and times not centering on midnight

I believe the following does what you want.

My locale is the following, so the results are different from yours.

Sys.getlocale("LC_TIME")
#[1] "Portuguese_Portugal.1252"

The difference will be due to the daylight savings time, the summer hour.

As for your problem, all you have to do is to remeber that the objects of class "POSIXct are coded as the number of seconds since an origin, and that origin is usually the midnight of 1970-01-01. So you have to add your seconds since midnight to the seconds of as.Date.

x <- "20180831"

xd <- mkdate(x)
y <- 10800

as.POSIXct(as.integer(xd) + y, origin = "1970-01-01")
#[1] "2018-08-31 04:00:00 BST"

as.POSIXct(as.integer(xd) + y, origin = "1970-01-01", tz = "America/Chicago")
#[1] "2018-08-30 22:00:00 CDT"

Convert posixct time format into 00:00:00

There is a round method for POSIX.ct

round(gg, units = "days")

To get it to display the 00:00:00

format(round(gg, units = "day"), '%Y-%m-%d %M:%H:%S')

Round has the benefit of being locale independent.

R - midnight POSIXct to date

You need to specify the timezone again.

As you can see in ?as.Date:

## S3 method for class 'POSIXct'

as.Date(x, tz = "UTC", ...)

The function defaults to UTC.

as.Date(as.POSIXct("2020-01-01 00:00:00",tz="CET"),tz="CET")
#[1] "2020-01-01"

as.Date(as.POSIXct("2020-01-01 00:00:00",tz="CET"))
#[1] "2019-12-31"

Weird POSIX behaviour for two closely time strings with and without specifying the format

  1. Why the time zone differs between the two lines

As said in the comments, it differs due to daylight savings. Since you don't include the zone in the call to as.POSIXct, you are prone to many problems. When at all possible, be explicit with timezone. This is a no-kidding moment: if you know it (and it is not part of the string), never assume it will be inferred correctly. In my experience, it will get it wrong enough to be really annoying and very difficult to detect, find, and fix.



  1. Why when no format is given it ignores the times' portion

It does not, though it might look like it. This is only a symptom of how it is printed, not stored. (This is common in many of R's functions, for instance how it shows pi with only a handful of decimal places while it is certainly storing many more. Without this "representation versus actual precision" model, R's console would be unnecessarily full of decimal places and such, all the time.)

If I update your code to explicitly include zone:

as.POSIXct(c('2017-03-24 02:59:59', '2017-03-24 03:00:00'), tz="Israel")
# [1] "2017-03-24 IST" "2017-03-24 IST"
as.POSIXct(c('2017-03-24 02:59:59', '2017-03-24 03:00:00'), tz="Israel") + 1
# [1] "2017-03-24 00:00:01 IST" "2017-03-24 00:00:01 IST"

In the second case, I added one second to the times, and you see the time is now there. You can look at the internals to see it in a different way:

dput(as.POSIXct(c('2017-03-24 02:59:59', '2017-03-24 03:00:00'), tz="Israel"))
# structure(c(1490306400, 1490306400), class = c("POSIXct", "POSIXt"
# ), tzone = "Israel")
dput(as.POSIXct(c('2017-03-24 02:59:59', '2017-03-24 03:00:00'), tz="Israel")+1)
# structure(c(1490306401, 1490306401), tzone = "Israel", class = c("POSIXct",
# "POSIXt"))

Times are stored as floating point numbers and a special class. Between the two (without and with a 1-second addition), you can see that the numbers are just off-by-one.

A third way to confirm is to take the "missing time" posix objects and explicitly print to something (which is no longer POSIXct, but it's just for demo):

a <- as.POSIXct(c('2017-03-24 02:59:59', '2017-03-24 03:00:00'), tz="Israel")
a
# [1] "2017-03-24 IST" "2017-03-24 IST"
format(a, format="the time is %Y-%m-%d %H:%M:%S")
# [1] "the time is 2017-03-24 00:00:00" "the time is 2017-03-24 00:00:00"


  1. Why does it fail to convert the first string when the format is specified?

As @Dave2e commented, according to the daylight savings conversions, that time "never happened".

According to https://www.timeanddate.com/time/change/israel/jerusalem?year=2017:

Mar 24, 2017 - Daylight Saving Time Started

When local standard time was about to reach
Friday, March 24, 2017, 2:00:00 am clocks were turned forward 1 hour to
Friday, March 24, 2017, 3:00:00 am local daylight time instead.

I interpret that to mean that the clock shifted from 01:59:59 to 03:00:00, so 02:**:** never happened. R is telling you with the NA that that time should not have occurred. There are certainly ways (hacks) you can infer that this is the case: find all NA values, then attempt to re-convert using plus or minus an hour; if the new value is not NA, then you found another instance where R thinks that time is not possible. If it is still NA, then there must be something else about the string (additional characters, different order, etc).

In my experience, I have not found this logic to ever be incorrect (though I don't know with certainty that it is flawless), even if it seems annoying. When I thought it might have been incorrect, I have always found something else that explained why I think I have that precise time:

  • data collection stored the wrong TZ
  • data collection failed to store the TZ, and I inferred incorrectly
  • some conversion in the pipeline mis-converted the times and/or zone(s)
  • likely something else I haven't rooted out

R lubridate package date-time creation omits time at midnight

So I just got my own answer to this question after google searches and error trying:

mydates <- format(as.POSIXct("2011-01-01 00:00:00", tz = "UTC"), "%m-%d-%Y %H:%M:%S")
mydates
[1] "01-01-2011 00:00:00"
format(as.POSIXct("2011-01-01 00:00:00", tz = "UTC"), "%m-%d-%Y %H:%M:%S")
[1] "01-01-2011 00:00:00"

It seems that using the R Base as.POSIXct with format arguments works.

How can I keep midnight (00:00h) using strptime() in R?

From R's strptime documentation (emphasis added):

format

A character string. The default for the format methods is "%Y-%m-%d %H:%M:%S" if any element has a time component which is not midnight, and "%Y-%m-%d" otherwise. If options("digits.secs") is set, up to the specified number of digits will be printed for seconds.

So the information is still there, you just need to format it to print it out with the time components.

> midnight <- strptime("2015-12-19 00:00:00","%Y-%m-%d %H:%M")
> midnight
[1] "2015-12-19 EST"
> format(midnight,"%Y/%m/%d %H:%M")
[1] "2015/12/19 00:00"


Related Topics



Leave a reply



Submit