33

I'm currently playing around a lot with dates and times for a package I'm building.

Stumbling across this post reminded me again that it's generally not a bad idea to check out if something can be done with basic R features before turning to contrib packages.

Thus, is it possible to round a date of class POSIXct with base R functionality?

I checked

methods(round)

which "only" gave me

[1] round.Date      round.timeDate*

   Non-visible functions are asterisked

This is what I'd like to do (Pseudo Code)

x <- as.POSIXct(Sys.time())
[1] "2012-07-04 10:33:55 CEST"

round(x, atom="minute")
[1] "2012-07-04 10:34:00 CEST"

round(x, atom="hour")
[1] "2012-07-04 11:00:00 CEST"

round(x, atom="day")
[1] "2012-07-04 CEST"

I know this can be done with timeDate, lubridate etc., but I'd like to keep package dependencies down. So before going ahead and checking out the source code of the respective packages, I thought I'd ask if someone has already done something like this.

Community
  • 1
  • 1
Rappster
  • 11,680
  • 7
  • 58
  • 113

3 Answers3

46

base has round.POSIXt to do this. Not sure why it doesn't come up with methods.

x <- as.POSIXct(Sys.time())
x
[1] "2012-07-04 10:01:08 BST"
round(x,"mins")
[1] "2012-07-04 10:01:00 BST"
round(x,"hours")
[1] "2012-07-04 10:00:00 BST"
round(x,"days")
[1] "2012-07-04"
James
  • 61,307
  • 13
  • 140
  • 186
  • great function, but strangely doesnt have week, month, or year rounding; for my situation I'll have to go with @nzcoops answer – chepyle Sep 30 '15 at 04:28
  • 5
    Furthermore, round converts POSIXct to POSIXlt - which sometimes can be a problem. – Pere Mar 23 '18 at 14:50
  • 1
    ... which can be solved by wrapping the output in `as.POSIXct()`, if desired. – fdetsch Feb 14 '20 at 04:20
15

On this theme with lubridate, also look into the ceiling_date() and floor_date() functions:

x <- as.POSIXct("2009-08-03 12:01:59.23")
ceiling_date(x, "second")
# "2009-08-03 12:02:00 CDT"
ceiling_date(x, "hour")
# "2009-08-03 13:00:00 CDT"
ceiling_date(x, "day")
# "2009-08-04 CDT"
ceiling_date(x, "week")
# "2009-08-09 CDT"
ceiling_date(x, "month")
# "2009-09-01 CDT"
RoyalTS
  • 7,796
  • 7
  • 45
  • 90
nzcoops
  • 8,524
  • 8
  • 36
  • 52
  • 2
    I wish the month rounding was available for POSIXct, because floor_date is painfully slow. – Alex A. Nov 11 '15 at 13:15
  • the question explicitly specify "I know this can be done with timeDate, lubridate etc., but I'd like to keep package dependencies down.". – Simon C. Dec 27 '18 at 10:04
1

If you don't want to call external libraries and want to keep POSIXct as I do this is one idea (inspired by this question): use strptime and paste a fake month and day. It should be possible to do it more straight forward, as said in this comment

"For strptime the input string need not specify the date completely: it is assumed that unspecified seconds, minutes or hours are zero, and an unspecified year, month or day is the current one."

Thus it seems that you have to use strftime to output a truncated string, paste the missing part and convert again in POSIXct.

This is how an update answer could look:

x <- as.POSIXct(Sys.time())
x
[1] "2018-12-27 10:58:51 CET"
round(x,"mins")
[1] "2018-12-27 10:59:00 CET"
round(x,"hours")
[1] "2018-12-27 11:00:00 CET"
round(x,"days")
[1] "2018-12-27 CET"
as.POSIXct(paste0(strftime(x,format="%Y-%m"),"-01"))     #trunc by month
[1] "2018-12-01 CET"
as.POSIXct(paste0(strftime(x,format="%Y"),"-01-01"))     #trunc by year
[1] "2018-01-01 CET"
Simon C.
  • 718
  • 7
  • 29