lubridate ymd() in R: Parse Dates From Strings

The ymd() function in lubridate parses date strings of the form "year-month-day" into proper Date objects. Sister functions mdy(), dmy(), ymd_hms() handle other orderings and date-times.

⚡ Quick Answer
ymd("2024-01-15")                          # year-month-day
mdy("01/15/2024")                          # month-day-year
dmy("15-01-2024")                          # day-month-year
ymd_hms("2024-01-15 14:30:00")             # date + time
ymd_hm("2024-01-15 14:30")                 # date + time (no seconds)
ymd_hms(x, tz = "America/New_York")        # with timezone
parse_date_time(x, orders = c("ymd","mdy"))# multi-format

Need explanation? Read on for examples and pitfalls.

📊 Which lubridate parser?
STARTknown order, year firstymd()known order, month firstmdy()known order, day firstdmy()with timeymd_hms() / mdy_hms() / dmy_hms()multiple possible ordersparse_date_time(x, orders = c(...))Excel serial dateas.Date(x, origin = "1899-12-30")Unix timestampas.POSIXct(x, origin = "1970-01-01")format unknownparse_date_time with multiple orders

What ymd() does in one sentence

ymd("2024-01-15") parses the input string and returns a Date object. lubridate auto-detects common separators (-, /, ., ) and is flexible about month and day padding (1 or 01 both work).

This is the simplest, most common date parsing function in R. Use it whenever you have a string that represents a year-first date.

Syntax

ymd(x, tz = NULL, locale = ...). x can be a single string, vector, or factor.

Run live
Run live, no install needed. Every R block on this page runs in your browser. Click Run, edit the code, re-run instantly. No setup.
RParse various year-first formats
library(lubridate) ymd("2024-01-15") #> [1] "2024-01-15" ymd(c("2024-01-15", "2024/03/20", "2024.07.04")) #> [1] "2024-01-15" "2024-03-20" "2024-07-04" ymd("20240115") #> [1] "2024-01-15"

  
Tip
lubridate parsers are FAR more flexible than base R as.Date(). as.Date("01/15/2024") errors without a format string. mdy("01/15/2024") just works. Use lubridate for parsing; reserve base R for cases where you absolutely need zero dependencies.

Five common patterns

1. Year-first date

RStandard ISO format
ymd("2024-01-15") #> [1] "2024-01-15" class(ymd("2024-01-15")) #> [1] "Date"

  

2. Different orderings

RMonth-first, day-first variants
mdy("January 15, 2024") #> [1] "2024-01-15" dmy("15 January 2024") #> [1] "2024-01-15" mdy("01-15-2024") #> [1] "2024-01-15"

  

mdy() for US-style; dmy() for European-style. Both accept text or numeric months.

3. Date + time

RParse a datetime
ymd_hms("2024-01-15 14:30:00") #> [1] "2024-01-15 14:30:00 UTC" class(ymd_hms("2024-01-15 14:30:00")) #> [1] "POSIXct" "POSIXt"

  

ymd_hms (with hms variants for partial times) returns a POSIXct object instead of Date.

4. With timezone

RSpecify a timezone explicitly
ymd_hms("2024-01-15 14:30:00", tz = "America/New_York") #> [1] "2024-01-15 14:30:00 EST"

  

tz = "America/New_York" parses the time as Eastern time. Default is UTC. Use OlsonNames() to list available timezones.

5. Multiple possible formats

Rparse_date_time for mixed-format inputs
mixed <- c("2024-01-15", "01/15/2024", "15 Jan 2024") parse_date_time(mixed, orders = c("ymd", "mdy", "dmy")) #> [1] "2024-01-15 UTC" "2024-01-15 UTC" "2024-01-15 UTC"

  

parse_date_time(x, orders = c(...)) tries each order until one succeeds. Useful for messy data with mixed formats.

Key Insight
lubridate's parsers are forgiving by design. ymd("2024.01.15"), ymd("2024-1-15"), ymd("20240115") all return the same result. This robustness is intentional: real-world dates come in countless variations and you should not have to guess separators.

ymd() vs as.Date() vs strptime() vs anytime

Four parsing functions with different trade-offs between convenience and control.

Function Package Format detection Best for
ymd() / mdy() / dmy() lubridate Auto Most cases; quick parse
as.Date() base R Manual format= Strict / known format
strptime() base R Manual format POSIXlt with timezone
anytime::anytime() anytime Auto, very loose Truly chaotic mixed inputs

For everyday work, lubridate's parsers are the default. Reach for base R when you want a strict format and zero dependencies.

Common pitfalls

Pitfall 1: parsing fails silently with NA. ymd("not a date") returns NA with a warning. Always check sum(is.na(parsed)) after parsing to spot bad inputs.

Pitfall 2: ambiguous formats. dmy("01/02/2024") returns Feb 1; mdy("01/02/2024") returns Jan 2. Pick the parser that matches the data's convention.

Warning
Excel serial dates are NOT date strings. "44927" represents 2023-01-01 in Excel, but ymd("44927") errors. For Excel serial dates: as.Date(44927, origin = "1899-12-30").

A practical lubridate workflow

Most real-world date pipelines follow a similar shape: read raw strings, parse, validate, extract components, compute durations.

The five steps:

  1. Read raw data as character vectors (CSV, JSON, web scrape).
  2. Parse to Date / POSIXct with ymd(), mdy(), or parse_date_time() for mixed inputs.
  3. Validate that none became NA: stopifnot(!any(is.na(parsed))).
  4. Extract components with year(), month(), day(), wday() for grouping or filtering.
  5. Compute durations with as.period(), difftime(), or interval arithmetic.

Following this pattern keeps date logic explicit and catches malformed inputs early. The single biggest source of date bugs in R is silent NA propagation from a failed parse. Always check for NAs immediately after parsing.

When working in dplyr pipelines, lubridate parsers chain naturally:

RParse and extract in one pipeline
library(dplyr) library(lubridate) events <- data.frame(date_str = c("2024-01-15", "2024-03-20", "2024-07-04")) events |> mutate(date = ymd(date_str), year = year(date), month = month(date)) #> date_str date year month #> 1 2024-01-15 2024-01-15 2024 1 #> 2 2024-03-20 2024-03-20 2024 3 #> 3 2024-07-04 2024-07-04 2024 7

  

Try it yourself

Try it: Parse the dates "Jan 15 2024", "March 20 2024", "July 4 2024" using mdy. Save to ex_dates.

RYour turn: parse month-first text dates
strings <- c("Jan 15 2024", "March 20 2024", "July 4 2024") ex_dates <- # your code here ex_dates #> Expected: c(2024-01-15, 2024-03-20, 2024-07-04) as Date

  
Click to reveal solution
RSolution
ex_dates <- mdy(c("Jan 15 2024", "March 20 2024", "July 4 2024")) ex_dates #> [1] "2024-01-15" "2024-03-20" "2024-07-04"

  

Explanation: mdy() handles month-name input as well as numeric. lubridate auto-detects "Jan" vs "January" and parses both. The result is a Date vector.

After mastering ymd, look at:

  • mdy(), dmy(): other ordering variants
  • ymd_hms(), ymd_hm(), ymd_h(): with time components
  • parse_date_time(): most flexible, accepts multiple orders
  • year(), month(), day(): extract components
  • floor_date(), ceiling_date(): round dates
  • now(), today(): current datetime / date

For date arithmetic, lubridate's days(7), months(1), years(1) are friendlier than base R's seq.Date.

FAQ

How do I convert a string to a Date in R?

Use lubridate::ymd("2024-01-15") for year-first strings. Use mdy() or dmy() for other orderings. lubridate handles common separators automatically.

What is the difference between ymd and as.Date?

Both convert strings to Dates. ymd() (lubridate) is forgiving about separators and ordering. as.Date() (base R) requires a format argument unless the input is exactly "YYYY-MM-DD". For most use cases, lubridate is easier.

How do I parse a date with time in R?

Use ymd_hms() for "year month day hour minute second": ymd_hms("2024-01-15 14:30:00"). Variants ymd_hm() and ymd_h() handle partial times.

How do I handle timezones with lubridate?

Pass tz = "America/New_York" (or another) to ymd_hms(). Use with_tz() to convert to a different timezone after parsing. List zones with OlsonNames().

How do I parse Excel dates in R?

Excel stores dates as serial numbers. Convert with as.Date(44927, origin = "1899-12-30"). The origin shifts depending on Excel's date system. Modern Excel uses 1899-12-30 as origin.