Functional Weave
Code in TypeScript

dates.month-boundaries@1.0.0

impl/typescript.ts

1,391 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 { daysInMonth, formatIsoDate, parseIsoDate } from "./dates_add_days.ts";  ← from dates.add-days ^1.0.0 · built alongside by fune
import { type MonthBoundaries } from "./dates_month_boundaries_types.ts";

/**
 * The first and last day of the month containing a date, and its length.
 *
 * Returns all three together because a caller needs them together - a billing
 * period, a statement range, a pro-rata fraction - and because deriving `end`
 * from `start` in caller code is where "31 January plus one month" bugs come
 * from.
 *
 * `end` is the last day of the month, inclusive, not the 1st of the next
 * month. Inclusive is what a statement prints and what a human checks against.
 */
export function monthBoundaries(iso: string): MonthBoundaries {
  const date = parseIsoDate(iso);
  // February is the only month whose length is not a constant, and it follows
  // the full Gregorian rule: 2024 had 29 days, 1900 had 28 (a century year),
  // 2000 had 29 (divisible by 400).
  const days = daysInMonth(date.year, date.month);
  return {
    start: formatIsoDate({ year: date.year, month: date.month, day: 1 }),
    end: formatIsoDate({ year: date.year, month: date.month, day: days }),
    days,
  };
}

/** True when the date is the last day of its month - a common billing trigger. */
export function isMonthEnd(iso: string): boolean {
  const date = parseIsoDate(iso);
  return date.day === daysInMonth(date.year, date.month);
}