FAILURE MAP
← Case archive

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

30/360 US bond basis: the 28th of a leap February counts as month end · case 01

Accruals starting on 28 February of a leap year are extended as if the month had ended.

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

ROOT CAUSE

The February-end test compares the day with 28 without consulting the leap-year rule.

VERIFIED REPAIR

Compare against 29 in leap years and 28 otherwise.

Unsuccessful approach: Accepting any day of at least 28 still treats 28 February in a leap year as month end.

Case contract

Inputs are two [year, month, day] dates. Apply, in order: if both dates are the last day of February, D2 becomes 30; if D1 is the last day of February, D1 becomes 30; if D2 is 31 and (adjusted) D1 is 30 or 31, D2 becomes 30; if D1 is 31, D1 becomes 30. Return 360*(Y2-Y1)+30*(M2-M1)+(D2-D1) as an integer day count.

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

N = 1
observations = []
def solve(a, b):
    y1, m1, d1 = a
    y2, m2, d2 = b
    def feb_end(y, m, d):
        leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
        return m == 2 and d == 28
    e1 = feb_end(y1, m1, d1)
    e2 = feb_end(y2, m2, d2)
    if e1 and e2:
        d2 = 30
    if e1:
        d1 = 30
    if d2 == 31 and d1 >= 30:
        d2 = 30
    if d1 == 31:
        d1 = 30
    return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression february end detection 1', [[2096, 2, 28], [2097, 1, 29]], 331], ['regression february end detection 2', [[2015, 2, 28], [2016, 2, 29]], 360], ['partial repair probe 1', [[2024, 2, 28], [2025, 1, 5]], 307], ['partial repair probe 2', [[2004, 2, 28], [2005, 3, 31]], 393], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 1, 1], [2024, 1, 1]], 0], ['normal control 1', [[2018, 4, 30], [2021, 5, 31]], 1110], ['normal control 2', [[2049, 1, 31], [2052, 2, 29]], 1109]], [['regression february end detection 1', [[2000, 2, 29], [2002, 2, 28]], 720], ['regression february end detection 2', [[2024, 2, 29], [2027, 2, 28]], 1080], ['partial repair probe 1', [[2004, 2, 28], [2005, 12, 17]], 649], ['partial repair probe 2', [[2008, 2, 28], [2011, 4, 25]], 1137], ['boundary control 1', [[2023, 2, 28], [2023, 8, 31]], 180], ['boundary control 2', [[2024, 1, 1], [2024, 1, 1]], 0], ['normal control 1', [[2047, 7, 31], [2050, 2, 28]], 928], ['normal control 2', [[2057, 2, 28], [2059, 5, 6]], 786]], [['regression february end detection 1', [[2000, 2, 29], [2000, 5, 30]], 90], ['regression february end detection 2', [[2000, 2, 28], [2003, 4, 30]], 1142], ['partial repair probe 1', [[2096, 2, 28], [2098, 1, 31]], 693], ['partial repair probe 2', [[2024, 2, 28], [2024, 11, 13]], 255], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2023, 5, 30], [2023, 7, 31]], 60], ['normal control 1', [[2089, 6, 30], [2091, 2, 28]], 598], ['normal control 2', [[2011, 6, 30], [2013, 7, 14]], 734]], [['regression february end detection 1', [[2096, 2, 28], [2098, 6, 1]], 813], ['regression february end detection 2', [[2016, 2, 28], [2016, 11, 30]], 272], ['partial repair probe 1', [[2000, 2, 28], [2001, 5, 31]], 453], ['partial repair probe 2', [[2024, 2, 28], [2027, 5, 1]], 1143], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2062, 11, 30], [2064, 3, 26]], 476], ['normal control 2', [[2060, 11, 30], [2062, 12, 30]], 750]], [['regression february end detection 1', [[2096, 2, 28], [2096, 10, 31]], 243], ['regression february end detection 2', [[2004, 2, 29], [2006, 5, 30]], 810], ['partial repair probe 1', [[2096, 2, 28], [2097, 1, 8]], 310], ['partial repair probe 2', [[2004, 2, 28], [2005, 5, 31]], 453], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2053, 1, 28], [2055, 2, 28]], 750], ['normal control 2', [[2011, 10, 20], [2012, 6, 19]], 239]]]
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 end detection 1329331Failed
regression february end detection 2359360Failed
partial repair probe 1305307Failed
partial repair probe 2390393Failed
boundary control 1180180Passed
boundary control 200Passed
normal control 111101110Passed
normal control 211091109Passed

SHA-256 / 1a9f43c38e4b30e180dafae4f984e31bff2db3bb4b9e708fd05a5da54112ca38

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(a, b):
    y1, m1, d1 = a
    y2, m2, d2 = b
    def feb_end(y, m, d):
        leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
        return m == 2 and d >= 28
    e1 = feb_end(y1, m1, d1)
    e2 = feb_end(y2, m2, d2)
    if e1 and e2:
        d2 = 30
    if e1:
        d1 = 30
    if d2 == 31 and d1 >= 30:
        d2 = 30
    if d1 == 31:
        d1 = 30
    return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression february end detection 1', [[2096, 2, 28], [2097, 1, 29]], 331], ['regression february end detection 2', [[2015, 2, 28], [2016, 2, 29]], 360], ['partial repair probe 1', [[2024, 2, 28], [2025, 1, 5]], 307], ['partial repair probe 2', [[2004, 2, 28], [2005, 3, 31]], 393], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 1, 1], [2024, 1, 1]], 0], ['normal control 1', [[2018, 4, 30], [2021, 5, 31]], 1110], ['normal control 2', [[2049, 1, 31], [2052, 2, 29]], 1109]], [['regression february end detection 1', [[2000, 2, 29], [2002, 2, 28]], 720], ['regression february end detection 2', [[2024, 2, 29], [2027, 2, 28]], 1080], ['partial repair probe 1', [[2004, 2, 28], [2005, 12, 17]], 649], ['partial repair probe 2', [[2008, 2, 28], [2011, 4, 25]], 1137], ['boundary control 1', [[2023, 2, 28], [2023, 8, 31]], 180], ['boundary control 2', [[2024, 1, 1], [2024, 1, 1]], 0], ['normal control 1', [[2047, 7, 31], [2050, 2, 28]], 928], ['normal control 2', [[2057, 2, 28], [2059, 5, 6]], 786]], [['regression february end detection 1', [[2000, 2, 29], [2000, 5, 30]], 90], ['regression february end detection 2', [[2000, 2, 28], [2003, 4, 30]], 1142], ['partial repair probe 1', [[2096, 2, 28], [2098, 1, 31]], 693], ['partial repair probe 2', [[2024, 2, 28], [2024, 11, 13]], 255], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2023, 5, 30], [2023, 7, 31]], 60], ['normal control 1', [[2089, 6, 30], [2091, 2, 28]], 598], ['normal control 2', [[2011, 6, 30], [2013, 7, 14]], 734]], [['regression february end detection 1', [[2096, 2, 28], [2098, 6, 1]], 813], ['regression february end detection 2', [[2016, 2, 28], [2016, 11, 30]], 272], ['partial repair probe 1', [[2000, 2, 28], [2001, 5, 31]], 453], ['partial repair probe 2', [[2024, 2, 28], [2027, 5, 1]], 1143], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2062, 11, 30], [2064, 3, 26]], 476], ['normal control 2', [[2060, 11, 30], [2062, 12, 30]], 750]], [['regression february end detection 1', [[2096, 2, 28], [2096, 10, 31]], 243], ['regression february end detection 2', [[2004, 2, 29], [2006, 5, 30]], 810], ['partial repair probe 1', [[2096, 2, 28], [2097, 1, 8]], 310], ['partial repair probe 2', [[2004, 2, 28], [2005, 5, 31]], 453], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2053, 1, 28], [2055, 2, 28]], 750], ['normal control 2', [[2011, 10, 20], [2012, 6, 19]], 239]]]
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 end detection 1329331Failed
regression february end detection 2360360Passed
partial repair probe 1305307Failed
partial repair probe 2390393Failed
boundary control 1180180Passed
boundary control 200Passed
normal control 111101110Passed
normal control 211091109Passed

SHA-256 / 722196da2d578439742402f21d45e4783c2e9441ade7bf589cf78398b58fd63f

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(a, b):
    y1, m1, d1 = a
    y2, m2, d2 = b
    def feb_end(y, m, d):
        leap = (y % 4 == 0 and y % 100 != 0) or y % 400 == 0
        return m == 2 and d == (29 if leap else 28)
    e1 = feb_end(y1, m1, d1)
    e2 = feb_end(y2, m2, d2)
    if e1 and e2:
        d2 = 30
    if e1:
        d1 = 30
    if d2 == 31 and d1 >= 30:
        d2 = 30
    if d1 == 31:
        d1 = 30
    return 360 * (y2 - y1) + 30 * (m2 - m1) + (d2 - d1)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression february end detection 1', [[2096, 2, 28], [2097, 1, 29]], 331], ['regression february end detection 2', [[2015, 2, 28], [2016, 2, 29]], 360], ['partial repair probe 1', [[2024, 2, 28], [2025, 1, 5]], 307], ['partial repair probe 2', [[2004, 2, 28], [2005, 3, 31]], 393], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 1, 1], [2024, 1, 1]], 0], ['normal control 1', [[2018, 4, 30], [2021, 5, 31]], 1110], ['normal control 2', [[2049, 1, 31], [2052, 2, 29]], 1109]], [['regression february end detection 1', [[2000, 2, 29], [2002, 2, 28]], 720], ['regression february end detection 2', [[2024, 2, 29], [2027, 2, 28]], 1080], ['partial repair probe 1', [[2004, 2, 28], [2005, 12, 17]], 649], ['partial repair probe 2', [[2008, 2, 28], [2011, 4, 25]], 1137], ['boundary control 1', [[2023, 2, 28], [2023, 8, 31]], 180], ['boundary control 2', [[2024, 1, 1], [2024, 1, 1]], 0], ['normal control 1', [[2047, 7, 31], [2050, 2, 28]], 928], ['normal control 2', [[2057, 2, 28], [2059, 5, 6]], 786]], [['regression february end detection 1', [[2000, 2, 29], [2000, 5, 30]], 90], ['regression february end detection 2', [[2000, 2, 28], [2003, 4, 30]], 1142], ['partial repair probe 1', [[2096, 2, 28], [2098, 1, 31]], 693], ['partial repair probe 2', [[2024, 2, 28], [2024, 11, 13]], 255], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2023, 5, 30], [2023, 7, 31]], 60], ['normal control 1', [[2089, 6, 30], [2091, 2, 28]], 598], ['normal control 2', [[2011, 6, 30], [2013, 7, 14]], 734]], [['regression february end detection 1', [[2096, 2, 28], [2098, 6, 1]], 813], ['regression february end detection 2', [[2016, 2, 28], [2016, 11, 30]], 272], ['partial repair probe 1', [[2000, 2, 28], [2001, 5, 31]], 453], ['partial repair probe 2', [[2024, 2, 28], [2027, 5, 1]], 1143], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2062, 11, 30], [2064, 3, 26]], 476], ['normal control 2', [[2060, 11, 30], [2062, 12, 30]], 750]], [['regression february end detection 1', [[2096, 2, 28], [2096, 10, 31]], 243], ['regression february end detection 2', [[2004, 2, 29], [2006, 5, 30]], 810], ['partial repair probe 1', [[2096, 2, 28], [2097, 1, 8]], 310], ['partial repair probe 2', [[2004, 2, 28], [2005, 5, 31]], 453], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['normal control 1', [[2053, 1, 28], [2055, 2, 28]], 750], ['normal control 2', [[2011, 10, 20], [2012, 6, 19]], 239]]]
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 end detection 1331331Passed
regression february end detection 2360360Passed
partial repair probe 1307307Passed
partial repair probe 2393393Passed
boundary control 1180180Passed
boundary control 200Passed
normal control 111101110Passed
normal control 211091109Passed

SHA-256 / d7acf55eb05f73442fc13213c4d94e4b46c6ae51f038016aee491bbf53b8e66f

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:49.370133+00:00.

Case digest / bffce8ec106d2bd0b5eb9696a3a602ff4e9e745c5aa2b986a51e16750bff224b