R Date & Time Exercises: 10 lubridate Practice Problems with Solutions
These 10 exercises test your ability to parse, manipulate, and calculate with dates and times in R using the lubridate package. Each problem includes starter code with hints and a full worked solution, run them directly in your browser to check your answers.
How do you parse dates from text strings?
The exercises below progress from basic date parsing to timezone conversions and interval calculations. Each one builds on a skill you'll use constantly when cleaning real-world data. Let's load lubridate and see it in action.
The function name tells lubridate the order of components, ymd() expects year-month-day, mdy() expects month-day-year, and dmy() expects day-month-year. Lubridate figures out the separators and formats automatically, so you only need to pick the right function.
Exercise 1: Parse a date with ymd()
Parse the string "2024-03-15" into a Date object called my_date. Print the result and confirm its class.
Click to reveal solution
Explanation: ymd() parses a year-month-day string into an R Date object. The class confirms it's a proper Date, not just a character string.
Exercise 2: Parse dates in different formats
Parse these two strings into Date objects: "March 15, 2024" (US format) and "15/03/2024" (European format). Store them as date_us and date_eu.
Click to reveal solution
Explanation: mdy() handles the US convention (month first), while dmy() handles the European convention (day first). Both produce the same Date object because they represent the same calendar date. The equality check confirms this.
Try it: Parse the string "07-Jan-2025" into a Date object. Which lubridate function do you need?
Click to reveal solution
Explanation: The string starts with day (07), then month (Jan), then year (2025), so dmy() is the right choice. Lubridate handles abbreviated month names just fine.
How do you extract and modify date components?
Once you have a Date object, you often need to pull out individual pieces, the year, month, or day of the week. Lubridate's component functions work as both getters (read a component) and setters (change a component in place).
Exercise 3: Extract year, month, and weekday
Using my_date (which holds 2024-03-15), extract the year, month number, and weekday name.
Click to reveal solution
Explanation: year() returns the four-digit year, month() returns the month as an integer (1-12), and wday() with label = TRUE returns the abbreviated weekday name. March 15, 2024 was a Friday.
Exercise 4: Change a date component in place
Change the month of my_date to December (12) and print the result. What date do you get?
Click to reveal solution
Explanation: Assigning to month(my_date) changes the month in place while keeping the year and day unchanged. The date moves from March 15 to December 15, 2024. This is one of lubridate's most convenient features, the same function works as both a getter and a setter.
month(x) to read the month, or month(x) <- 6 to change it to June. The same pattern works for year(), day(), hour(), minute(), and second().Try it: Use quarter() to find which quarter of the year today's date falls in.
Click to reveal solution
Explanation: quarter() returns 1-4 depending on which three-month block the date falls in. April 2026 is in Q2 (April-June).
How do you do arithmetic with dates?
Date arithmetic is where lubridate really shines. You can add or subtract periods like days(), months(), and years() directly to Date objects. But watch out, adding months can produce surprising results when the starting day doesn't exist in the target month.
Exercise 5: Add days vs. months, compare the results
Starting from "2024-01-31", add 90 days and separately add 1 month. Store the results as result_days and result_months. Are they the same?
Click to reveal solution
Explanation: Adding 90 days to January 31 lands on April 20. But adding 1 month to January 31 produces NA because February 31 doesn't exist. Lubridate returns NA rather than guessing. Use base_date %m+% months(1) if you want it to roll back to the last valid day of February (Feb 29, since 2024 is a leap year).
%m+% operator instead of + if you want lubridate to roll back to the last valid day of the month.Exercise 6: Calculate the number of days between two dates
How many days are there between January 1, 2024 and December 31, 2024? Store the answer as diff_days.
Click to reveal solution
Explanation: Subtracting two Date objects gives a difftime object. Wrapping it in as.numeric() extracts the number of days as a plain integer. 2024 is a leap year (366 days total), but the difference from Jan 1 to Dec 31 is 365 because the subtraction doesn't count the start date.
Try it: What date is 100 days from today?
Click to reveal solution
Explanation: today() gives the current date, and days(100) creates a period of 100 days. Adding them together gives the date 100 days from now.
How do you work with date-time objects and timezones?
Real-world data often includes timestamps with hours, minutes, and seconds, plus timezones that can trip you up if you're not careful. Lubridate has two key timezone functions: with_tz() converts the display to a different timezone (same instant), while force_tz() reinterprets the clock time as a different timezone (different instant).
Exercise 7: Parse a datetime and convert timezones
Parse "2024-07-04 14:30:00" as a datetime in UTC. Then convert it to "America/New_York" time. Store the results as dt_utc and dt_ny.
Click to reveal solution
Explanation: ymd_hms() parses a full timestamp with hours, minutes, and seconds. The tz argument sets the timezone. with_tz() then converts the same moment in time to how it would appear on a clock in New York, 4 hours earlier in July (Eastern Daylight Time, UTC-4).
Exercise 8: Extract the hour in different timezones
Using dt_utc and dt_ny from Exercise 7, extract the hour from each. What's the difference?
Click to reveal solution
Explanation: The UTC time is 14:30 and the New York time is 10:30, a 4-hour difference. This matches the EDT (Eastern Daylight Time) offset of UTC-4 that applies in July. In winter, New York uses EST (UTC-5), so the difference would be 5 hours.
Try it: Use force_tz() to reinterpret dt_utc as if it were recorded in "Asia/Tokyo" time. Compare the result to dt_utc, is it the same instant?
Click to reveal solution
Explanation: force_tz() keeps the clock reading (14:30) but changes the timezone label to Tokyo. Since Tokyo is UTC+9, this now represents a completely different moment, 9 hours earlier in UTC terms. That's why == returns FALSE. Use force_tz() only when you need to correct a mislabeled timezone.
How do you calculate intervals and durations?
Lubridate distinguishes between three ways to measure time spans: durations (exact seconds), periods (human units like "1 month"), and intervals (a specific start-to-end span). Intervals are especially useful when you need to check if a date falls within a range or calculate an exact age.
Exercise 9: Create an interval and test membership
Create an interval from "2024-01-01" to "2024-06-30". Then check whether "2024-03-15" falls within that interval.
Click to reveal solution
Explanation: The %--% operator creates an interval between two dates. The %within% operator then tests whether a date falls inside that interval. March 15 is between January 1 and June 30, so the result is TRUE. This is much cleaner than writing check_date >= start & check_date <= end.
Exercise 10: Calculate exact age in years
Calculate the exact age in complete years from the birthdate "1995-08-22" to today's date. Store the result as my_age.
Click to reveal solution
Explanation: The %--% operator creates an interval from the birthdate to today. Dividing by years(1) with the integer division operator %/% gives the number of complete years, the person's age. This handles leap years and varying month lengths correctly, which makes it more reliable than dividing a day count by 365.25.
Try it: How many complete months are in the interval from "2024-03-01" to "2024-11-15"?
Click to reveal solution
Explanation: From March 1 to November 15 spans 8 complete months (March 1 to November 1 is exactly 8 months, and the remaining 15 days don't complete a 9th month). The %/% operator floors to the nearest whole number.
Practice Exercises
These capstone exercises combine multiple concepts from the problems above. They're harder than the individual exercises, take your time and refer back to earlier solutions if needed.
Exercise 1: Parse mixed-format dates and find the span
You have a vector of 5 date strings in different formats. Parse all of them into Date objects, sort them chronologically, and calculate the number of days between the earliest and latest dates.
Click to reveal solution
Explanation: Each date string requires a different parsing function because the component order varies. After parsing, sort() arranges them chronologically. The span is the difference between the latest (Dec 1) and earliest (Jan 15) dates, 321 days. In real data pipelines, you'd often detect the format programmatically, but knowing which function to use for each format is the fundamental skill.
Exercise 2: Compute exact age as years, months, and days
Given a birthdate of "1990-05-17" and a target date of "2026-04-12", compute the exact age as "X years, Y months, Z days" using lubridate's interval and period functions.
Click to reveal solution
Explanation: as.period() converts an interval into human-readable components. You can then extract the year, month, and day parts individually using the same component functions you use on dates. This approach correctly handles varying month lengths and leap years, much more reliable than dividing total days by 365.25 and 30.44.
Complete Example
Let's tie everything together with a practical scenario. You're planning a conference on October 15, 2024. You need to compute key deadlines, show them in multiple timezones for international attendees, and track how many days remain.
This example combines date parsing (ymd_hms()), period arithmetic (months(), days()), timezone conversion (with_tz()), date subtraction, and interval testing (%--%, %within%), all the skills from the 10 exercises above. In your own projects, you'll use exactly these patterns whenever you work with scheduling, logging, or any time-stamped data.
Summary
| Exercise | Skill Tested | Key Function(s) |
|---|---|---|
| 1 | Parse date from string | ymd() |
| 2 | Parse multiple formats | mdy(), dmy() |
| 3 | Extract date components | year(), month(), wday() |
| 4 | Modify date components | month(x) <- value |
| 5 | Date arithmetic with periods | days(), months(), %m+% |
| 6 | Calculate date difference | Date subtraction + as.numeric() |
| 7 | Parse datetime + timezone conversion | ymd_hms(), with_tz() |
| 8 | Extract components across timezones | hour() |
| 9 | Interval creation + membership test | %--%, %within% |
| 10 | Calculate age in complete years | %--%, %/%, years() |
Key takeaways:
- The parse function name matches the component order:
ymd(),mdy(),dmy(), lubridate handles separators automatically - Component functions (
year(),month(),day()) work as both getters and setters months()adds calendar months (which can produce NA on invalid dates);days()always adds exact dayswith_tz()converts display (same instant),force_tz()changes the instant (same clock reading)%--%creates intervals,%within%tests membership, and%/%withyears(1)ormonths(1)gives whole units
References
- Spinu, V., Grolemund, G., & Wickham, H., "Dates and Times Made Easy with lubridate." Journal of Statistical Software, 40(3), 2011. Link
- lubridate documentation, CRAN reference manual. Link
- Wickham, H. & Grolemund, G., R for Data Science, 2nd Edition. Chapter 17: Dates and Times. Link
- lubridate tidyverse documentation, Function reference and vignettes. Link
- R Core Team,
?DateTimeClasses, Base R documentation for POSIXct and POSIXlt classes. Link
Continue Learning
- lubridate in R, Full lubridate tutorial covering every function used in these exercises, with detailed explanations and more examples
- R String Exercises, Practice R string manipulation with 10 stringr problems, similar format to this exercise set
- R Syntax 101, Foundational R syntax covering assignment, operators, and basic data types if you need a refresher