lubridate day() in R: Extract Day of Month from Dates
The day() function in lubridate returns the day of the month from a Date or POSIXct value as an integer between 1 and 31. It is an alias of mday(), vectorised over date columns, and supports in-place replacement.
day(ymd("2024-07-15")) # extract day of month
day(now()) # day of current datetime
day(as.Date("2024-07-15")) # works on base Date too
day(c(ymd("2024-01-31"), ymd("2024-06-15"))) # vectorised
day(x) <- 1 # replace day in place
df %>% mutate(d = day(order_date)) # extract a column
wday(ymd("2024-07-15")) # day of week instead
yday(ymd("2024-07-15")) # day of year (1 to 366)Need explanation? Read on for examples and pitfalls.
What day() does in one sentence
day() returns the day-of-month component of a date-time as an integer. Pass any Date, POSIXct, or POSIXlt vector and you get a numeric vector of the same length with values 1 through 31.
This is the lubridate counterpart to base R's format(x, "%d") and as.POSIXlt(x)$mday. The lubridate version is shorter, vectorised, and returns an integer you can use directly in arithmetic or comparisons.
Syntax
day(x, label = FALSE, abbr = TRUE) accepts a date-time vector and two optional flags. The return value is an integer vector of the same length. The label and abbr arguments are inherited from the generic for compatibility with wday() but have no effect for day().
The input must be a class lubridate recognises as a date or datetime: base Date, POSIXct, or POSIXlt. Character strings are NOT accepted. Parse them first with ymd(), mdy(), or as.Date().
day(x) <- value to overwrite the day in place. This is the replacement form. It keeps the year, month, and time of day untouched, so you can snap an entire column of dates to the first of the month with one assignment.Six common patterns
1. Extract day of month from a single date
The result is integer 15, not the string "15". Use it in arithmetic directly: day(d) + 7 returns 22 for next week's day number.
2. Extract day from a vector of dates
day() is fully vectorised. A million-row date column becomes a million-row integer vector in one call. No loop or sapply() needed. The result respects month length, so February 29 in a leap year returns 29 cleanly.
3. Replace day values in place
The replacement form keeps year, month, and time untouched. This is the cleanest way to bucket a series of timestamps to the start of their month without a separate floor_date() call.
4. Use day() inside a dplyr pipeline
mutate(dom = day(order_date)) adds a day-of-month column. This is the start of any analysis that asks "do sales spike on payday?" or "are mid-month days different from month-end days?".
5. Filter rows by day of month
day(order_date) <= 5 evaluates row by row and keeps dates in the first five days of any month. Combine with between() for mid-month windows: filter(between(day(order_date), 10, 20)).
6. Build month-end and payday flags
days_in_month() returns the last valid day for each month (28, 29, 30, or 31), which beats hard-coding 31 and getting wrong answers for February. Pairing it with day() is the safe way to flag month-end rows.
day() only answers the first. A common bug is reaching for day() when you wanted Monday/Tuesday/etc. For weekday use wday(x, label = TRUE). For day-of-year use yday(). The four day-family extractors do not overlap, so picking the wrong one returns plausible but wrong numbers.day() vs mday() vs wday() vs yday() vs qday()
Five extractors return a "day" number, each measuring a different calendar position.
| Function | Returns | Range | Typical use |
|---|---|---|---|
day() |
Day of month | 1 to 31 | Default; same as mday() |
mday() |
Day of month | 1 to 31 | Alias of day(); explicit name |
wday() |
Day of week | 1 to 7 (or label) | Weekday analysis |
yday() |
Day of year | 1 to 366 | Seasonality, julian day |
qday() |
Day of quarter | 1 to 92 | Fiscal quarter cadence |
day() and mday() are exactly the same function under different names. Use mday() in code that reads alongside wday() and yday(), because the three-letter prefixes line up and the intent is obvious. Use day() when the context already makes the month-day meaning clear.
July 15, 2024 is the 15th day of the month, the second day of the ISO week (Monday), the 197th day of the year, and the 15th day of Q3 (which starts July 1). All five are correct answers to different questions.
Common pitfalls
Pitfall 1: passing a character string. day("2024-07-15") errors because lubridate does not auto-parse strings inside day(). Wrap with ymd() first: day(ymd("2024-07-15")). The same applies to slash-separated formats: parse, then extract.
Pitfall 2: assigning day = 31 to a 30-day month. day(ymd("2024-04-15")) <- 31 returns "2024-05-01" because April has only 30 days. Lubridate rolls forward instead of erroring. If you want a fail-fast behavior, cap the value with pmin(target_day, days_in_month(x)) before assigning.
day() with wday() is the most common bug. day(ymd("2024-07-15")) returns 15 (day of month). wday(ymd("2024-07-15")) returns 2 (Monday as day-of-week). The two look interchangeable in code review but produce silently wrong group-by results. Always read the function name carefully.day(x) is s.dt.day on a datetime Series. The dplyr pipeline mutate(dom = day(order_date)) mirrors df.assign(dom=df.order_date.dt.day). Pandas calls weekday s.dt.dayofweek, which corresponds to lubridate's wday().A practical workflow with day()
Day-of-month shows up in three places: filtering to a window, building features for a model, and pairing with days_in_month() for safe month-end logic.
The patterns:
- Filter to a within-month window:
filter(day(date) <= 7)keeps the first week;filter(day(date) >= 25)keeps the last week. - Build features for a model: extract
day()alongsidemonth(),year(), andwday()when daily seasonality matters. - Safe month-end checks: combine
day(x) == days_in_month(x)rather than hard-codingday(x) == 31.
For ggplot2 time series, prefer floor_date(x, "day") over day() when you need a Date axis. day() returns an integer 1-31 that loses month and year context. floor_date() keeps the full Date class.
Try it yourself
Try it: Use the orders tibble above and filter to rows where the day of month is between 10 and 20 inclusive. Save the result to ex_mid_month.
Click to reveal solution
Explanation: between(day(order_date), 10, 20) is shorthand for day(...) >= 10 & day(...) <= 20. The result keeps January 15 and February 14, which fall inside the inclusive 10-to-20 window.
Related lubridate functions
After mastering day(), look at:
mday(),wday(),yday(),qday(): extract other day-position numbersyear(),month(): extract the other date partshour(),minute(),second(): extract time-of-day componentsdays_in_month(): get the last valid day for any monthfloor_date(),ceiling_date(): round a date to day, week, month, or yearmake_date(),make_datetime(): build a date from year, month, day integersymd(),mdy(),dmy(),ymd_hms(): parse strings into dates first
For the official reference, see the lubridate day() documentation.
FAQ
How do I extract the day of the month from a date in R?
Use lubridate::day(x) where x is a Date or POSIXct value. The result is an integer between 1 and 31. Base R alternatives are format(x, "%d"), which returns a string, or as.POSIXlt(x)$mday, which returns an integer. The lubridate version is the shortest, fully vectorised, and works directly inside dplyr pipelines.
What is the difference between day() and mday() in lubridate?
There is no functional difference. mday() is an alias of day(), and both return the day of the month. Use mday() when the surrounding code also uses wday() and yday(), because the three-letter prefixes line up visually. Use day() in standalone code where the meaning is already clear from context.
How do I get the day of the week from a date in R?
Reach for wday(), not day(). wday(x) returns an integer 1 to 7 with Sunday as 1 by default. Pass label = TRUE to get an ordered factor of weekday names like Mon, Tue, Wed. To start the week on Monday, set week_start = 1: wday(x, label = TRUE, week_start = 1).
Can I change the day of a date in R?
Yes, with the replacement form: day(x) <- 1. This sets the day while keeping year, month, and time untouched. It is vectorised, so day(date_vector) <- 1 snaps an entire column to the first of the month. Be careful with values above the month's length: day(ymd("2024-04-15")) <- 31 rolls forward to May 1 because April has only 30 days.
How do I get the day of the year (Julian day) in R?
Use yday() from lubridate: yday(ymd("2024-07-15")) returns 197. The range is 1 to 365 in normal years and 1 to 366 in leap years. This is the standard "ordinal date" used in time-series and seasonality work. The base R equivalent is as.POSIXlt(x)$yday + 1 (lubridate is one-indexed; POSIXlt is zero-indexed).