lubridate duration() in R: Exact Time Spans in Seconds
The duration() function in lubridate builds a Duration object that stores a time span as an exact number of seconds. Use it when you need second-precise arithmetic that ignores calendar quirks like daylight saving, leap days, and variable month lengths.
duration(60) # 60 seconds duration("2 hours 30 mins") # parse a friendly string duration(num = 1, units = "days") # one exact day = 86400s ddays(7) # helper: 7 exact days dweeks(2) + dhours(6) # add two durations ymd_hms("2024-03-09 22:00", tz = "US/Eastern") + ddays(1) # exact 86400s shift as.duration(difftime(t2, t1)) # convert difftime to Duration as.numeric(duration("1 week"), "hours") # 168
Need explanation? Read on for examples and pitfalls.
What duration() does in one sentence
duration() constructs an exact, second-precise time span stored as a Duration object. Pass a number of seconds, a string like "2 hours 30 mins", or a numeric value with an explicit units argument and lubridate returns a Duration vector that always represents the same elapsed clock seconds.
A Duration treats one day as exactly 86,400 seconds and one year as exactly 31,557,600 seconds (365.25 days). It never asks the calendar what month or time zone you are in, which makes it the right primitive for scientific or financial calculations where exactness matters more than human readability.
Syntax
duration(num = NULL, units = "seconds", ...) accepts a numeric value plus a unit, or a single descriptive string. When you pass a string, lubridate parses words like seconds, minutes, hours, days, weeks, months, and years into the corresponding second count.
The print method shows the raw second count plus a human-friendly approximation. The "s" suffix is the visual signal that distinguishes a Duration from a Period, which prints with "d", "H", "M", "S" parts.
duration() when "the same number of seconds" is the right semantic. Stopwatches, network timeouts, log-file deltas, and physics simulations all care about elapsed seconds, not calendar dates. Use a Period when "next month, same day of month" is what you really mean.Six common patterns
1. Build a Duration from a numeric value
Negative numbers are valid and represent a backward span. duration(-3600) subtracts one hour when added to a time stamp.
2. Parse a friendly string
The parser is permissive about spacing and unit abbreviations. When in doubt, pass the unit explicitly with num= and units= to avoid surprises.
3. Use the d-prefix helper family
The helpers are syntactic sugar that read more naturally inside expressions. ddays(7) is identical to duration(7, "days") and to duration(7 * 86400).
days(7) (a Period of 7 calendar days that respects DST) with ddays(7) (a Duration of exactly 7 * 86,400 seconds). The two diverge near time zone transitions and on leap seconds.4. Add a Duration to a POSIXct timestamp
The two results differ by one hour because the US "spring forward" jump happens that night. ddays(1) adds 86,400 seconds literally; days(1) adds one calendar day and lets the clock follow the time zone rules.
5. Get a Duration from the difference of two timestamps
Subtracting two POSIXct values yields a base R difftime. Wrap it in as.duration() to get a lubridate Duration with consistent units.
6. Vectorise across a column
as.duration() is vectorised, so it works inside mutate() or any column-wise assignment without a loop.
Duration vs Period: when exact seconds matter
Use a Duration for elapsed clock seconds; use a Period for human calendar units. The two classes look similar in print but answer different questions about time.
| Concept | Duration | Period |
|---|---|---|
| Stored as | Exact seconds | Calendar units (years, months, days, ...) |
| Constructor | duration(), ddays(), dhours() |
period(), days(), hours() |
| One year equals | 31,557,600 s (365.25 days) | Variable (365 or 366 days) |
| DST behaviour | Adds raw seconds, clock shifts | Respects time zone rules |
| Right for | Stopwatches, log gaps, science | "Same day next month" semantics |
The rule of thumb: if you care that the answer is the same elapsed time on a stopwatch, use a Duration. If you care that the answer lands on a particular calendar date, use a Period.
Date value directly because Date has no time component. Coerce the Date with as.POSIXct() first, or use a Period (days(N)) when you only need calendar-day arithmetic.Common pitfalls
Assuming dyears(1) lands on the same calendar date next year. It does not. A Duration year is 365.25 days, so adding dyears(1) to ymd("2024-02-29") produces 2025-03-01 06:00:00 rather than a calendar-aligned date. Use years(1) (a Period) when you want "same month, same day, next year".
Treating a Duration like a number when comparing. Comparisons between a Duration and a numeric work because lubridate coerces the number to seconds, but mixing units silently can mislead. Prefer explicit coercion: as.numeric(d, "hours") > 24.
Forgetting that duration("1 month") is approximate. lubridate uses 30.4375 days for one month and warns when you build a Duration from a unit that has no fixed second count. Use months(1) (Period) for month arithmetic on dates.
Try it yourself
Try it: Build a Duration of exactly 90 minutes, add it to the POSIXct timestamp ymd_hms("2024-06-15 08:00:00"), and store the result in ex_finish.
Click to reveal solution
Explanation: dminutes(90) builds a Duration of 5,400 seconds, and adding it to a POSIXct value advances the clock by exactly that many seconds. duration(90, "minutes") produces the same result.
Related lubridate functions
period()anddays(),months(),years()build calendar-aware Periods (see lubridate days() in R).interval()builds an Interval bounded by two timestamps; convert to a Duration withas.duration().time_length()returns a numeric length of an Interval in any unit.as.difftime()converts a Duration's seconds back to a base R difftime.make_difftime()builds a difftime directly from numeric seconds.
For the full lubridate reference, see the tidyverse lubridate documentation.
FAQ
What is the difference between duration() and period() in lubridate?
duration() returns a Duration that stores time as an exact second count, while period() returns a Period that stores time as calendar units (years, months, days, etc.). A Duration of one day is always 86,400 seconds. A Period of one day is one calendar day, which can equal 23, 24, or 25 hours when daylight saving begins or ends. Pick Duration for elapsed time; pick Period for date arithmetic.
How do I convert a difftime to a lubridate Duration?
Wrap the difftime in as.duration(). For example, as.duration(difftime(t2, t1, units = "secs")) returns a Duration with the same elapsed seconds. The reverse conversion, Duration to difftime, uses as.difftime(d, units = "secs") and yields the base R class.
Can I add a Duration to a Date object?
Not directly, because a Date has no time component. Coerce the Date to POSIXct first with as.POSIXct(d) and then add the Duration, or use a Period (days(N)) when calendar-day arithmetic is enough. Adding a Duration to a POSIXct returns a POSIXct shifted by the exact number of seconds in the Duration.
Why does dyears(1) not land on the same calendar date one year later?
Because a Duration year is fixed at 365.25 days (31,557,600 seconds), and that number does not align with real calendar years that are either 365 or 366 days long. Adding dyears(1) to a date frequently produces a result six hours into the next day. For "same calendar date next year" semantics, use the Period helper years(1).
How do I get the length of a Duration in hours or days?
Coerce with as.numeric() and pass the target unit: as.numeric(duration("1 week"), "hours") returns 168. Supported units include "seconds", "minutes", "hours", "days", "weeks", "months", and "years". The conversion uses fixed second counts, so the result is exact for time units and approximate for months and years.