lubridate year() in R: Extract Year from Dates
The year() function in lubridate returns the year component of a Date or POSIXct value as an integer. It is vectorised, so a column of dates becomes a column of years in one call.
year(ymd("2024-07-15")) # extract year from a Date
year(now()) # year of current datetime
year(as.Date("2024-07-15")) # works on base Date too
year(c(ymd("2023-01-01"), ymd("2024-06-30"))) # vectorised
year(x) <- 2025 # replace year in place
isoyear(ymd("2024-12-30")) # ISO 8601 week year
epiyear(ymd("2024-12-30")) # epidemiological year
df %>% mutate(yr = year(order_date)) # extract a columnNeed explanation? Read on for examples and pitfalls.
What year() does in one sentence
year() returns the calendar year of a date-time as an integer. Pass any Date, POSIXct, or POSIXlt vector and you get back a numeric vector of the same length.
This is the lubridate counterpart to base R's format(x, "%Y") and as.POSIXlt(x)$year + 1900. The lubridate version is shorter, vectorised, and returns a numeric value you can use directly in arithmetic or comparisons.
Syntax
year(x) takes one argument: a Date or date-time vector. The return value is an integer vector of the same length.
The input can be any class that lubridate recognises as a date or datetime, including base Date, POSIXct, and POSIXlt. Character strings are NOT accepted directly. Parse them first with ymd(), mdy(), or as.Date().
year(x) <- value to overwrite the year in place. This is the assignment form (replacement function) and it edits the date without changing the month, day, or time of day. Useful for shifting a series of dates to a different year.Five common patterns
1. Extract year from a single date
The result is integer 2024, not a string "2024". Use it in arithmetic directly: year(d) + 1 returns 2025.
2. Extract year from a vector of dates
year() is fully vectorised. A 1-million-row date column becomes a 1-million-row integer vector in one call. No loop or sapply() needed.
3. Replace year values in place
The replacement form keeps month, day, hour, minute, and second untouched. This is the cleanest way to "rebase" a series of dates onto a new year, for example when reusing a calendar of holidays.
4. Use year() inside a dplyr pipeline
mutate(yr = year(order_date)) adds a year column. group_by(yr) then summarise() rolls up the amounts. This is the canonical pattern for time-bucketed analysis.
5. Filter rows by year
year(order_date) == 2023 evaluates the expression row by row and keeps only matching dates. Combine with between() to get a year range: filter(between(year(order_date), 2022, 2023)). To filter to the current year, swap the literal for year(today()).
year() on a column is faster than format(x, "%Y") on the same column. lubridate operates on the underlying numeric representation directly. format() allocates a string for every row, then you parse it back. On a million-row column the difference is roughly 5x.year() vs isoyear() vs epiyear()
Three functions return a "year", but each uses a different calendar definition.
| Function | Calendar | When does the year start? | Use case |
|---|---|---|---|
year() |
Gregorian | January 1 | Default for almost all analysis |
isoyear() |
ISO 8601 | Week 1 contains Jan 4 | Weekly reports, fiscal weeks |
epiyear() |
CDC/MMWR | Week 1 contains Jan 4 (Sun start) | Public health, surveillance |
The three usually agree, but they disagree at the year boundary. December 30, 2024 is Monday of an ISO week that contains January 1, 2025. So year() says 2024, but isoyear() says 2025.
If you are aggregating by isoweek(), pair it with isoyear() to keep weeks and years consistent. Mixing year() with isoweek() produces off-by-one errors at year boundaries.
Common pitfalls
Pitfall 1: passing a character string. year("2024-07-15") errors because lubridate does not auto-parse strings inside year(). Wrap with ymd() first: year(ymd("2024-07-15")). The same applies to slash-separated and other formats: parse, then extract.
Pitfall 2: using year() with isoweek(). Combining Gregorian year with ISO week causes off-by-one errors at the December/January boundary. If you call isoweek(), also call isoyear(). The same pairing rule holds for epiweek() and epiyear().
year(ymd("2024-02-29")) <- 2023 returns "2023-03-01" because 2023 is not a leap year and February 29 does not exist. Lubridate rolls forward instead of erroring. Check leap-day inputs explicitly before reassigning the year.year(x) is s.dt.year on a datetime Series. The dplyr pipeline mutate(yr = year(order_date)) mirrors df.assign(yr=df.order_date.dt.year).A practical workflow with year()
The three places year() shows up most often: filtering, grouping, and feature engineering for models.
The patterns:
- Filter to a year of interest:
filter(year(date) == 2024)to keep only this year's rows. - Group and summarise: add a
yrcolumn withmutate(), thengroup_by(yr)for annual roll-ups. - Build features for a model: extract
year(),month(),day(),wday()as predictors when the date itself is too granular.
For long time series, prefer floor_date(x, "year") over year() when you want a Date column to plot on. year() returns an integer, which is fine for grouping but loses the date class needed by scale_x_date() in ggplot2.
Try it yourself
Try it: Take the orders tibble above and compute total amount per year using year() inside group_by() directly (no mutate() step). Save the result to ex_yearly.
Click to reveal solution
Explanation: dplyr lets you compute the grouping column inline: group_by(yr = year(order_date)). This skips a separate mutate() step and gives the new column a clean name in one go.
Related lubridate functions
After mastering year(), look at:
month(),day(),wday(),qday(),yday(): extract other date partshour(),minute(),second(): extract time-of-day componentsisoyear(),epiyear(): year using ISO 8601 or epi-week calendarsfloor_date(),ceiling_date(): round a date to year, quarter, month, weekmake_date(),make_datetime(): construct dates from year, month, day integersymd(),mdy(),dmy(),ymd_hms(): parse strings into dates first
For the official reference, see the lubridate year() documentation.
FAQ
How do I extract just the year from a date in R?
Use lubridate::year(x) where x is a Date or POSIXct. The result is an integer like 2024. Base R alternatives are format(x, "%Y") (returns string) or as.POSIXlt(x)$year + 1900 (returns integer with a 1900 offset). The lubridate version is the shortest and works on vectors directly.
How do I get the current year in R?
Combine year() with today() or now(): year(today()). This returns the current calendar year as an integer in your session's time zone. For a UTC year boundary use year(today("UTC")).
What package is year() in R?
The year() function is exported by the lubridate package, part of the tidyverse. Load it with library(lubridate) or call it namespaced as lubridate::year(x). Base R has no year() function. The closest base equivalents are format(x, "%Y") and as.POSIXlt(x)$year + 1900.
What is the difference between year() and isoyear()?
year() uses the Gregorian calendar starting January 1. isoyear() uses the ISO 8601 calendar where week 1 is the week containing January 4. They disagree on the few days at the December and January boundary. Use isoyear() only when paired with isoweek() for weekly reporting.
Can I change the year of a date in R?
Yes, with the replacement form: year(x) <- 2025. This sets the year while keeping month, day, and time. It is vectorised, so year(date_vector) <- 2025 updates an entire column. Be careful with February 29 inputs: the assignment rolls forward to March 1 in non-leap years rather than erroring.