Functional Weave
Code in Rust

legal.limitation-date

The last day to bring a claim under the Limitation Act 1980, by claim type, from the date it accrued.

1.0.0 (not the latest) · published 2026-10-03 by charlie · Anterra

Pinned by 26 tests, run in TypeScript, Python and Rust.

Not professional advice. This capability calculates legal figures from published rules. It is a software component for developers, not legal advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have a litigation solicitor review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.

What it does

Status: needs review by a solicitor before it is published; it does not handle disability, fraud, concealment or date-of-knowledge disputes.

The last day on which a claim may be brought in England and Wales under the Limitation Act 1980, from the claim type and the date the cause of action accrued. The periods are data, one row per claim type, with the section that sets each:

For example

  • limitation_date(contract, 2020-03-15, —) → claim type contract, years 6, section s.5, runs from 2020-03-15, expiry 2026-03-15 a contract breached on 15 March 2020 may be sued on until 15 March 2026
  • limitation_date(tort, 2020-03-15, —) → claim type tort, years 6, section s.2, runs from 2020-03-15, expiry 2026-03-15 the anniversary, not 6 x 365 days (which lands on 14 March 2026)
  • limitation_date(contract, 2020-02-29, —) → claim type contract, years 6, section s.5, runs from 2020-02-29, expiry 2026-02-28 accrued on 29 February: no corresponding date in 2026, so the end of February

The function

The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.

pub fn limitation_date(claim_type: &str, accrual_date: &str, knowledge_date: Option<&str>) -> LimitationPeriod
claim_typestringcontract, tort, personal-injury, fatal-accident, defamation, malicious-falsehood, deed, statute-sum, contribution, recovery-of-land, rent-arrears, mortgage-principal, mortgage-interest, judgment or judgment-interest
accrual_datedatethe date the cause of action accrued (for fatal-accident the date of death; for judgment the date it became enforceable)
knowledge_datedate?personal-injury and fatal-accident only: the claimant's date of knowledge (s.14), or null; must be null for other types
returnsLimitationPeriod

The type it declares, generated into your project

/// The period that applies and the last day it allows.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LimitationPeriod {
    pub claim_type: String,
    pub years: i64,
    /// the section of the Limitation Act 1980 that sets it
    pub section: String,
    /// the accrual date, or the later date of knowledge where the section allows it
    pub runs_from: String,
    /// the last day on which the claim may be brought
    pub expiry: String,
}

Your code names it in one line, in the file that uses it

fune!(legal.limitation-date@^1);  // then call limitation_date(…)
impl/rust.rs · 75 lines · open · raw

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

use super::funejson::Value;  ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
use super::dates_add_days::parse_iso_date;  ← from dates.add-days ^1.0.0 · built alongside by fune
use super::dates_add_months::add_months;  ← from dates.add-months ^1.0.0 · built alongside by fune
use super::legal_limitation_date_data::{LIMITATION_PERIODS, LIMITATION_PERIODS_HISTORY};  ← this capability’s own data, compiled from data/limitation-periods.json into the same file by fune build

/// The last day to bring a claim: the anniversary of the accrual date (or the
/// later date of knowledge) the period's number of years on, the accrual day
/// itself excluded, and the month's last day where no anniversary exists.
///
/// # Panics
/// Panics on an unknown claim type, a bad date, an accrual date before the
/// data, or a knowledge date for a claim type that does not run from knowledge.
pub fn limitation_date(claim_type: &str, accrual_date: &str, knowledge_date: Option<&str>) -> LimitationPeriod {
    parse_iso_date(accrual_date);
    let rows: Vec<_> = LIMITATION_PERIODS.iter().filter(|r| r.claim_type == claim_type).collect();
    if rows.is_empty() {
        let mut known: Vec<&str> = Vec::new();
        for r in LIMITATION_PERIODS {
            if !known.contains(&r.claim_type) {
                known.push(r.claim_type);
            }
        }
        panic!("unknown claim type \"{}\": expected one of {}", claim_type, known.join(", "));
    }
    let rule = match rows
        .iter()
        .find(|r| accrual_date >= r.valid_from && r.valid_to.map_or(true, |to| accrual_date <= to))
    {
        Some(r) => *r,
        None => {
            let earliest = rows.iter().map(|r| r.valid_from).min().unwrap();
            if LIMITATION_PERIODS_HISTORY != "full" && accrual_date < earliest {
                panic!(
                    "no limitation rule for {} accruing on {}: this build was installed with history={}, so it only carries rules from {}. Reinstall with history=full for older claims.",
                    claim_type, accrual_date, LIMITATION_PERIODS_HISTORY, earliest
                );
            }
            panic!("no limitation rule for {} accruing on {}", claim_type, accrual_date);
        }
    };
    let mut runs_from = accrual_date;
    if let Some(knowledge) = knowledge_date {
        if !rule.from_knowledge {
            panic!("a {} claim runs from accrual under {}; knowledgeDate must be null", claim_type, rule.section);
        }
        parse_iso_date(knowledge);
        if knowledge > accrual_date {
            runs_from = knowledge;
        }
    }
    // The corresponding date rule: the anniversary, clamped to the month end.
    let expiry = add_months(runs_from, rule.years * 12);
    LimitationPeriod {
        claim_type: claim_type.to_string(),
        years: rule.years,
        section: rule.section.to_string(),
        runs_from: runs_from.to_string(),
        expiry,
    }
}

pub fn limitation_period_to_value(p: &LimitationPeriod) -> Value {
    Value::obj(vec![
        ("claimType", Value::str(&p.claim_type)),
        ("years", Value::Int(p.years)),
        ("section", Value::str(&p.section)),
        ("runsFrom", Value::str(&p.runs_from)),
        ("expiry", Value::str(&p.expiry)),
    ])
}

pub fn fune_vector(args: &[Value]) -> Value {
    let knowledge = if args[2].is_null() { None } else { Some(args[2].as_str()) };
    limitation_period_to_value(&limitation_date(args[0].as_str(), args[1].as_str(), knowledge))
}

Install

fune build

With that line in your source, in a Rust project (language rust in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Rust package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. A crate’s build.rs runs it before every compile. Or pin a range in fune.project and build in one step:

fune add legal.limitation-date
Download for Rust legal.limitation-date-1.0.0-rust.fune · 23,657 bytes sha256 e49d7a9bb2522b615469e060400a9b48eb9a7dd54f3c543de206001552842270

The manifest, vectors and README with only the Rust implementation. Install it without the registry with fune add ./legal.limitation-date-1.0.0-rust.fune, or fetch it from a terminal with fune pull legal.limitation-date@1.0.0:rust.

The whole function, every language, is one file too: legal.limitation-date-1.0.0.fune, 28,192 bytes, sha256 fb927b7b4d84491a9f291168a574d7603080d0605d6802f9fa4bdc34eb6eaac5. It installs into a project of any language.

Customise it in your app

The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.

before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.

// fune: before legal.limitation-date

after — your function gets the result and the arguments, and returns the final result.

// fune: after legal.limitation-date

replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.

// fune: replace dates.add-days in legal.limitation-date
// fune: replace dates.add-months in legal.limitation-date

step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show legal.limitation-date --steps.

// fune: step legal.limitation-date after <n|label>

Tests

A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.

CaseArgumentsExpected
a contract breached on 15 March 2020 may be sued on until 15 March 2026 contract, 2020-03-15, — → claim type contract, years 6, section s.5, runs from 2020-03-15, expiry 2026-03-15
the anniversary, not 6 x 365 days (which lands on 14 March 2026) tort, 2020-03-15, — → claim type tort, years 6, section s.2, runs from 2020-03-15, expiry 2026-03-15
accrued on 29 February: no corresponding date in 2026, so the end of February contract, 2020-02-29, — → claim type contract, years 6, section s.5, runs from 2020-02-29, expiry 2026-02-28
accrued on 28 February: the anniversary, not 29 February in a leap year tort, 2019-02-28, — → claim type tort, years 6, section s.2, runs from 2019-02-28, expiry 2025-02-28
accrued on 31 December: expires 31 December, not 30th or 1 January deed, 2014-12-31, — → claim type deed, years 12, section s.8(1), runs from 2014-12-31, expiry 2026-12-31
personal injury with no later knowledge: 3 years from the accident personal-injury, 2023-06-01, — → claim type personal-injury, years 3, section s.11(4), runs from 2023-06-01, expiry 2026-06-01
personal injury: a later date of knowledge starts the clock personal-injury, 2015-01-10, 2024-11-30 → claim type personal-injury, years 3, section s.11(4), runs from 2024-11-30, expiry 2027-11-30
personal injury: a knowledge date before accrual changes nothing personal-injury, 2023-06-01, 2023-05-01 → claim type personal-injury, years 3, section s.11(4), runs from 2023-06-01, expiry 2026-06-01
fatal accident: 3 years from the date of death fatal-accident, 2024-02-29, — → claim type fatal-accident, years 3, section s.12(2), runs from 2024-02-29, expiry 2027-02-28
defamation: one year from publication defamation, 2025-10-01, — → claim type defamation, years 1, section s.4A, runs from 2025-10-01, expiry 2026-10-01
Show the other 16 tests
CaseArgumentsExpected
defamation accruing on the day the 1996 Act's one-year period began defamation, 1996-09-04, — → claim type defamation, years 1, section s.4A, runs from 1996-09-04, expiry 1997-09-04
malicious falsehood: one year malicious-falsehood, 2026-01-31, — → claim type malicious-falsehood, years 1, section s.4A, runs from 2026-01-31, expiry 2027-01-31
contribution: two years from the judgment contribution, 2025-07-14, — → claim type contribution, years 2, section s.10(1), runs from 2025-07-14, expiry 2027-07-14
recovery of land: twelve years recovery-of-land, 2010-04-06, — → claim type recovery-of-land, years 12, section s.15(1), runs from 2010-04-06, expiry 2022-04-06
rent arrears: six years from the day each instalment fell due rent-arrears, 2021-03-25, — → claim type rent-arrears, years 6, section s.19, runs from 2021-03-25, expiry 2027-03-25
mortgage principal: twelve years mortgage-principal, 2016-08-31, — → claim type mortgage-principal, years 12, section s.20(1), runs from 2016-08-31, expiry 2028-08-31
mortgage interest arrears: six years mortgage-interest, 2016-08-31, — → claim type mortgage-interest, years 6, section s.20(5), runs from 2016-08-31, expiry 2022-08-31
an action on a judgment: six years from when it became enforceable judgment, 2020-01-15, — → claim type judgment, years 6, section s.24(1), runs from 2020-01-15, expiry 2026-01-15
judgment interest arrears: six years judgment-interest, 2020-01-15, — → claim type judgment-interest, years 6, section s.24(2), runs from 2020-01-15, expiry 2026-01-15
sums recoverable by statute: six years statute-sum, 1981-05-01, — → claim type statute-sum, years 6, section s.9(1), runs from 1981-05-01, expiry 1987-05-01
an unknown claim type is an error negligence, 2020-01-01, — → error: unknown claim type "negligence"
a knowledge date for a contract claim is an error, not ignored contract, 2020-01-01, 2021-01-01 → error: knowledgeDate must be null
defamation accruing before 4 September 1996 is not carried defamation, 1996-09-03, — → error: no limitation rule for defamation accruing on 1996-09-03
a cause of action before the Act came into force is not carried contract, 1981-04-30, — → error: no limitation rule for contract accruing on 1981-04-30
an impossible accrual date is an error contract, 2021-02-29, — → error: not a real calendar date
an impossible knowledge date is an error personal-injury, 2021-02-01, 2021-13-01 → error: 2021-13-01

More from the author

| claimType | years | from | section | |---|---|---|---| | contract | 6 | accrual | s.5 | | tort | 6 | accrual | s.2 | | personal-injury | 3 | accrual or date of knowledge, if later | s.11(4) | | fatal-accident | 3 | death or dependant's date of knowledge, if later | s.12(2) | | defamation | 1 | accrual (publication) | s.4A | | malicious-falsehood | 1 | accrual | s.4A | | deed | 12 | accrual | s.8(1) | | statute-sum | 6 | accrual | s.9(1) | | contribution | 2 | the judgment, award or agreement | s.10 | | recovery-of-land | 12 | accrual | s.15(1) | | rent-arrears | 6 | the date the arrears fell due | s.19 | | mortgage-principal | 12 | the date the right to the money accrued | s.20(1) | | mortgage-interest | 6 | the date the interest fell due | s.20(5) | | judgment | 6 | the date the judgment became enforceable | s.24(1) | | judgment-interest | 6 | the date the interest fell due | s.24(2) |

**Counting.** The day the cause of action accrues is excluded (Marren v Dawson Bentley & Co Ltd [1961] 2 QB 135), and a period of years ends on the corresponding date, the anniversary (the corresponding date rule, Dodds v Walker [1981] 1 WLR 1027, HL). So a contract breached on 15 March 2020 may be sued on until the end of 15 March 2026, and that date is `expiry`: the last day the claim may be brought. Where there is no corresponding date, the period ends on the last day of that month: a cause of action accruing on 29 February 2020 expires on 28 February 2026 (and on 29 February 2024 for a 4-year period, if there were one). Adding 365 days a year, or subtracting a day from the anniversary, gives the wrong answer; the vectors show both.

**Date of knowledge.** For personal injury and fatal accident claims the period runs from the later of accrual (or death) and the date of knowledge defined in s.14. Pass that date if one is asserted; the answer is only as good as it is, and whether a claimant had knowledge on a given date is exactly the kind of question this does not decide. Other claim types do not run from knowledge under these sections, so a knowledge date for them is an error rather than being silently ignored (latent damage in negligence under s.14A, which does, is not covered).

**Dates in force.** Every row applies to causes of action accruing on or after 1 May 1981, when the Act came into force (s.41(2)); defamation and malicious falsehood use the one-year period for causes of action accruing on or after 4 September 1996 (Defamation Act 1996 s.5, which by s.20(2) does not affect causes of action arising before). An earlier accrual date is an error, not an answer: the old periods are not carried.

## What this deliberately does not do

- Extensions and postponements: disability (s.28), fraud, concealment or mistake (s.32), acknowledgement or part payment (ss.29-31), the court's discretion to disapply the personal-injury periods (s.33), and the defamation discretion (s.32A). - Latent damage (ss.14A, 14B, including the 15-year longstop), product liability (s.11A, 10-year longstop), theft (s.4), loans without a date for repayment (s.6), buildings (s.4B), and every limit outside this Act (employment tribunals, judicial review, the Equality Act, contractual time bars, other jurisdictions). - When the last day falls on a day the court office is closed: the claim may generally be issued on the next day it is open (Pritam Kaur v S Russell & Sons Ltd [1973] QB 336), but whether that applies to a given claim and filing route is a question for the solicitor; `expiry` is the unextended date. - Which date the cause of action accrued on. That is the legal question, and it is the caller's input.

## Sources

Limitation Act 1980 (c. 58), latest version on legislation.gov.uk, read on 23 September 2026: sections 2, 4A, 5, 8, 9, 10, 11, 12, 15, 19, 20, 24 and 41, https://www.legislation.gov.uk/ukpga/1980/58/contents; section 4A amendment note (Defamation Act 1996 ss.5, 20(2), from 4 September 1996), https://www.legislation.gov.uk/ukpga/1980/58/section/4A.

A change to a period is a new version of this capability with a new row; the rows declare effective dates, so a `history=current` build refuses a date before its horizon rather than answering it with today's rule.

Files

PathBytes
README.md4,645
data/limitation-periods.json4,075
impl/python.py2,225
impl/rust.rs3,218
impl/typescript.ts2,163
vectors.json6,694