use super::funejson::Value; use super::dates_add_days::{format_iso_date, parse_iso_date, CivilDate}; /// The UK personal tax year begins on 6 April, a leftover of the 1752 calendar /// reform that nobody has been willing to move since. const TAX_YEAR_START_MONTH: i64 = 4; const TAX_YEAR_START_DAY: i64 = 6; /// The UK personal tax year containing a date: 6 April to 5 April inclusive. /// /// The boundary is where this gets implemented wrong. 5 April 2026 belongs to /// 2025/26 and 6 April 2026 belongs to 2026/27, so a comparison written as /// `month >= 4` puts the whole of 1-5 April in the wrong year, and one written /// against 1 April - the company financial year end, a different thing entirely /// - moves the boundary by five days for everybody. /// /// # Panics /// Panics on a malformed or impossible date, or one whose tax year would fall /// outside the supported range. pub fn uk_tax_year(iso: &str) -> UkTaxYear { let date = parse_iso_date(iso); // Compare (month, day) against (4, 6) as a pair. On or after 6 April the // date is in the tax year that started this calendar year; before it, the // date still belongs to the one that started last April. let on_or_after_start = (date.month, date.day) >= (TAX_YEAR_START_MONTH, TAX_YEAR_START_DAY); let start_year = if on_or_after_start { date.year } else { date.year - 1 }; if start_year < 1 || start_year + 1 > 9999 { panic!( "\"{}\" is outside the supported range 0001-01-01 to 9999-12-31", iso ); } UkTaxYear { start: format_iso_date(&CivilDate { year: start_year, month: TAX_YEAR_START_MONTH, day: TAX_YEAR_START_DAY, }), end: format_iso_date(&CivilDate { year: start_year + 1, month: TAX_YEAR_START_MONTH, day: TAX_YEAR_START_DAY - 1, }), label: tax_year_label(start_year), } } /// Just the label, for report headings: "2026/27". pub fn uk_tax_year_label(iso: &str) -> String { uk_tax_year(iso).label } fn tax_year_label(start_year: i64) -> String { // The second half is two digits of the end year, zero-padded, so 1999/00 and // 2000/01 come out right rather than as 1999/0 or 1999/2000. format!("{:04}/{:02}", start_year, (start_year + 1) % 100) } pub fn uk_tax_year_to_value(year: &UkTaxYear) -> Value { Value::obj(vec![ ("start", Value::str(&year.start)), ("end", Value::str(&year.end)), ("label", Value::str(&year.label)), ]) } pub fn fune_vector(args: &[Value]) -> Value { uk_tax_year_to_value(&uk_tax_year(args[0].as_str())) }