Fiscal years and how JavaScript is wrong about months
Developers love working with dates. One day, someone asked themselves, what if the year didn’t start in January, but could start in any month of the year. Welcome to the fascinating world of fiscality.
One of the neat things about fiscal months is that you can’t know in which fiscal year a date is in until you know what the fiscal month is. A date in August 2022 can be in fiscal year 2021 if the fiscal month is September or later and in 2022 otherwise. Also very fun is the fact that some libraries expect months to be represented by the numbers from 0 to 11, while others use 1 to 12. I don’t know about you but, if I see month 3, I assume March, not April. Oh, and JavaScript and Ruby don’t agree.
# ruby
Date.new(2022, 2, 3) # => February 3rd, 2022
// JavaScript
new Date(2022, 2, 3) // => March 3rd, 2022
That means you have to be very careful when passing month indexes from Ruby to JavaScript and vice versa.
Displaying fiscal years
Our customers have long been able to specify what their fiscal month was, but we recently launched a new setting to let the customers customize how to display the fiscal years and quarters in their accounts.
There doesn’t seem to be any standards for displaying fiscal quarters so we provide a few options: the abbreviated fiscal year (ex: FY22) or the full fiscal year (ex: 2022 or 2021/22 when the fiscal month is anything other than January) as well as the option to put the quarter at the front or at the back.
So we end up with something like this:
# The fiscal year is the year it ends so the fiscal year for October 2019
# when the fiscal month of March (3) is the next year (2020)
# Special case for the default fiscal month of January (1) since
# it doesn't span 2 years. The current year is always the fiscal year.
if fiscal_month == 1
return long_fiscal_year_format ? year.to_s : "FY#{year % 100}"
end
before_fiscal_month = month < fiscal_month
if before_fiscal_month
# Render the year the fiscal year ends (the current year)
# ex: 2018/2019 or FY19 if we're in 2019 and the fiscal year ends in 2019
long_fiscal_year_format ? "#{year - 1}/#{year % 100}" : "FY#{year % 100}"
else
# Render the year the fiscal year ends (the next year)
# ex: 2019/2020 or FY20 if we're in 2019 and the fiscal year ends in 2020
long_fiscal_year_format ? "#{year}/#{(year + 1) % 100}" : "FY#{(year + 1) % 100}"
end
Add a few tests and you’ve got yourself a nice simple method for displaying fiscal years.
Making sense of the January = 0 confusion
We had a really hard time knowing when we were using a zero-based index or a one-based index so we made a few changes to make the code much clearer:
- Always use the one-based months in Ruby
- Always use the one-based months in JavaScript
- When we need to use a zero-based month in JavaScript (to create a date for example), first assign it to a variable that ends with index (ex: fiscalMonthIndex), then call the function with that new variable.
- Be generous with comments
For example, when trying to figure out the fiscal quarter, we could do something like this:
// The fiscalMonth goes from 1 - 12, but date.month() goes from 0 - 11
const fiscalMonthIndex = this.options.fiscalMonth - 1;
return (date.month() - fiscalMonthIndex) % 3 === 0;
Now there’s very little confusion when using month numbers.