R Date & Time Exercises: 10 lubridate Practice Problems with Solutions
Practice R date and time manipulation with 10 exercises: parse dates from strings, calculate age and duration, extract components, create sequences, and handle messy date formats. Uses both base R and lubridate.
Dates are one of the trickiest data types in any language. These exercises cover the most common date operations you'll encounter in data analysis — from basic parsing to business-day calculations.
Easy (1-4): Parse and Extract
Exercise 1: Create and Inspect Dates
Create Date objects from strings in different formats. Extract the year, month, and day of the week.
# Exercise 1: Parse dates and extract components
# Parse these date strings into Date objects:
# "2026-03-29", "March 15, 2024", "01/20/2025"
# Then extract: year, month name, day of week
Click to reveal solution
# Base R date parsing
d1 <- as.Date("2026-03-29") # ISO format (default)
d2 <- as.Date("March 15, 2024", format = "%B %d, %Y") # Month name, day, year
d3 <- as.Date("01/20/2025", format = "%m/%d/%Y") # MM/DD/YYYY
dates <- c(d1, d2, d3)
cat("Parsed dates:", format(dates), "\n\n")
# Extract components
for (d in dates) {
d <- as.Date(d, origin = "1970-01-01")
cat(sprintf("%s → Year: %d, Month: %s, Day of week: %s\n",
format(d), as.integer(format(d, "%Y")),
format(d, "%B"), format(d, "%A")))
}
# Quick reference of format codes
cat("\nCommon format codes:\n")
cat(" %Y = 4-digit year %m = month number %d = day\n")
cat(" %B = month name %b = abbrev month %A = weekday name\n")
Exercise 2: Date Arithmetic
Calculate: how many days between two dates, what date is 100 days from today, and your age in days.
# Exercise 2: Date math
today <- Sys.Date()
# 1. Days between Jan 1, 2026 and today
# 2. What date is 100 days from today?
# 3. What date was 1000 days ago?
# 4. Days between Christmas 2025 and Christmas 2026
Click to reveal solution
today <- Sys.Date()
cat("Today:", format(today), "\n\n")
# 1. Days since Jan 1, 2026
jan1 <- as.Date("2026-01-01")
diff1 <- as.integer(today - jan1)
cat("Days since Jan 1, 2026:", diff1, "\n")
# 2. 100 days from today
future <- today + 100
cat("100 days from now:", format(future), "(", format(future, "%A"), ")\n")
# 3. 1000 days ago
past <- today - 1000
cat("1000 days ago:", format(past), "\n")
# 4. Between Christmases
xmas_2025 <- as.Date("2025-12-25")
xmas_2026 <- as.Date("2026-12-25")
cat("Days between Christmases:", as.integer(xmas_2026 - xmas_2025), "\n")
# Bonus: weeks and months
cat("\nWeeks since Jan 1:", round(diff1 / 7, 1), "\n")
cat("Months (approx):", round(diff1 / 30.44, 1), "\n")
Exercise 3: Date Sequences
Create date sequences: every day for a week, every Monday in March 2026, and the first day of each month in 2026.
# Exercise 3: Generate date sequences
# 1. Every day from March 1-7, 2026
# 2. Every Monday in March 2026
# 3. First day of each month in 2026
Click to reveal solution
# 1. Daily sequence
daily <- seq(as.Date("2026-03-01"), as.Date("2026-03-07"), by = "day")
cat("Daily:", format(daily, "%b %d (%a)"), "\n\n")
# 2. Every Monday in March 2026
all_march <- seq(as.Date("2026-03-01"), as.Date("2026-03-31"), by = "day")
mondays <- all_march[format(all_march, "%A") == "Monday"]
cat("Mondays in March:", format(mondays, "%b %d"), "\n\n")
# 3. First of each month
month_starts <- seq(as.Date("2026-01-01"), as.Date("2026-12-01"), by = "month")
cat("Month starts:\n")
for (d in month_starts) {
d <- as.Date(d, origin = "1970-01-01")
cat(sprintf(" %s (%s)\n", format(d, "%B %d"), format(d, "%A")))
}
Key concept:seq() works with dates. by = "day", by = "week", by = "month", by = "quarter" are all valid.
Exercise 4: Parse Messy Dates
Clean a vector of dates in mixed formats and convert them all to proper Date objects.
# Exercise 4: Parse messy date formats
messy_dates <- c("2024-01-15", "03/22/2024", "January 5, 2024",
"2024.06.30", "15-Aug-2024", "12/1/24")
# Parse ALL of these into Date objects
Click to reveal solution
messy_dates <- c("2024-01-15", "03/22/2024", "January 5, 2024",
"2024.06.30", "15-Aug-2024", "12/1/24")
# Try multiple formats for each date
parse_date <- function(x) {
formats <- c("%Y-%m-%d", "%m/%d/%Y", "%B %d, %Y",
"%Y.%m.%d", "%d-%b-%Y", "%m/%d/%y")
for (fmt in formats) {
result <- tryCatch(as.Date(x, format = fmt), error = function(e) NA)
if (!is.na(result)) return(result)
}
return(NA)
}
parsed <- as.Date(sapply(messy_dates, parse_date), origin = "1970-01-01")
cat("Parsing results:\n")
for (i in seq_along(messy_dates)) {
cat(sprintf(" %-20s → %s\n", messy_dates[i], format(parsed[i])))
}
cat("\nAll parsed:", format(parsed), "\n")
cat("Any failures:", sum(is.na(parsed)), "\n")
Key concept: Real data has mixed date formats. Try multiple format strings and use the first that succeeds. %y is 2-digit year, %Y is 4-digit.
Medium (5-7): Calculations and Grouping
Exercise 5: Age Calculator
Write a function that calculates exact age in years given a birth date. Test with several dates.
# Exercise 5: Calculate exact age
# Write age_years(birth_date) that returns age in decimal years
# Account for leap years properly
Click to reveal solution
age_years <- function(birth_date, ref_date = Sys.Date()) {
birth <- as.Date(birth_date)
ref <- as.Date(ref_date)
# Calculate year difference, adjust if birthday hasn't occurred yet
years <- as.integer(format(ref, "%Y")) - as.integer(format(birth, "%Y"))
# Has this year's birthday passed?
birth_this_year <- as.Date(paste0(format(ref, "%Y"), format(birth, "-%m-%d")))
if (ref < birth_this_year) years <- years - 1
# Decimal part
if (ref >= birth_this_year) {
next_birthday <- as.Date(paste0(as.integer(format(ref, "%Y")) + 1, format(birth, "-%m-%d")))
fraction <- as.numeric(ref - birth_this_year) / as.numeric(next_birthday - birth_this_year)
} else {
prev_birthday <- as.Date(paste0(as.integer(format(ref, "%Y")) - 1, format(birth, "-%m-%d")))
fraction <- as.numeric(ref - prev_birthday) / as.numeric(birth_this_year - prev_birthday)
}
return(round(years + fraction, 2))
}
# Test
births <- c("1990-06-15", "2000-01-01", "1985-12-31", "2005-03-29")
today <- Sys.Date()
for (b in births) {
cat(sprintf("Born %s → Age: %.2f years\n", b, age_years(b, today)))
}
Exercise 6: Business Days
Calculate the number of business days (Mon-Fri, excluding weekends) between two dates.
# Exercise 6: Count business days
# How many business days between March 1 and March 31, 2026?
# How many weekend days?
Key concept:format(date, "%u") gives the ISO weekday number (1=Monday, 7=Sunday). Filter for 1-5 to get business days.
Exercise 7: Monthly Aggregation
Given daily sales data, aggregate by month and find the best month.
# Exercise 7: Group daily data by month
set.seed(42)
dates <- seq(as.Date("2025-01-01"), as.Date("2025-12-31"), by = "day")
sales <- round(runif(length(dates), 500, 2000) +
sin(seq_along(dates)/365 * 2 * pi) * 300, 0)
# 1. Create a data frame with date and sales
# 2. Add a month column
# 3. Calculate monthly totals
# 4. Find the best and worst months
Generate a complete daily time series, fill in missing dates, and identify gaps.
# Exercise 9: Fill missing dates in time series
# This data has gaps (weekends and random missing days)
set.seed(42)
raw_dates <- seq(as.Date("2026-03-01"), as.Date("2026-03-31"), by = "day")
# Remove weekends and some random days
keep <- format(raw_dates, "%u") %in% c("1","2","3","4","5") & runif(length(raw_dates)) > 0.15
observed <- data.frame(
date = raw_dates[keep],
value = round(rnorm(sum(keep), 100, 10), 1)
)
# 1. How many days are missing?
# 2. Create a complete daily sequence and merge
# 3. Fill missing values with the previous day's value
Click to reveal solution
set.seed(42)
raw_dates <- seq(as.Date("2026-03-01"), as.Date("2026-03-31"), by = "day")
keep <- format(raw_dates, "%u") %in% c("1","2","3","4","5") & runif(length(raw_dates)) > 0.15
observed <- data.frame(
date = raw_dates[keep],
value = round(rnorm(sum(keep), 100, 10), 1)
)
cat("Observed days:", nrow(observed), "of 31\n")
# 1. Missing days
all_days <- seq(as.Date("2026-03-01"), as.Date("2026-03-31"), by = "day")
missing <- all_days[!all_days %in% observed$date]
cat("Missing days:", length(missing), "\n")
cat("Missing dates:", format(missing, "%b %d"), "\n\n")
# 2. Complete daily sequence
complete <- data.frame(date = all_days)
complete <- merge(complete, observed, by = "date", all.x = TRUE)
# 3. Forward-fill NAs
for (i in 2:nrow(complete)) {
if (is.na(complete$value[i])) {
complete$value[i] <- complete$value[i - 1]
}
}
cat("Complete series (first 10):\n")
complete$filled <- ifelse(complete$date %in% observed$date, "", " (filled)")
print(head(complete, 10))
Exercise 10: Holiday and Event Calculator
Build a function that calculates the dates of common holidays and events for any given year.
# Exercise 10: Holiday calculator
# Write get_holidays(year) that returns dates for:
# - New Year's Day (Jan 1)
# - Valentine's Day (Feb 14)
# - US Independence Day (Jul 4)
# - Halloween (Oct 31)
# - Christmas (Dec 25)
# - Thanksgiving (4th Thursday of November)
# - Easter (harder — use an algorithm or lookup)
# Also: show which day of the week each falls on
Click to reveal solution
get_holidays <- function(year) {
# Fixed holidays
holidays <- data.frame(
name = c("New Year's Day", "Valentine's Day", "Independence Day",
"Halloween", "Christmas"),
date = as.Date(c(
paste0(year, "-01-01"), paste0(year, "-02-14"),
paste0(year, "-07-04"), paste0(year, "-10-31"),
paste0(year, "-12-25")
)),
stringsAsFactors = FALSE
)
# Thanksgiving: 4th Thursday of November
nov_days <- seq(as.Date(paste0(year, "-11-01")),
as.Date(paste0(year, "-11-30")), by = "day")
thursdays <- nov_days[format(nov_days, "%A") == "Thursday"]
thanksgiving <- thursdays[4]
holidays <- rbind(holidays,
data.frame(name = "Thanksgiving", date = thanksgiving))
# Sort by date
holidays <- holidays[order(holidays$date), ]
holidays$day_of_week <- format(holidays$date, "%A")
holidays$formatted <- format(holidays$date, "%B %d")
return(holidays)
}
# Test for 2026
cat("=== 2026 Holidays ===\n")
h2026 <- get_holidays(2026)
for (i in 1:nrow(h2026)) {
cat(sprintf(" %-20s %s (%s)\n",
h2026$name[i], h2026$formatted[i], h2026$day_of_week[i]))
}
# Compare two years
cat("\n=== Christmas Day of Week ===\n")
for (y in 2024:2030) {
xmas <- as.Date(paste0(y, "-12-25"))
cat(sprintf(" %d: %s\n", y, format(xmas, "%A")))
}
Key concept: Fixed holidays are easy — just construct the date string. "Nth weekday of month" holidays (like Thanksgiving) require generating all days in the month and filtering. This is a common real-world date calculation.
Summary: Skills Practiced
Exercises
Date/Time Skills
1-4 (Easy)
as.Date(), format(), date arithmetic, seq(), parsing mixed formats
5-7 (Medium)
Age calculation, business days, monthly aggregation
8-10 (Hard)
Cohort analysis, time series gap filling, holiday calculation
What's Next?
One more exercise set:
R apply Family Exercises — master apply, lapply, sapply, tapply
Or continue learning: Data Wrangling with dplyr tutorial.