lubridate month() in R: Extract or Set Month
The month() function in lubridate returns the month of a Date or POSIXct value as an integer 1 to 12, or as an ordered factor of month names when you pass label = TRUE. It is vectorised and works on any class lubridate recognises as a date.
month(ymd("2024-07-15")) # 7 (integer)
month(ymd("2024-07-15"), label = TRUE) # Jul (ordered factor)
month(ymd("2024-07-15"), label = TRUE, abbr = FALSE) # July
month(now()) # month of current datetime
month(c(ymd("2024-01-01"), ymd("2024-06-30"))) # vectorised
month(x) <- 12 # replace month in place
df %>% mutate(m = month(order_date)) # extract a column
table(month(sales$date, label = TRUE)) # monthly countsNeed explanation? Read on for examples and pitfalls.
What month() does in one sentence
month() returns the calendar month of a date-time, either as an integer 1 to 12 or as an ordered factor of month names. Pass any Date, POSIXct, or POSIXlt vector and you get back a vector of the same length.
This is the lubridate counterpart to format(x, "%m") and as.POSIXlt(x)$mon + 1 in base R. The lubridate version is shorter, vectorised, and the label = TRUE mode returns names already wrapped in an ordered factor, which means ggplot2 and dplyr::arrange() keep the months in calendar order automatically.
Syntax
month(x, label = FALSE, abbr = TRUE, locale = Sys.getlocale("LC_TIME")) takes one required argument and three optional ones. The return type changes based on label.
The integer return is what you want for arithmetic and grouping; the ordered factor return is what you want for plotting and tables. Character strings are not accepted directly. Parse first with ymd(), mdy(), or as.Date().
label = TRUE whenever the month appears in a chart or table. The ordered factor keeps January through December in calendar order, so ggplot2 does not alphabetise them to Apr, Aug, Dec... and you avoid the most common monthly-report bug.Six common patterns
1. Extract the month from a single date
The result is an integer between 1 and 12. Useful for filtering: month(d) == 7 is a clean check for July.
2. Get the abbreviated or full month name
abbr = TRUE (the default when label = TRUE) gives the 3-letter abbreviation. abbr = FALSE gives the full name. Both come back as ordered factors with 12 levels.
3. Vectorise over a column
The function applies element by element, which is what you want inside mutate() or transform().
4. Group and summarise sales by month
(Output truncated to the first 3 months.) Using label = TRUE here means the printed table reads as Jan, Feb, Mar rather than 1, 2, 3.
5. Replace the month in place
The replacement form keeps the year, day-of-month, and time of day, and only swaps the month. This is the lubridate idiom for "move this event from July to December" without rebuilding the date string.
6. Filter rows to a specific month
A clean alternative to parsing the string or using regex on format(date, "%m").
label = FALSE) plugs into arithmetic, comparisons, and dplyr::group_by(). Factor mode (label = TRUE) plugs into plotting axes, summary tables, and any output a human will read. Pick the type for the consumer, not the producer.Compare with alternatives
month() is one of several ways to pull the month from a date in R. Each has a different return type and different ergonomics.
| Approach | Return type | Notes |
|---|---|---|
month(x) |
integer | Vectorised, fast, default lubridate idiom |
month(x, label = TRUE) |
ordered factor | Best for charts and reports |
format(x, "%m") |
character (zero-padded) | Base R, returns "07" not 7 |
format(x, "%B") |
character | Base R, full month name, no ordering |
as.POSIXlt(x)$mon + 1 |
integer | Base R, needs +1 because mon is 0-indexed |
data.table::month(x) |
integer | Same idea, no factor mode |
Rule of thumb: use month(x) for math and grouping, month(x, label = TRUE) for display. Reach for format() only when you need character output for a downstream system that expects a string.
as.POSIXlt(x)$mon is zero-indexed. January is 0, December is 11. Forgetting the + 1 is a classic off-by-one bug. month() returns 1 to 12 directly, which is why most R code now uses lubridate even when base R alone would do.Common pitfalls
Three mistakes appear in almost every code review of date code. All three are easy to avoid once you know the pattern.
- Passing a character string instead of a Date.
month("2024-07-15")errors. Parse first:month(ymd("2024-07-15")). The error message is helpful but the fix is always the same. - Sorting a factor month with
as.character(). Once you callas.character()on thelabel = TRUEoutput, you lose the order andarrange()puts Apr before Jan. Keep it as a factor as long as possible. - Confusing
month()withmonths().month()(singular) extracts a component.months()(plural) builds aPeriodobject for arithmetic:ymd("2024-07-15") + months(3)returns2024-10-15. Different functions, near-identical spelling.
Try it yourself
Try it: Extract the month name (abbreviated) from ymd("2026-11-04") and store it in ex_month. The expected value is Nov as an ordered factor.
Click to reveal solution
Explanation: label = TRUE returns the abbreviated name. abbr defaults to TRUE, so the result is Nov. The ordered factor keeps calendar order, which matters once you plot or arrange.
Related lubridate functions
Reach for these when you need a different component or different operation:
year(x)returns the calendar year as an integerday(x)andmday(x)return the day of monthwday(x, label = TRUE)returns the weekday name as an ordered factorquarter(x)returns the quarter (1 to 4)floor_date(x, "month")rounds a date down to the first of its monthx %m+% months(n)adds n months while handling end-of-month edge cases
For the full reference, see the lubridate documentation on lubridate.tidyverse.org.
FAQ
How do I get the month name instead of the number?
Pass label = TRUE to month(). The function returns an ordered factor with 12 levels in calendar order. The default abbreviation is the 3-letter form (Jan, Feb, Jul). Pass abbr = FALSE to get the full names (January, February, July). The ordered factor is the right type for ggplot2 axes and grouped summaries because it preserves January through December order instead of alphabetising.
What is the difference between month() and months()?
month() (singular) is an accessor. It pulls the month component out of an existing date and returns an integer or factor. months() (plural) is a constructor. It builds a Period object representing a span of N months that you can add to a date with + or %m+%. So month(d) reads a date, while d + months(3) shifts a date three months forward.
Why does month() return a factor sometimes and an integer other times?
The return type depends on the label argument. With label = FALSE (the default) the function returns a plain integer between 1 and 12, suitable for math and group_by(). With label = TRUE it returns an ordered factor of month names suitable for charts and reports. Pick the type for the downstream consumer.
Can month() handle character strings directly?
No. month("2024-07-15") errors because the input is character, not a date. Parse first with ymd(), mdy(), dmy(), or base R's as.Date(), then pipe the result into month(). The two-step pattern is month(ymd(x)), and it works the same way for vectors.
How do I change the language of month names?
Pass a locale argument matching your system's locale strings. On Linux and macOS, month(d, label = TRUE, locale = "fr_FR.UTF-8") returns French names. On Windows, use the platform locale string such as "French_France.1252". The default uses Sys.getlocale("LC_TIME"), so set the system locale once if every chart should match.