Functional Weave
Code in TypeScript

payroll.tax-period@1.0.0

impl/typescript.ts

1,480 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { daysBetween, parseIsoDate } from "./dates_add_days.ts";  ← from dates.add-days ^1.0.0 · built alongside by fune
import { ukTaxYear } from "./dates_uk_tax_year.ts";  ← from dates.uk-tax-year ^1.0.0 · built alongside by fune
import { type PayFrequency, type TaxPeriod } from "./payroll_tax_period_types.ts";

/**
 * The tax week, tax month and payroll period a pay date falls in.
 *
 * The tax year is 365 or 366 days, which is 52 weeks and one or two days over.
 * Those spare days are week 53, and they are the reason `period` exists: HMRC
 * numbers the extra payday 53, 54 or 56 by frequency and taxes it
 * non-cumulatively, so a payroll that just counts weeks gets it wrong.
 */
export function taxPeriod(payDate: string, frequency: PayFrequency): TaxPeriod {
  if (frequency !== "weekly" && frequency !== "fortnightly" && frequency !== "four-weekly" && frequency !== "monthly") {
    throw new RangeError(`unknown pay frequency "${frequency}"`);
  }
  const year = ukTaxYear(payDate);
  const week = Math.floor(daysBetween(year.start, payDate) / 7) + 1;

  // Tax months run from the 6th to the 5th, so the calendar month moves on
  // five days before the tax month does.
  const date = parseIsoDate(payDate);
  let month = date.day >= 6 ? date.month - 3 : date.month - 4;
  if (month <= 0) month += 12;

  let period: number;
  if (frequency === "monthly") period = month;
  else if (week < 53) period = week;
  else period = frequency === "weekly" ? 53 : frequency === "fortnightly" ? 54 : 56;

  return { taxYear: year.label, taxYearStart: year.start, week, month, period };
}