Written and maintained by CASRAI Editorial Board
Last updated
Working with days in R means three jobs: adding or subtracting a number of days from a date, counting the days between two dates, and keeping the date class intact while you do it. Base R handles all three; the lubridate package adds period and duration objects that behave differently around clock changes, which is where most silent date errors in analysis scripts come from.
What it is used for in research
Date arithmetic appears wherever a protocol is defined in days: follow-up windows, time from enrolment to event, washout periods, retention deadlines and reporting windows. Getting the arithmetic right depends on knowing what an R date actually is. The base R documentation states that Date objects are represented as the number of days since 1970-01-01, with negative values for earlier dates. That is why adding a whole number to a date moves it by that many days. The same page notes that fractional days can be produced by adding or subtracting, and that fractional days are ignored when printing, so a value can look like a clean date while holding a fraction underneath.
Install or access (official source only)
Date, as.Date() and difftime() are part of base R and need no installation. The lubridate package is published on CRAN; its CRAN page lists version 1.9.5 (published 2026-02-04) and a dependency of R 3.2 or later. Install it from CRAN and load it:
install.packages("lubridate")
library(lubridate)
See lubridate on CRAN. If you are new to getting data into R first, start with how to load data into R or reading a CSV into R.
Step-by-step: add, subtract and count days
1. Turn text into a Date
Date arithmetic only works on a Date, not on a character string. as.Date() takes the arguments as.Date(x, format, tryFormats = c("%Y-%m-%d", "%Y/%m/%d"), optional = FALSE, ...). When you do not give format, R tries each entry of tryFormats in turn on the first non-NA element and gives an error if none works. Both defaults follow ISO 8601 (year, month, day), so write dates that way.
start <- as.Date("2009-08-09")
2. Add days
In base R, add a number. The result keeps the Date class.
start + 30
With lubridate, add a period built by days(). The lubridate reference gives days(x = 1) as the usage and shows that class(as.Date("2009-08-09") + days(1)) returns "Date", so the class is retained. Its argument must be an integer.
start + days(30)
3. Subtract days
Subtract the number in the same way: start - 30 in base R, or start - days(30) with lubridate.
4. Count the days between two dates
Use difftime(), whose signature is difftime(time1, time2, tz, units = c("auto", "secs", "mins", "hours", "days", "weeks")). Setting the units explicitly removes any guesswork. The documentation also says time1 - time2 gives the same kind of result.
end <- as.Date("2009-08-20")
difftime(end, start, units = "days")
as.numeric(difftime(end, start, units = "days"))
The second line returns a plain number, which is what you want for a column in a data frame or a model. The base R documentation says as.numeric(z, units = "hours") converts a difftime to a number in the units you choose, so the same pattern works for any unit.
5. Step through a series of dates
The Date documentation points to seq.Date for sequences of dates and gives the example seq(today, length.out = 10, by = "1 week"). Read the seq.Date help page in your R installation for the full list of accepted by values before building a daily or monthly series.
Output and how to read it
Adding days to a Date returns a Date, which prints as a calendar date. Subtracting two Dates, or calling difftime(), returns a difftime object: a number with a units attribute. Two points matter for reading it correctly.
- Auto units. With the default
units = "auto", R chooses a suitable set of units, the largest possible excluding weeks. The chosen unit depends on the size of the difference, so it is not guaranteed to be days. Passunits = "days"whenever the result feeds a calculation. - Fractions. Because Dates are day counts underneath, a fractional result can hide behind a date that prints cleanly. Check with
as.numeric()if a count looks off by a fraction.
Period or duration: the gap most tutorials skip
Lubridate separates two kinds of time span. days(1) makes a period, and the lubridate documentation says that within a period, time units do not have a fixed length (except for seconds) until they are added to a date-time. ddays(1) makes a duration, a fixed span of 86,400 seconds. On plain Dates the two give the same calendar answer. They diverge on date-times across a daylight saving change.
The lubridate reference demonstrates this with a boundary in March 2009: boundary + days(1) returns “2009-03-09 01:59:59 CDT”, keeping the clock time, while boundary + ddays(1) returns “2009-03-09 02:59:59 CDT”, an exact 86,400 seconds later. For a protocol defined as “the same time on the next day”, use a period. For an exact elapsed time, use a duration.
| You want | Use | Source behaviour |
|---|---|---|
| Same clock time, N calendar days later | days(N) (period) |
Units have no fixed length until added to a date-time |
| Exactly N x 86,400 seconds later | ddays(N) (duration) |
Fixed length in seconds |
| Day count between two dates | difftime(..., units = "days") |
Explicit units avoid the auto choice |
Troubleshooting
as.Date() returns an error or NA
If no entry in tryFormats matches the first non-NA element, as.Date() stops with an error. Supply format to match your data, for example format = "%d/%m/%Y", or set optional = TRUE to get NA instead of an error. Incorrect dates also give an error or NA. Check how many NAs you have before you compute differences, because they propagate silently into the result.
The difference is not in days
This is the auto-units behaviour above. Set units = "days".
Adding months to a month-end date
The lubridate documentation warns that arithmetic with periods can give undefined behaviour when non-existent dates are involved. Adding a month to a 31st is the usual case. Inspect any result near a month end rather than assuming it is right.
Values stored as text in a data frame
Check the column with class(). A character column will not accept date arithmetic until you convert it with as.Date(). Once the column is numeric, a plot such as a histogram in R shows whether the day counts look sensible.
Free alternatives
| Tool | Add days | Days between | Where it comes from |
|---|---|---|---|
| Base R | date + n |
difftime(..., units = "days") or time1 - time2 |
Included with R |
| lubridate | date + days(n) |
Same base R functions; periods and durations for date-times | CRAN package |
For the reproducibility side of date handling, such as recording the R and package versions your analysis used, see computational reproducibility.
FAQ
How do I add days to a date in R?
Add a number to a Date (as.Date("2009-08-09") + 30) or add a lubridate period (+ days(30)). Both return a Date.
How do I count the days between two dates in R?
Use difftime(end, start, units = "days"), and wrap it in as.numeric() when you need a plain number.
Why does R say a date is a number?
Dates are stored as the number of days since 1970-01-01. Printing shows the calendar form.
What is the difference between days() and ddays()?
days() makes a period with no fixed length until it is added to a date-time; ddays() makes a duration of a fixed 86,400 seconds.
Where do I go next once the day counts are in a column?
Use them like any numeric column. See R if statements and ifelse in R for conditional logic.
Sources (official docs)
- R documentation: Date Class (checked 2026-10-29)
- R documentation: as.Date (checked 2026-10-29)
- R documentation: difftime (checked 2026-10-29)
- lubridate reference: period (checked 2026-10-29)
- lubridate on CRAN (checked 2026-10-29)







