Skip to main content
v2026.11,858 entries · CC-BY 4.0

R Days: Add, Subtract and Count Days Between Dates

How to add, subtract and count days in R with base R and lubridate, including the period vs duration difference across daylight saving changes.

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. Pass units = "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)

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about R Days: Add, Subtract and Count Days Between Dates

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →