lubridate minute() in R: Extract Minutes From Datetimes
The minute() function in lubridate returns the minute-of-hour component of a POSIXct or POSIXlt value as an integer between 0 and 59. It is vectorised across datetime columns, supports in-place replacement, and pairs with hour() and second() for full time-of-day work.
minute(ymd_hms("2024-07-15 14:30:00")) # 30 (the minute-of-hour)
minute(now()) # minute of current datetime
minute(as.POSIXct("2024-07-15 09:15:00")) # works on base POSIXct too
minute(c(ymd_hms("2024-07-15 09:00:00"),
ymd_hms("2024-07-15 21:45:00"))) # vectorised
minute(x) <- 0 # snap to top of the hour
df %>% mutate(m = minute(timestamp)) # extract a column
filter(events, minute(ts) %% 5 == 0) # 5-minute marks only
table(minute(events$ts) %/% 15) # 15-minute bucketsNeed explanation? Read on for examples and pitfalls.
What minute() does in one sentence
minute() returns the minute-of-hour component of a datetime as an integer between 0 and 59. Pass any POSIXct or POSIXlt vector and you get a numeric vector of the same length, with the top of the hour as 0 and the last minute before the next hour as 59.
This is the lubridate counterpart to base R's format(x, "%M") and as.POSIXlt(x)$min. The lubridate version is shorter, vectorised, and returns an integer you can use directly in arithmetic, modulo bucketing, or dplyr::filter().
Syntax
minute(x) accepts a datetime vector and returns an integer vector of the same length. There are no optional arguments. The replacement form minute(x) <- value overwrites the minute while preserving the date, hour, and second.
The input must be a class lubridate recognises as a datetime: POSIXct or POSIXlt. A plain Date has no time component, so minute(as.Date("2024-07-15")) returns 0 after silent coercion to midnight UTC, which is rarely what you want. Parse strings with ymd_hms(), mdy_hms(), or as.POSIXct() first.
minute(x) <- value to overwrite the minute in place. This is the replacement form. It keeps the date, hour, and second untouched, so you can snap an entire column of timestamps to the top of the hour with one assignment without disturbing the date or hour columns.Six common patterns
1. Extract the minute from a single datetime
The result is integer 30, not the string "30". Use it in arithmetic directly: minute(ts) %% 15 returns 0, telling you the timestamp lands on a 15-minute boundary.
2. Extract minutes from a vector of datetimes
minute() is fully vectorised. A million-row column becomes a million-row integer vector in one call. The top of the hour returns 0, the last minute returns 59.
3. Replace minute values in place
The replacement form keeps the date and hour untouched. Pairing it with second(x) <- 0 aligns a column of timestamps to a clean hour boundary. To snap to a specific minute mark, set the value to 15, 30, or 45.
4. Use minute() inside a dplyr pipeline
mutate(m = minute(ts)) adds a minute-of-hour column. This is the start of any analysis that asks "are latency spikes clustered around the top of the hour?" or "do polling jobs land on the same minute marks?".
5. Filter rows on minute marks
minute(ts) %% 15 == 0 keeps timestamps whose minute lands on a multiple of 15 (0, 15, 30, 45), the canonical filter for a quarter-hour cron job. Replace 15 with 5 or 30 to match other polling intervals.
6. Bucket timestamps into 15-minute windows
minute(ts) %/% 15 integer-divides by 15, producing 0, 1, 2, or 3 for the four quarter-hour windows; count() then collapses rows by window. The simplest SLA bucket count when scoped to a single hour. For multi-hour bucketing, prefer floor_date(ts, "15 mins").
minute() returns the minute COMPONENT, while minutes(n) and dminutes(n) build a DURATION of n minutes. The accessor and the constructors share a name root but do opposite things. minute(ts) extracts a 0-to-59 integer from a datetime; ts + minutes(15) adds 15 minutes to a datetime. Mixing them up corrupts feature engineering and produces silently wrong arithmetic.minute() vs hour() vs second() vs format()
Three lubridate accessors split a timestamp into hour, minute, and second integers; format() returns the same parts as a string.
| Function | Returns | Range | Typical use |
|---|---|---|---|
hour() |
Hour of day | 0 to 23 | Diurnal grouping, business-hours filters |
minute() |
Minute of hour | 0 to 59 | Sub-hour bucketing, SLA windows, polling marks |
second() |
Second of minute | 0 to 59.99 | Precision timing, log replay |
format(x, "%H:%M") |
"HH:MM" string | character | Display only; not numeric |
The accessors return integers you can compare and aggregate. format() returns characters that force an as.numeric() conversion if you want to compute on them. Reach for minute(), hour(), second() for analysis; reach for format() for printed output.
14:30:45 returns 14, 30, 45 for hour, minute, second. The three integers reconstruct the time-of-day; format() returns the same data as a single character string.
Common pitfalls
Pitfall 1: confusing minute() with minutes() or dminutes(). minute(x) extracts the minute component (returns 0 to 59). minutes(15) builds a Period of 15 minutes; dminutes(15) builds a Duration of 900 seconds. The first reads from a timestamp, the other two add to one. x + minute(x) is almost always a bug; you probably meant x + minutes(15).
Pitfall 2: passing a Date instead of a POSIXct. minute(as.Date("2024-07-15")) returns 0 because Date has no time component; lubridate silently coerces to midnight UTC. Parse with ymd_hms() or as.POSIXct() first so the time survives.
minute() only works inside a single hour. minute(ts) %/% 15 gives 0, 1, 2, or 3 for one hour, but timestamps in the next hour also produce 0, 1, 2, 3, so groupings collapse across hours. For analysis that spans multiple hours or days, switch to floor_date(ts, "15 mins"), which preserves the full datetime so each 15-minute window remains distinct.minute(x) is s.dt.minute on a datetime Series. The dplyr pipeline mutate(m = minute(ts)) mirrors df.assign(m=df.ts.dt.minute). Pandas also returns a 0-to-59 integer, so the convention matches lubridate exactly.A practical workflow with minute()
Minute of hour shows up in three places: filtering for polling marks, building a sub-hour feature, and bucketing log timestamps for SLA windows.
- Polling marks:
filter(minute(ts) %% 5 == 0)keeps events on 5-minute boundaries; useful for verifying a cron job ran on schedule. - Model features: extract
minute()alongsidehour()andwday()when the response depends on sub-hour timing. - SLA bucketing: prefer
floor_date(ts, "15 mins")for multi-hour windows; useminute(ts) %/% 15only inside a single hour.
Try it yourself
Try it: Use the requests tibble above and filter to rows where the minute is between 15 and 45 inclusive. Save the result to ex_midhour.
Click to reveal solution
Explanation: between(minute(ts), 15, 45) is shorthand for minute(ts) >= 15 & minute(ts) <= 45. The result keeps the 17, 30, and 42 minute rows, which fall inside the inclusive 15-to-45 window.
Related lubridate functions
After mastering minute(), look at:
hour(),second(): extract the other time-of-day componentsday(),month(),year(): extract the date componentswday(),yday(),qday(): extract day-position numbersfloor_date(),ceiling_date(): round a datetime to 5, 15, or 30 minute marksminutes(),dminutes(): build Period or Duration objects to add or subtractmake_datetime(): build a datetime from year, month, day, hour, minute, second integerswith_tz(),force_tz(): shift or set the timezone before extracting parts
For the official reference, see the lubridate minute() documentation.
FAQ
How do I extract the minute from a datetime in R?
Use lubridate::minute(x) where x is a POSIXct or POSIXlt value. The result is an integer 0 to 59. Base R alternatives are format(x, "%M") (returns a string) or as.POSIXlt(x)$min (returns an integer). The lubridate version is shortest, fully vectorised, and slots straight into dplyr pipelines like mutate(m = minute(ts)).
What is the difference between minute() and minutes() in R?
minute() is an accessor that reads the minute out of a datetime, returning a 0-to-59 integer. minutes(n) builds a Period of n minutes you add to a datetime, like now() + minutes(15). The names look alike, the behaviour is opposite. dminutes(n) builds a fixed Duration in seconds for cases where daylight-saving boundaries should not stretch the offset.
Why does minute() return 0 for a Date object?
A Date has no time component, so lubridate silently coerces it to midnight UTC and returns 0. The zero is a coercion artifact, not real data. To get an actual minute value, parse the source with ymd_hms() or as.POSIXct() first so the time survives.
How do I change the minute of a datetime in R?
Use the replacement form: minute(x) <- 0. This sets the minute while keeping the date, hour, and second untouched. It is vectorised, so minute(timestamps) <- 0 snaps an entire column to the top of the hour. Pair with second(x) <- 0 for a clean hour boundary, or set 15, 30, 45 for quarter-hour marks.
How do I bucket timestamps into 15 minute windows?
Inside a single hour, minute(ts) %/% 15 returns 0, 1, 2, or 3. Across multiple hours, prefer floor_date(ts, "15 mins"), which truncates the full datetime to the nearest 15-minute mark and keeps each window distinct across days.