Functional Weave
Code in TypeScript

subscriptions.trial-end@1.0.0

README.md

1,208 bytes · view raw

# subscriptions.trial-end

A trial of N days that starts on day S covers S and the N - 1 days after it,
so the last free day is S + N - 1 and the first payment is taken on S + N. A
14-day trial starting 1 January is free up to and including 14 January and is
charged on 15 January. Both dates come back, because a sign-up page prints the
first ("your trial ends on 14 January") and the billing system schedules the
second, and deriving one from the other is where the off-by-one creeps in.

Trials are counted in days, never in months: a 30-day trial from 31 January
ends on 1 March in 2026 (2 March's charge), not on "28 February". If the offer
really is "one month free", the first charge is dates.add-months from the
start date and this capability is not the one to use.

A trial of 0 days is no trial: lastTrialDay is null and the first charge is on
the start date itself. A negative length is an error, as is an impossible
start date (2026-02-30), which the dates.add-days kernel refuses to parse.

This works on calendar dates. A trial that starts at 23:59 in one time zone
and is billed at midnight in another is a time-zone decision for the caller;
convert to the billing account's local date first.