FAILURE MAP
← Case archive

FA-60936 / Bond day-count conventions / Open access

Act/365L denominator selection: a period starting on 29 February uses a 366-day year · case 01

Annual periods beginning on a leap day and periods ending on one are classified the wrong way round.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The leap-day membership test uses [start, end) instead of (start, end].

VERIFIED REPAIR

Count 29 February only when start < 29 Feb <= end.

Unsuccessful approach: Closing both ends still counts a 29 February start date.

Case contract

Inputs start, end ([y,m,d]) and coupon frequency. Days are actual days. For annual frequency the denominator is 366 if any 29 February lies in (start, end], else 365. For other frequencies the denominator is 366 if the end date year is a leap year, else 365. Return days/denominator rounded to 9 decimals.

Why this case matters

Bond accrual and pricing systems depend on exact day-count arithmetic; a single-day error changes settlement cash.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
from fractions import Fraction
N = 1
observations = []
def solve(a, b, freq):
    A = datetime.date(*a)
    B = datetime.date(*b)
    def leap(y):
        return (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
    days = (B - A).days
    if freq == 1:
        has29 = any(leap(y) and A <= datetime.date(y, 2, 29) < B for y in range(A.year, B.year + 1))
        den = 366 if has29 else 365
    else:
        den = 366 if leap(B.year) else 365
    return round(days / den, 9)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression february 29 interval ends 1', [[2023, 2, 28], [2024, 2, 29], 1], 1.0], ['regression february 29 interval ends 2', [[2028, 2, 29], [2029, 2, 28], 1], 1.0], ['partial repair probe 1', [[2104, 2, 29], [2106, 2, 28], 1], 2.0], ['partial repair probe 2', [[1996, 2, 29], [1997, 6, 27], 1], 1.326027397], ['boundary control 1', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['boundary control 2', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['normal control 1', [[2071, 8, 3], [2072, 12, 10], 1], 1.352459016], ['normal control 2', [[2027, 12, 29], [2030, 12, 29], 1], 2.994535519]], [['regression february 29 interval ends 1', [[2096, 2, 29], [2099, 2, 28], 1], 3.0], ['regression february 29 interval ends 2', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 1', [[2000, 2, 29], [2001, 6, 25], 1], 1.320547945], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['normal control 1', [[2028, 12, 15], [2029, 3, 15], 4], 0.246575342], ['normal control 2', [[2077, 12, 6], [2078, 9, 23], 1], 0.797260274], ['normal control 3', [[2025, 4, 30], [2026, 4, 30], 1], 1.0]], [['regression february 29 interval ends 1', [[2096, 2, 29], [2097, 2, 28], 1], 1.0], ['regression february 29 interval ends 2', [[2096, 2, 29], [2096, 6, 30], 1], 0.334246575], ['partial repair probe 1', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 2', [[2104, 2, 29], [2105, 2, 28], 1], 1.0], ['boundary control 1', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['boundary control 2', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['normal control 1', [[1935, 3, 4], [1936, 3, 12], 4], 1.021857923], ['normal control 2', [[2024, 3, 29], [2024, 9, 29], 4], 0.50273224]], [['regression february 29 interval ends 1', [[2024, 2, 29], [2027, 2, 28], 1], 3.0], ['regression february 29 interval ends 2', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 1', [[1996, 2, 29], [1996, 4, 30], 1], 0.167123288], ['partial repair probe 2', [[2024, 2, 29], [2025, 4, 23], 1], 1.147945205], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['normal control 1', [[2103, 12, 29], [2104, 3, 29], 4], 0.24863388], ['normal control 2', [[2076, 11, 19], [2077, 11, 19], 1], 1.0]], [['regression february 29 interval ends 1', [[2048, 2, 29], [2049, 2, 28], 1], 1.0], ['regression february 29 interval ends 2', [[2023, 2, 28], [2024, 2, 29], 1], 1.0], ['partial repair probe 1', [[1980, 2, 29], [1981, 2, 28], 1], 1.0], ['partial repair probe 2', [[2028, 2, 29], [2030, 2, 28], 1], 2.0], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['normal control 1', [[1896, 1, 30], [1896, 4, 30], 4], 0.24863388], ['normal control 2', [[2015, 3, 17], [2016, 3, 17], 1], 1.0]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression february 29 interval ends 11.0027397261.0Failed
regression february 29 interval ends 20.997267761.0Failed
partial repair probe 11.9945355192.0Failed
partial repair probe 21.3224043721.326027397Failed
boundary control 10.2520547950.252054795Passed
boundary control 21.01.0Passed
normal control 11.3524590161.352459016Passed
normal control 22.9945355192.994535519Passed

SHA-256 / b947b526f2540242fc9ef05c64872810ab5a1383d65215153677ac397b008be4

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
from fractions import Fraction
N = 1
observations = []
def solve(a, b, freq):
    A = datetime.date(*a)
    B = datetime.date(*b)
    def leap(y):
        return (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
    days = (B - A).days
    if freq == 1:
        has29 = any(leap(y) and A <= datetime.date(y, 2, 29) <= B for y in range(A.year, B.year + 1))
        den = 366 if has29 else 365
    else:
        den = 366 if leap(B.year) else 365
    return round(days / den, 9)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression february 29 interval ends 1', [[2023, 2, 28], [2024, 2, 29], 1], 1.0], ['regression february 29 interval ends 2', [[2028, 2, 29], [2029, 2, 28], 1], 1.0], ['partial repair probe 1', [[2104, 2, 29], [2106, 2, 28], 1], 2.0], ['partial repair probe 2', [[1996, 2, 29], [1997, 6, 27], 1], 1.326027397], ['boundary control 1', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['boundary control 2', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['normal control 1', [[2071, 8, 3], [2072, 12, 10], 1], 1.352459016], ['normal control 2', [[2027, 12, 29], [2030, 12, 29], 1], 2.994535519]], [['regression february 29 interval ends 1', [[2096, 2, 29], [2099, 2, 28], 1], 3.0], ['regression february 29 interval ends 2', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 1', [[2000, 2, 29], [2001, 6, 25], 1], 1.320547945], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['normal control 1', [[2028, 12, 15], [2029, 3, 15], 4], 0.246575342], ['normal control 2', [[2077, 12, 6], [2078, 9, 23], 1], 0.797260274], ['normal control 3', [[2025, 4, 30], [2026, 4, 30], 1], 1.0]], [['regression february 29 interval ends 1', [[2096, 2, 29], [2097, 2, 28], 1], 1.0], ['regression february 29 interval ends 2', [[2096, 2, 29], [2096, 6, 30], 1], 0.334246575], ['partial repair probe 1', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 2', [[2104, 2, 29], [2105, 2, 28], 1], 1.0], ['boundary control 1', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['boundary control 2', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['normal control 1', [[1935, 3, 4], [1936, 3, 12], 4], 1.021857923], ['normal control 2', [[2024, 3, 29], [2024, 9, 29], 4], 0.50273224]], [['regression february 29 interval ends 1', [[2024, 2, 29], [2027, 2, 28], 1], 3.0], ['regression february 29 interval ends 2', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 1', [[1996, 2, 29], [1996, 4, 30], 1], 0.167123288], ['partial repair probe 2', [[2024, 2, 29], [2025, 4, 23], 1], 1.147945205], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['normal control 1', [[2103, 12, 29], [2104, 3, 29], 4], 0.24863388], ['normal control 2', [[2076, 11, 19], [2077, 11, 19], 1], 1.0]], [['regression february 29 interval ends 1', [[2048, 2, 29], [2049, 2, 28], 1], 1.0], ['regression february 29 interval ends 2', [[2023, 2, 28], [2024, 2, 29], 1], 1.0], ['partial repair probe 1', [[1980, 2, 29], [1981, 2, 28], 1], 1.0], ['partial repair probe 2', [[2028, 2, 29], [2030, 2, 28], 1], 2.0], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['normal control 1', [[1896, 1, 30], [1896, 4, 30], 4], 0.24863388], ['normal control 2', [[2015, 3, 17], [2016, 3, 17], 1], 1.0]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression february 29 interval ends 11.01.0Passed
regression february 29 interval ends 20.997267761.0Failed
partial repair probe 11.9945355192.0Failed
partial repair probe 21.3224043721.326027397Failed
boundary control 10.2520547950.252054795Passed
boundary control 21.01.0Passed
normal control 11.3524590161.352459016Passed
normal control 22.9945355192.994535519Passed

SHA-256 / e1165ed204b96415d72ae2824ac08cd20422c3f11b7a5197ac9fe530d6f21b0d

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import datetime
from fractions import Fraction
N = 1
observations = []
def solve(a, b, freq):
    A = datetime.date(*a)
    B = datetime.date(*b)
    def leap(y):
        return (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
    days = (B - A).days
    if freq == 1:
        has29 = any(leap(y) and A < datetime.date(y, 2, 29) <= B for y in range(A.year, B.year + 1))
        den = 366 if has29 else 365
    else:
        den = 366 if leap(B.year) else 365
    return round(days / den, 9)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression february 29 interval ends 1', [[2023, 2, 28], [2024, 2, 29], 1], 1.0], ['regression february 29 interval ends 2', [[2028, 2, 29], [2029, 2, 28], 1], 1.0], ['partial repair probe 1', [[2104, 2, 29], [2106, 2, 28], 1], 2.0], ['partial repair probe 2', [[1996, 2, 29], [1997, 6, 27], 1], 1.326027397], ['boundary control 1', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['boundary control 2', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['normal control 1', [[2071, 8, 3], [2072, 12, 10], 1], 1.352459016], ['normal control 2', [[2027, 12, 29], [2030, 12, 29], 1], 2.994535519]], [['regression february 29 interval ends 1', [[2096, 2, 29], [2099, 2, 28], 1], 3.0], ['regression february 29 interval ends 2', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 1', [[2000, 2, 29], [2001, 6, 25], 1], 1.320547945], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['normal control 1', [[2028, 12, 15], [2029, 3, 15], 4], 0.246575342], ['normal control 2', [[2077, 12, 6], [2078, 9, 23], 1], 0.797260274], ['normal control 3', [[2025, 4, 30], [2026, 4, 30], 1], 1.0]], [['regression february 29 interval ends 1', [[2096, 2, 29], [2097, 2, 28], 1], 1.0], ['regression february 29 interval ends 2', [[2096, 2, 29], [2096, 6, 30], 1], 0.334246575], ['partial repair probe 1', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 2', [[2104, 2, 29], [2105, 2, 28], 1], 1.0], ['boundary control 1', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['boundary control 2', [[2024, 11, 15], [2025, 2, 15], 4], 0.252054795], ['normal control 1', [[1935, 3, 4], [1936, 3, 12], 4], 1.021857923], ['normal control 2', [[2024, 3, 29], [2024, 9, 29], 4], 0.50273224]], [['regression february 29 interval ends 1', [[2024, 2, 29], [2027, 2, 28], 1], 3.0], ['regression february 29 interval ends 2', [[2024, 2, 29], [2025, 2, 28], 1], 1.0], ['partial repair probe 1', [[1996, 2, 29], [1996, 4, 30], 1], 0.167123288], ['partial repair probe 2', [[2024, 2, 29], [2025, 4, 23], 1], 1.147945205], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['normal control 1', [[2103, 12, 29], [2104, 3, 29], 4], 0.24863388], ['normal control 2', [[2076, 11, 19], [2077, 11, 19], 1], 1.0]], [['regression february 29 interval ends 1', [[2048, 2, 29], [2049, 2, 28], 1], 1.0], ['regression february 29 interval ends 2', [[2023, 2, 28], [2024, 2, 29], 1], 1.0], ['partial repair probe 1', [[1980, 2, 29], [1981, 2, 28], 1], 1.0], ['partial repair probe 2', [[2028, 2, 29], [2030, 2, 28], 1], 2.0], ['boundary control 1', [[2023, 3, 1], [2024, 3, 1], 1], 1.0], ['boundary control 2', [[2023, 12, 15], [2024, 3, 15], 4], 0.24863388], ['normal control 1', [[1896, 1, 30], [1896, 4, 30], 4], 0.24863388], ['normal control 2', [[2015, 3, 17], [2016, 3, 17], 1], 1.0]]]
for label, args, expected in fixtures[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression february 29 interval ends 11.01.0Passed
regression february 29 interval ends 21.01.0Passed
partial repair probe 12.02.0Passed
partial repair probe 21.3260273971.326027397Passed
boundary control 10.2520547950.252054795Passed
boundary control 21.01.0Passed
normal control 11.3524590161.352459016Passed
normal control 22.9945355192.994535519Passed

SHA-256 / bdae5fd37ea6c661b981e68b3bbc0f317e3aaa4d6c8afe5bb7b443322ca44645

Verification & scope

A deterministic toy contract stated explicitly in the contract field; no claim of conformance to any published convention text. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:46:50.292701+00:00.

Case digest / de254554e39c018b705e59a20ed67cad1850e8a3072bd0b5715bf21d8ef72fc2