Working with Dates using lubridate

· 5 min read · Updated March 7, 2026 · beginner
lubridate dates datetime tidyverse time

Dates and times are everywhere in data analysis. Whether you’re tracking sales over time, analyzing user behavior, or scheduling tasks, you’ll need to work with dates. R’s base Date handling can be awkward, but the lubridate package makes it intuitive and powerful.

In this tutorial, you’ll learn how to parse dates, extract components, perform calculations, and handle time zones—all with easy-to-remember functions.

Installing and Loading lubridate

First, install and load lubridate from CRAN:

install.packages("lubridate")
library(lubridate)

lubridate is part of the tidyverse, so if you have tidyverse loaded, you already have lubridate:

library(tidyverse)

Parsing Dates and Times

The hardest part of working with dates is often just getting R to recognize them. lubridate makes this straightforward with functions that match common date formats.

Automatic Parsing Functions

lubridate provides functions named after the order of date components:

FunctionFormatExample
ymd()Year-Month-Day2024-03-15
mdy()Month-Day-YearMarch 15, 2024
dmy()Day-Month-Year15 March 2024
ymd_hms()With time2024-03-15 14:30:00
# Parse dates from different formats
ymd("2024-03-15")
# [1] "2024-03-15"

mdy("March 15, 2024")
# [1] "2024-03-15"

dmy("15-03-2024")
# [1] "2024-03-15"

# Parse datetime with time
ymd_hms("2024-03-15 14:30:45")
# [1] "2024-03-15 14:30:45 UTC"

Parsing from Character Vectors

You can parse entire vectors at once—perfect for data frames:

dates <- c("2024-01-15", "2024-02-20", "2024-03-15")
ymd(dates)
# [1] "2024-01-15" "2024-02-20" "2024-03-15"

# With times
datetimes <- c("2024-03-15 09:30:00", "2024-03-15 10:45:00")
ymd_hms(datetimes)
# [1] "2024-03-15 09:30:00 UTC" "2024-03-15 10:45:00 UTC"

Handling Mixed Formats

If your data has mixed formats, use parse_date_time() with format strings:

mixed <- c("2024-01-15", "15/03/2024", "2024.03.20")
parse_date_time(mixed, orders = c("ymd", "dmy", "ymd"))
# [1] "2024-01-15" "2024-03-15" "2024-03-20"

Extracting Date Components

Once you have a date or datetime, you can extract individual components:

date <- ymd_hms("2024-03-15 14:30:45")

year(date)      # Extract year
# [1] 2024

month(date)     # Extract month as number
# [1] 3

month(date, label = TRUE)  # Month as abbreviation
# [1] Mar

day(date)       # Extract day
# [1] 15

hour(date)      # Extract hour
# [1] 14

minute(date)    # Extract minute
# [1] 30

second(date)    # Extract second
# [1] 45

wday(date)      # Day of week (1 = Sunday)
# [1] 6

wday(date, label = TRUE)  # Day name
# [1] Fri

You can also use these functions to modify components:

date <- ymd("2024-03-15")
month(date) <- 6  # Change to June
date
# [1] "2024-06-15"

year(date) <- year(date) + 1  # Add one year
date
# [1] "2025-06-15"

Date Arithmetic

lubridate makes date arithmetic intuitive with functions like days(), weeks(), months(), and years():

Adding and Subtracting Time

# Start date
start <- ymd("2024-03-15")

# Add time
start + days(10)
# [1] "2024-03-25"

start + weeks(2)
# [1] "2024-03-29"

start + months(1)
# [1] "2024-04-15"

start + years(1)
# [1] "2025-03-15"

# Subtract time
start - days(5)
# [1] "2024-03-10"

Difference Between Dates

Use %--% to calculate the duration between two dates:

start <- ymd("2024-01-01")
end <- ymd("2024-03-15")

# Calculate difference
interval <- start %--% end
interval
# [1] 2024-01-01 UTC--2024-03-15 UTC

# Get the span in different units
as.duration(interval)
# [1] "74d 0H 0M 0S"

as.period(interval)
# [1] "2 months and 14 days"

# Quick calculations
end - start
# Time difference of 74 days

Rounding Dates

Round dates to nearby boundaries:

datetime <- ymd_hms("2024-03-15 14:30:45")

floor_date(datetime, "day")   # Round down to day
# [1] "2024-03-15 UTC"

ceiling_date(datetime, "day") # Round up to day
# [1] "2024-03-16 UTC"

round_date(datetime, "hour")  # Round to nearest hour
# [1] "2024-03-15 15:00:00 UTC"

Handling Time Zones

Time zones can be tricky. lubridate makes them manageable:

Setting Time Zones

# Parse with specific timezone
datetime <- ymd_hms("2024-03-15 14:30:00", tz = "America/New_York")
datetime
# [1] "2024-03-15 14:30:00 EST"

# Convert to another timezone
with_tz(datetime, "UTC")
# [1] "2024-03-15 19:30:00 UTC"

with_tz(datetime, "Europe/London")
# [1] "2024-03-15 18:30:00 GMT"

Common Time Zone Operations

# Get current timezone
Sys.timezone()
# [1] "UTC"

# See available timezones
length(OlsonNames())
# [1] 599

# Parse and force timezone (even if string has no tz)
force_tz(ymd_hms("2024-03-15 14:30:00"), tzone = "America/Los_Angeles")
# [1] "2024-03-15 14:30:00 PST"

Practical Examples

Example 1: Calculate Age

calculate_age <- function(birth_date) {
  birth <- ymd(birth_date)
  today <- today()
  age <- year(today) - year(birth)
  # Subtract 1 if birthday hasn't occurred this year
  month(today) < month(birth) | 
    (month(today) == month(birth) & day(today) < day(birth))
  age - 1
}

calculate_age("1990-05-15")
# [1] 35

Example 2: Business Days Between Dates

library(lubridate)

# Create date range
start <- ymd("2024-03-01")
end <- ymd("2024-03-31")

# Get all days in March
all_days <- seq(start, end, by = "day")

# Remove weekends
business_days <- all_days[!wday(all_days) %in% c(1, 7)]
length(business_days)
# [1] 21

Example 3: Find Last Day of Month

# Last day of March 2024
ceiling_date(ymd("2024-03-15"), "month") - days(1)
# [1] "2024-03-31"

# Or use the dedicated function
ceiling_date(ymd("2024-03-15"), "month") - ddays(1)
# [1] "2024-03-31 23:59:59 UTC"

Example 4: Parse Dates from Real Data

# Simulating reading from a CSV
library(dplyr)

sample_data <- tibble(
  date_str = c("2024-01-15", "2024-02-20", "2024-03-10", "2024-04-05")
)

# Parse and extract components
sample_data %>%
  mutate(
    date = ymd(date_str),
    year = year(date),
    month = month(date, label = TRUE),
    day = day(date),
    quarter = quarter(date),
    week = week(date)
  )
# # A tibble: 4 × 7
#   date_str  date        year month   day quarter  week
#   <chr>     <date>     <dbl> <ord> <int>   <int> <int>
# 1 2024-01-15 2024-01-15  2024 Jan    15       1     3
# 2 2024-02-20 2024-02-20  2024 Feb    20       1     8
# 3 2024-03-10 2024-03-10  2024 Mar    10       1    11
# 4 2024-04-05 2024-04-05  2024 Apr     5       2    14

Summary

lubridate transforms date handling from a headache into a pleasure:

  • Parsing: Use ymd(), mdy(), dmy() and their datetime variants
  • Extraction: year(), month(), day(), hour(), wday()
  • Arithmetic: Add/subtract with days(), months(), years()
  • Intervals: Use %--% to calculate spans between dates
  • Time zones: with_tz() and force_tz() for timezone conversion
  • Rounding: floor_date(), ceiling_date(), round_date()

With these tools, you’ll handle any date-related task with confidence. In the next tutorial, we’ll cover forcats for handling categorical variables—another common data wrangling challenge.