lubridate round_date() in R
The lubridate round_date() function rounds a date or date-time in R to the nearest time unit, such as the nearest hour, day, week, or month.
round_date(x, "hour") # nearest hour round_date(x, "day") # nearest day round_date(x, "minute") # nearest minute round_date(x, "15 minutes") # nearest 15-minute mark round_date(x, "month") # nearest month start round_date(x, "week") # nearest week round_date(x, "week", week_start = 1) # weeks start Monday
Need explanation? Read on for examples and pitfalls.
What round_date() does in one sentence
round_date() snaps a timestamp to the closest boundary of a chosen unit. You pass a date or date-time and a unit string, and it returns the same object type rounded to the nearest hour, day, week, or any other supported unit. Unlike floor_date() and ceiling_date(), which always move in one direction, round_date() picks whichever boundary is nearer.
Here 15:47:30 is past the half-hour mark, so the nearest hour boundary is 16:00:00. The result keeps the original POSIXct class and time zone.
Syntax and the unit argument
The function signature is short, but the unit argument carries all the flexibility. The full form is round_date(x, unit = "second", week_start = getOption("lubridate.week.start", 7)).
| Argument | Purpose |
|---|---|
x |
A date, date-time, or vector of them |
unit |
A string naming the rounding unit |
week_start |
Day a week begins on (7 = Sunday, 1 = Monday) |
The unit string accepts "second", "minute", "hour", "day", "week", "month", "bimonth", "quarter", "halfyear", and "year". You can also prefix a number to round to a multiple, such as "15 minutes" or "2 hours".
The "day" result lands on March 15 because 15:47 is past noon. The "month" result rounds back to March 1 because the 14th sits in the first half of the month.
round_date() measures the distance to the boundary below and the boundary above, then returns the closer one. That single rule explains every result, including the surprising ones.Rounding to a multiple of a unit
Prefix the unit with a number to round to custom intervals. This is the feature people reach for when bucketing sensor data, log timestamps, or appointment slots into regular blocks.
At 15:47:30, the nearest 15-minute mark is 15:45 (only 2.5 minutes away versus 12.5 to the next). The nearest 10-minute mark is 15:50. Multiples make round_date() a one-line replacement for hand-written modulo arithmetic.
round_date() vs floor_date() vs ceiling_date()
All three share the same unit argument but differ in rounding direction. floor_date() always moves down, ceiling_date() always moves up, and round_date() moves to whichever is closer.
| Function | Direction | Typical use |
|---|---|---|
floor_date() |
Down to start of unit | Group rows into completed periods |
ceiling_date() |
Up to next boundary | Deadlines, next billing date |
round_date() |
Nearest boundary | Snap noisy timestamps to a clean grid |
Use round_date() when the timestamp is approximate and you want the closest tidy value. Use floor_date() or ceiling_date() when the direction matters for correctness.
Common pitfalls
Three mistakes account for most confusing round_date() results.
Noon is exactly halfway through the day, so the result is a tie. round_date() resolves ties by rounding up to the later boundary, which surprises people expecting the earlier one.
The second pitfall is forgetting week_start. Rounding to "week" uses Sunday as the default first day. Pass week_start = 1 for ISO Monday-based weeks.
The third is assuming the time zone changes. It does not. round_date() rounds the clock time in the existing zone and never converts it.
"7 minutes" do not tile an hour cleanly, so boundaries reset each hour and rounding near the top of the hour can look uneven. Stick to divisors such as 5, 10, 15, or 30 minutes.round_date() to create the grouping key, then summarise. Rounding the aggregated output instead produces misaligned buckets.Try it yourself
Try it: Round the timestamp ymd_hms("2025-01-20 08:40:00") to the nearest 30 minutes. Save the result to ex_rounded.
Click to reveal solution
Explanation: 08:40:00 is 10 minutes past the 08:30 mark and 20 minutes before 09:00, so the nearest 30-minute boundary is 08:30:00.
Related lubridate functions
These functions pair well with round_date() for date-time work in R:
floor_date()rounds a timestamp down to the start of a unit.ceiling_date()rounds a timestamp up to the next boundary.rollback()jumps to the last moment of the previous month.ymd_hms()parses a character string into a date-time.hour()andday()extract a single component from a timestamp.
FAQ
What is the difference between round_date and floor_date in R?
floor_date() always rounds a timestamp down to the start of the chosen unit, while round_date() rounds to whichever boundary is closer. For 15:47:30 rounded to the hour, floor_date() returns 15:00 but round_date() returns 16:00 because the time is past the half-hour mark. Use floor_date() when you specifically need the period start, and round_date() when you want the nearest clean value.
How does round_date handle exact halfway times?
When a timestamp falls exactly on the midpoint between two boundaries, round_date() rounds up to the later one. Noon rounded to the nearest day returns the next day, and 30 seconds rounded to the nearest minute returns the next minute. If you need ties to break downward, use floor_date() instead, or shift the input slightly before rounding.
Can round_date round to 15 minutes or other multiples?
Yes. Prefix the unit string with a number, such as "15 minutes", "2 hours", or "10 seconds". round_date() then snaps the timestamp to the nearest multiple of that interval. For predictable results, choose multiples that divide their parent unit evenly, like 5, 10, 15, or 30 minutes within an hour.
Does round_date change the time zone of a date?
No. round_date() rounds the clock time within the existing time zone and returns the same zone it received. If you need a different zone, convert it first with with_tz() or force_tz(), then round. The class of the object, such as POSIXct or Date, is also preserved.
How do I round a date to the nearest week starting on Monday?
Pass week_start = 1 along with unit = "week". By default round_date() treats Sunday as the first day of the week (week_start = 7). Setting week_start = 1 switches to ISO-style Monday-based weeks, which matches most business reporting calendars.