Geraldine
Geraldine

Reputation: 821

R: rounding time to the nearest hour

I have a vector with time (from an Excel file). It gets read in as a factor. This is a short version/example of it:

starttime <- factor(c("12:55:00", "13:45:00", "14:30:00", "10:00:00", "10:40:00", "12:00:00", "12:30:00"))

I wanted to round all these times to the nearest hour in military time and extract the hour as an integer, and I ran into issues trying to do it with a package or a base function. I used the lubridate package, and tried:

round_date(hms(starttime), unit= "hour")

hms seems to work fine on its own (I indeed get a my data as class Period), but then using it in combination with round_date gives me the error

Error in as.POSIXct.numeric(x) : 'origin' must be supplied

I then tried the following (1904 because it's data from Excel/csv file on a Mac)

round_date(hms(starttime, origin="1904-01-01"), unit="hour")

but that gave me the exact same error, so I gathered I specified the origin in the wrong function. Next try was

round_date(hms(starttime), unit="hour"), origin="1904-01-01")

which gave me the error

Error in round_date(hms(starttime), unit = "hour", origin = "1904-01-01") : unused argument (origin = "1904-01-01")

I also tried base R:

round.Date(as.POSIXct(as.character(starttime), format="%H:%M:%S"), units="hours")

gave me

Error in NextMethod() : generic function not specified

Also here, the conversion of the vector to dates works, but the trouble starts when the rounding function is added.

Now, I do totally understand why this might not work using lubridate, as there are no dates associated with the times (so the timer under the hood can't do its thing). I'm a little confused though about the error message that an origin is needed but then it is ignored. I can't use ymd_hms() because there is no data in the vector for dates. And I can't figure out what the error for the base R function means at all - where there are actually dates added (the current date).

I ended up solving this manually:

ifelse(minute(hms(starttime))>=30, hour(hms(starttime))+1, hour(hms(starttime)))

Is there a way to do this with a function lubridate or base R? If not, any ideas why not? It seems to me like something that should be possible.

Upvotes: 3

Views: 4349

Answers (1)

thelatemail
thelatemail

Reputation: 93938

Try a slight modification to what you have:

round(as.POSIXct(starttime, format="%H:%M:%S", tz="UTC"), units="hours")
#[1] "2018-06-27 13:00:00 UTC" "2018-06-27 14:00:00 UTC"
#[3] "2018-06-27 15:00:00 UTC" "2018-06-27 10:00:00 UTC"
#[5] "2018-06-27 11:00:00 UTC" "2018-06-27 12:00:00 UTC"
#[7] "2018-06-27 13:00:00 UTC"

R will automatically dispatch to the proper round.POSIXt when you call round with an as.POSIXct/as.POSIXlt object. In your original code, you are forcing round to try to work with a Date object, which you don't have - you have a POSIXct datetime object.

A Date in R is generally just "year/month/day", no time component, as opposed to a POSIXct which is a "year/month/day hh/mm/ss/fractional seconds." The two are not directly interchangeable or comparable. E.g.:

> Sys.time() == Sys.Date()
#[1] FALSE
#Warning message:
#Incompatible methods ("Ops.POSIXt", "Ops.Date") for "==" 

(If you want to get technical, a Date is stored as the number of days since 1970-01-01 - including fractional days, and a POSIXct is the number of seconds since 1970-01-01:00:00:00 - including fractional seconds. A POSIXlt is different again, and is a series of list objects, with parts for day,month,year,minutes etc).

Upvotes: 5

Related Questions