In the UK many organisations do not talk about financial years by referring to a specific four-number year (as with _the 2024 financial year_), instead they write the two years side-by-side (as with _the 2024-2025 financial year_).
The Lubridate package in #R provides a great deal of useful date-formating tools but it does not cover this use case.
Let's say that we have the date 31/08/24 and that the financial year starts on April 01st.
```
library(tidyverse)
our_date <- ymd(20240831)
our_fiscal_month <- 4`
```
# Modifying the code
Lubridate's fiscal year function is `quarter()`.
`quarter()` has four different [documented formats](https://lubridate.tidyverse.org/reference/quarter.html):
* *"quarter" - return numeric quarter (default),
* _"year.quarter" return fractional numeric year.quarter_
* _"date_first" or "date_last" which return the date at the quarter's start end end._
Of these, "year.quarter" is the most promising.
Let's run it with our date:
`quarter(our_date, fiscal_start = our_fiscal_month, type = "year.quarter")
`[1] 2025.2`
``
That is a pretty good start, but we can do one better.
# Undocumented formats
If we look at quarter by running `quarter` , without the brackets, in the console we can see it refers to another format "year_start/end". This is much closer to what we want:
`quarter(our_date, fiscal_start = our_fiscal_month, type = "year_start/end")
`[1] "2024/25 Q2"
I don't know why this format isn't documented but it's much closer to what we need.
# Making our own function
While our function is close, what we really want is _2024-2025_. To do this we're going to want to make our own function.
Lets run the function in the console to see how it works:
```
lubridate::quarter
function (x, type = "quarter", fiscal_start = 1, with_year = identical(type,
"year.quarter"))
{
if (length(fiscal_start) > 1) {
stop("`fiscal_start` must be a singleton", call. = FALSE)
}
fs <- (fiscal_start - 1)%%12
shifted <- seq(fs, 11 + fs)%%12 + 1
m <- month(x)
quarters <- rep(1:4, each = 3)
s <- match(m, shifted)
q <- quarters[s]
if (is.logical(type)) {
type <- if (type)
"year.quarter"
else "quarter"
}
if (with_year == TRUE) {
type <- "year.quarter"
}
switch(type, quarter = q, year_start/end` = , year.quarter = {
nxt_year_months <- if (fs != 0) (fs + 1):12
y = year(x) + (m %in% nxt_year_months)
out = y + (q/10)
if (type == "year_start/end") {
out = sprintf("%d/%d Q%d", y - 1, y%%100, q)
}
out
}, date_first = , date_last = {
starting_months <- shifted[seq(1, length(shifted), 3)]
final_years <- year(x) - (starting_months[q] > m)
quarter_starting_dates <- make_date(year = final_years,
month = starting_months[q], day = 1L)
if (type == "date_first") {
quarter_starting_dates
} else if (type == "date_last") {
add_with_rollback(quarter_starting_dates, months(3)) -
days(1)
}
}, stop("Unsuported type ", type))
}
```
Even if we don't understand the function in full, we can probably see the basic pattern is that it calculates out the fiscal year and then makes the correct output type based on the *type* argument.
We know that "year_start/end" is the closest to what we want.
```
if (type == "year_start/end") {
out = sprintf("%d/%d Q%d", y - 1, y%%100, q)
}
out
```
In we look up the function sprintf, we see the first arguement shows the format of the text. %d is used to show the result should be a digit. So "%d/%d Q%d" means that it should show a digit, then a "/", then a digit, then a space, then a Q and finally a digit. Three different digits. The next three arguments give the values for the different digits.
`y` is defined above as the year. So the first digit is the year minus one.
`y%%100` is the remainder of the division of the year by 100. Helpfully this is the year in single digits. Finally the last digit is just a quarter.
Knowing this we know that to get 2024-2025 we just need to get the remainder of the year minus 1 divided by 100, "-" symbol, and the remainder of the year divided by 100.
# Our function
Now we know enough to create our own function
```
UK_fiscal_year <- function(date, fiscal_start = 4, short = FALSE) {
# Get the 'from_year' directly from the date
# If the month is greater or equal to the fiscal month leave it alone, otherwise take away 1 year
from_year <- lubridate::year(date) + ifelse(lubridate::month(date) >= fiscal_start, 0, -1)
# 'to_year' should always be the next calendar year
to_year <- from_year + 1
# Return the fiscal year in "from_year-to_year" format
if(short) sprintf("%d-%d", from_year%%100, to_year%%100)
else sprintf("%d-%d", from_year, to_year)
}
```
``
I used the short variable to switch between the full year and only the last two digits.
And here is a demo of the first twelve months of the year:
```
UK_fiscal_year(make_date(y=2024,month = 1:12))
[1] "2023-2024" "2023-2024" "2023-2024" "2024-2025" "2024-2025" "2024-2025" "2024-2025" "2024-2025" "2024-2025" "2024-2025" "2024-2025"
[12] "2024-2025"
UK_fiscal_year(make_date(y=2024, month = 1:12), short = TRUE)
[1] "23-24" "23-24" "23-24" "24-25" "24-25" "24-25" "24-25" "24-25" "24-25" "24-25" "24-25" "24-25"
```