3

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.

Geraldine
  • 771
  • 3
  • 9
  • 23
  • how does starttime change into testtime ? is it a typo ? do you do the same mistake in your real code ? – moodymudskipper Jun 27 '18 at 00:09
  • 2
    `round(as.POSIXct(starttime, format="%H:%M:%S", tz="UTC"), units="hours")` - R will automatically dispatch to the proper `round.POSIXt` - you are forcing it to try to work with a `Date` object, which you don't have - you have a `datetime`. – thelatemail Jun 27 '18 at 00:12
  • @Moody_Mudskipper Apologies, that was a typo that is not in my actual code. My data is more complicated than what is posted here, so in making the example with the vector, I failed to update the object name. Updated now. – Geraldine Jun 27 '18 at 00:15
  • @thelatemail Ha, yes, that works... Could you post that as a proper answer so I can accept it? And maybe also include what the difference is between a `Date` and a `datetime` object? I hadn't heard of the latter, and Google is not super helpful. But - why would the base R function not work, which uses `round.Date()` and `POSIXct()`? – Geraldine Jun 27 '18 at 00:22

1 Answers1

3

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).

thelatemail
  • 91,185
  • 12
  • 128
  • 188
  • Related follow-up question: what's the difference between your solution and my `round.Date(as.POSIXct(as.character(starttime), format="%H:%M:%S"), units="hours")`? – Geraldine Jun 29 '18 at 17:41