New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

polish-statutory-interest

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

polish-statutory-interest

Zero-dependency calculation of Polish statutory late-payment interest, segmented across NBP reference-rate changes and half-year commercial rates.

latest
Source
npmnpm
Version
0.1.1
Version published
Maintainers
1
Created
Source

polish-statutory-interest

Zero-dependency calculation of Polish statutory late-payment interest, correctly segmented across rate changes.

Computing this looks like a one-liner:

interest = amount × rate × days / 365

That is wrong often enough to matter, because the rate changes while the debt is outstanding, and Poland has two separate statutory regimes that change on two different schedules.

The two regimes

Odsetki ustawowe za opóźnienie (art. 481 § 2 Kodeksu cywilnego) apply to ordinary obligations. The rate is the NBP reference rate plus 5.5 pp, so it moves whenever the central bank moves rates, effective from the date of the decision.

Odsetki za opóźnienie w transakcjach handlowych apply to B2B commercial transactions. Per art. 11b, this rate is pinned per half-year: it is set from the NBP reference rate in force on 1 January for interest accruing in the first half of the year, and on 1 July for the second half. It does not move if the central bank cuts rates in March.

So the same 200-day delay may need five segments under one regime and exactly two under the other. Picking the wrong regime does not just change a number, it changes the shape of the calculation.

Install

npm install polish-statutory-interest

Node 22 or newer. No runtime dependencies.

Usage

import { calculateInterest } from 'polish-statutory-interest';

const result = calculateInterest({
  amountGrosze: 1_230_000,   // 12 300,00 zł, in minor units
  dueDate: '2026-05-31',     // termin płatności
  calcDate: '2026-08-31',    // interest calculated up to and including this day
  regime: 'commercial',
});
{
  "regime": "commercial",
  "totalDays": 92,
  "segments": [
    { "from": "2026-06-01", "to": "2026-06-30", "days": 30, "ratePercent": 14,    "interestGrosze": 14153 },
    { "from": "2026-07-01", "to": "2026-08-31", "days": 62, "ratePercent": 13.75, "interestGrosze": 28728 }
  ],
  "totalInterestGrosze": 42881,
  "commercial": { "rate": 13.75, "halfYearStart": "2026-07-01", "halfYearLabel": "II polrocze 2026", "healthcareDebtor": false }
}

The delay crossed 1 July, so the commercial rate stepped from 14,00% to 13,75% on that exact day and the calculation split in two. Under the civil regime the same window would split on NBP decision dates instead.

Keep the segments, not just the total. When someone disputes the figure, "here are the periods and the rate in each" ends the conversation. A single number does not.

Design decisions worth knowing

Money is integer grosze. 0.1 + 0.2 !== 0.3, and interest is a sum of many small terms. Accumulating in floats produces drift that surfaces as a one-grosz disagreement with the other side's accountant. amountGrosze must be a positive integer or the call throws.

Rounding happens per segment. Each segment is a distinct legally defined period, so it is the natural unit to round on. This is a modelling decision rather than a mathematical one, and it is documented rather than incidental. Whichever convention you pick, pick it deliberately.

Interest starts the day after the due date. An off-by-one over a 400-day delay is a whole day of interest, not a rounding error.

Dates are ISO strings and all arithmetic is UTC, so a delay spanning the spring clock change does not silently lose or gain a day.

Unknown dates throw rather than guess. The reference-rate history starts 2020-05-29 (EARLIEST_KNOWN_DATE); asking about an earlier date raises a RangeError instead of quietly returning a wrong rate.

Formatting is not included. Polish CLDR sets minimumGroupingDigits=2, so four-digit numbers are not grouped: 1234,56, but 100 000,00. Hand-rolled formatters get this wrong. Use the platform:

new Intl.NumberFormat('pl-PL', { minimumFractionDigits: 2 }).format(result.totalInterestGrosze / 100);

API

ExportPurpose
calculateInterest(input)Accrued interest with a per-segment breakdown
referenceRateOn(iso)NBP reference rate in force on a date
civilLateRateOn(iso)Odsetki ustawowe za opóźnienie, art. 481 § 2 KC
maxLateRateOn(iso)Odsetki maksymalne, twice the civil rate
commercialRateOn(iso, opts)Commercial rate with its half-year label; healthcareDebtor uses the 8 pp margin
recoveryCompensationEur(amountGrosze)Rekompensata tier: 40, 70 or 100 EUR
daysBetween, addDays, halfYearStartISO date helpers used internally
NBP_REFERENCE_RATES, OFFSETS, EARLIEST_KNOWN_DATEThe underlying configuration

Rekompensata

Commercial transactions also carry a fixed recovery-cost compensation under art. 10 ustawy o przeciwdziałaniu nadmiernym opóźnieniom, owed by operation of law with no need to prove costs: 40 EUR below 5 000 zł, 70 EUR from 5 000 zł, 100 EUR from 50 000 zł.

Converting it to złoty requires the NBP mid rate from the last working day of the month preceding the payment due date. That is left to the caller so this library makes no network calls.

Keeping rates current

NBP_REFERENCE_RATES is the single source of truth. When the Rada Polityki Pieniężnej moves the reference rate, add one entry; every statutory rate is derived, so no percentage is hardcoded anywhere. Pull requests updating it are welcome.

Accuracy

Rates are checked against the published obwieszczenia: II półrocze 2026 = 13,75%, I półrocze 2026 = 14,00%, II półrocze 2022 = 16,00%, I półrocze 2022 = 11,75%. The suite also covers half-year pinning, the mid-half-year central-bank move that must not be followed, segment tiling with no gaps or overlaps, and DST boundaries.

This is a calculation library, not legal advice.

Licence

MIT. Built while working on WezwaniePro, a free generator for Polish payment demands and interest notes, where this arithmetic took the longest to get right.

Keywords

odsetki

FAQs

Package last updated on 02 Aug 2026

Related posts