End of previous quarter / last day of previous quarter

5.8k Views Asked by At

Using lubridate, how to calculate the last day of the previous quarter for a given date? The below formula doesn't seem to work for Nov 3rd, 2014 (other dates work)

library(lubridate)
date = as.POSIXct("2014-11-03")
date - days(day(date)) - months(month(date) %% 3 - 1)
# NA

Interesting enough, changing order works:

date - months(month(date) %% 3 - 1) - days(day(date))
# "2014-09-30 UTC"
4

There are 4 best solutions below

2
On BEST ANSWER

Here are some possibilities with functions from packages zoo and timeDate, and base R. The zoo code was improved by @G.Grothendieck, and he also suggested the base alternative (thanks a lot!). I leave the lubridate solution(s) to someone else.

First, use class yearqtr in package zoo to represent the quarterly data. You may then use as.Date.yearqtr and the frac argument "which is a number between 0 and 1 inclusive that indicates the fraction of the way through the period that the result represents. The default is 0 which means the beginning of the period" (see ?yearqtr, and ?yearmon for frac).

Step by step:

library(zoo)

date <- as.Date("2014-11-03")

# current quarter
current_q <- as.yearqtr(date)
current_q
# [1] "2014 Q4"

# first date in current quarter
first_date_current_q <- as.Date(current_q, frac = 0)
first_date_current_q 
# [1] "2014-10-01"

# last date in previous quarter
last_date_prev_q <- first_date_current_q - 1
last_date_prev_q
# [1] "2014-09-30"

And a short version by @G.Grothendieck (thanks!)

as.Date(as.yearqtr(date)) - 1
# [1] "2014-09-30"

A nice base R solution by @G.Grothendieck

as.Date(cut(date, "quarter")) - 1
# [1] "2014-09-30"

Another possibility is to use timeFirstDayInQuarter and timeLastDayInQuarter functions in package timeDate:

library(timeDate)
timeLastDayInQuarter(timeFirstDayInQuarter(date) - 1)
# GMT
# [1] [2014-09-30]
0
On

I know this is old, but since this question specifically asks for a lubridate solution and I couldn't find working code for it elsewhere, I figured I'd post, keeping in mind that the base R solution is likely the most succinct:

> sampleDate <- ymd('2015-11-11')
> yearsToSample <- years(year(sampleDate) - year(origin))
> yearsToSample
[1] "45y 0m 0d 0H 0M 0S"
> additionalMonths <- months((quarter(sampleDate) - 1) * 3)
> additionalMonths
[1] "9m 0d 0H 0M 0S"
> startOfQuarter <- ymd(origin) + yearsToSample + additionalMonths - days(1)
> startOfQuarter
[1] "2015-09-30 UTC"

Confirmed that this works for dates previous to the origin ('1970-01-01') as well.

0
On

Here is a pure lubridate solution for 2021:

> date <- ymd("2014-11-03")
> yq(quarter(date, with_year = TRUE)) - days(1)
[1] "2014-09-30"

Breaking it down:

> # The year and quarter
> quarter(date, with_year = TRUE)
[1] 2014.4

> # The first day of the quarter as a date
> yq(quarter(date, with_year = TRUE))
[1] "2014-10-01"

# The day before the first day of the quarter as a date
> yq(quarter(date, with_year = TRUE)) - days(1)
[1] "2014-09-30"
0
On

you can change the unit for year, month or quarter

floor_date(Sys.Date(), unit = "quarter") - 1