FAILURE MAP
← Case archive

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

30/360 US bond basis: the D2 rule is evaluated before D1 is promoted from February end · case 01

A period from the end of February to a 31st keeps D2=31 and overstates the count by one day.

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

ROOT CAUSE

The D2=31 test runs before the February-end promotion of D1, so it sees the raw 28 or 29.

VERIFIED REPAIR

Promote a February-end D1 to 30 before testing D2=31 against D1.

Unsuccessful approach: Lowering the D2 threshold to 28 instead also clamps D2 for ordinary starts on the 28th or 29th.

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 == (29 if leap else 28)
    e1 = feb_end(y1, m1, d1)
    e2 = feb_end(y2, m2, d2)
    if e1 and e2:
        d2 = 30
    if d2 == 31 and d1 >= 30:
        d2 = 30
    if e1:
        d1 = 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 adjustment ordering 1', [[2004, 2, 29], [2007, 7, 31]], 1230], ['regression adjustment ordering 2', [[2075, 2, 28], [2075, 5, 31]], 90], ['partial repair probe 1', [[2000, 12, 29], [2002, 10, 31]], 662], ['partial repair probe 2', [[2074, 10, 28], [2076, 8, 31]], 663], ['boundary control 1', [[2024, 2, 29], [2025, 2, 28]], 360], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2024, 2, 29], [2026, 2, 28]], 720], ['normal control 2', [[2049, 6, 15], [2051, 7, 31]], 766]], [['regression adjustment ordering 1', [[2001, 2, 28], [2001, 3, 31]], 30], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2041, 1, 28], [2041, 3, 31]], 63], ['partial repair probe 2', [[2096, 2, 28], [2099, 10, 31]], 1323], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2059, 6, 30], [2060, 8, 31]], 420], ['normal control 2', [[2003, 8, 14], [2004, 12, 7]], 473]], [['regression adjustment ordering 1', [[2096, 2, 29], [2098, 5, 31]], 810], ['regression adjustment ordering 2', [[2000, 2, 29], [2000, 3, 31]], 30], ['partial repair probe 1', [[2002, 9, 28], [2005, 5, 31]], 963], ['partial repair probe 2', [[2096, 2, 28], [2099, 1, 31]], 1053], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2043, 11, 30], [2046, 2, 28]], 808], ['normal control 2', [[2024, 2, 28], [2026, 2, 28]], 720]], [['regression adjustment ordering 1', [[2096, 2, 29], [2097, 7, 31]], 510], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2073, 12, 29], [2074, 8, 31]], 242], ['partial repair probe 2', [[2000, 2, 28], [2002, 5, 31]], 813], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[1998, 6, 14], [2000, 2, 29]], 615], ['normal control 2', [[2068, 2, 9], [2070, 10, 13]], 964]], [['regression adjustment ordering 1', [[2096, 2, 29], [2099, 3, 31]], 1110], ['regression adjustment ordering 2', [[2096, 2, 29], [2096, 1, 31]], -30], ['partial repair probe 1', [[2024, 2, 28], [2025, 3, 31]], 393], ['partial repair probe 2', [[2046, 8, 28], [2048, 7, 31]], 693], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2052, 12, 30], [2053, 8, 16]], 226], ['normal control 2', [[2049, 9, 25], [2052, 8, 30]], 1055]]]
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 adjustment ordering 112311230Failed
regression adjustment ordering 29190Failed
partial repair probe 1662662Passed
partial repair probe 2663663Passed
boundary control 1360360Passed
boundary control 2180180Passed
normal control 1720720Passed
normal control 2766766Passed

SHA-256 / 7386d7f4469c5f004267f9f22174127119385ac56fcada44ab4fe0a581ada1b6

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 == (29 if leap else 28)
    e1 = feb_end(y1, m1, d1)
    e2 = feb_end(y2, m2, d2)
    if e1 and e2:
        d2 = 30
    if d2 == 31 and d1 >= 28:
        d2 = 30
    if e1:
        d1 = 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 adjustment ordering 1', [[2004, 2, 29], [2007, 7, 31]], 1230], ['regression adjustment ordering 2', [[2075, 2, 28], [2075, 5, 31]], 90], ['partial repair probe 1', [[2000, 12, 29], [2002, 10, 31]], 662], ['partial repair probe 2', [[2074, 10, 28], [2076, 8, 31]], 663], ['boundary control 1', [[2024, 2, 29], [2025, 2, 28]], 360], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2024, 2, 29], [2026, 2, 28]], 720], ['normal control 2', [[2049, 6, 15], [2051, 7, 31]], 766]], [['regression adjustment ordering 1', [[2001, 2, 28], [2001, 3, 31]], 30], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2041, 1, 28], [2041, 3, 31]], 63], ['partial repair probe 2', [[2096, 2, 28], [2099, 10, 31]], 1323], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2059, 6, 30], [2060, 8, 31]], 420], ['normal control 2', [[2003, 8, 14], [2004, 12, 7]], 473]], [['regression adjustment ordering 1', [[2096, 2, 29], [2098, 5, 31]], 810], ['regression adjustment ordering 2', [[2000, 2, 29], [2000, 3, 31]], 30], ['partial repair probe 1', [[2002, 9, 28], [2005, 5, 31]], 963], ['partial repair probe 2', [[2096, 2, 28], [2099, 1, 31]], 1053], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2043, 11, 30], [2046, 2, 28]], 808], ['normal control 2', [[2024, 2, 28], [2026, 2, 28]], 720]], [['regression adjustment ordering 1', [[2096, 2, 29], [2097, 7, 31]], 510], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2073, 12, 29], [2074, 8, 31]], 242], ['partial repair probe 2', [[2000, 2, 28], [2002, 5, 31]], 813], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[1998, 6, 14], [2000, 2, 29]], 615], ['normal control 2', [[2068, 2, 9], [2070, 10, 13]], 964]], [['regression adjustment ordering 1', [[2096, 2, 29], [2099, 3, 31]], 1110], ['regression adjustment ordering 2', [[2096, 2, 29], [2096, 1, 31]], -30], ['partial repair probe 1', [[2024, 2, 28], [2025, 3, 31]], 393], ['partial repair probe 2', [[2046, 8, 28], [2048, 7, 31]], 693], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2052, 12, 30], [2053, 8, 16]], 226], ['normal control 2', [[2049, 9, 25], [2052, 8, 30]], 1055]]]
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 adjustment ordering 112301230Passed
regression adjustment ordering 29090Passed
partial repair probe 1661662Failed
partial repair probe 2662663Failed
boundary control 1360360Passed
boundary control 2180180Passed
normal control 1720720Passed
normal control 2766766Passed

SHA-256 / 0c72421f46d4fa110547406b8ff253f139173d1a3e220ac0dfba27ed4e7d4caf

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 adjustment ordering 1', [[2004, 2, 29], [2007, 7, 31]], 1230], ['regression adjustment ordering 2', [[2075, 2, 28], [2075, 5, 31]], 90], ['partial repair probe 1', [[2000, 12, 29], [2002, 10, 31]], 662], ['partial repair probe 2', [[2074, 10, 28], [2076, 8, 31]], 663], ['boundary control 1', [[2024, 2, 29], [2025, 2, 28]], 360], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2024, 2, 29], [2026, 2, 28]], 720], ['normal control 2', [[2049, 6, 15], [2051, 7, 31]], 766]], [['regression adjustment ordering 1', [[2001, 2, 28], [2001, 3, 31]], 30], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2041, 1, 28], [2041, 3, 31]], 63], ['partial repair probe 2', [[2096, 2, 28], [2099, 10, 31]], 1323], ['boundary control 1', [[2023, 1, 31], [2023, 3, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2059, 6, 30], [2060, 8, 31]], 420], ['normal control 2', [[2003, 8, 14], [2004, 12, 7]], 473]], [['regression adjustment ordering 1', [[2096, 2, 29], [2098, 5, 31]], 810], ['regression adjustment ordering 2', [[2000, 2, 29], [2000, 3, 31]], 30], ['partial repair probe 1', [[2002, 9, 28], [2005, 5, 31]], 963], ['partial repair probe 2', [[2096, 2, 28], [2099, 1, 31]], 1053], ['boundary control 1', [[2023, 5, 30], [2023, 7, 31]], 60], ['boundary control 2', [[2024, 3, 15], [2024, 9, 15]], 180], ['normal control 1', [[2043, 11, 30], [2046, 2, 28]], 808], ['normal control 2', [[2024, 2, 28], [2026, 2, 28]], 720]], [['regression adjustment ordering 1', [[2096, 2, 29], [2097, 7, 31]], 510], ['regression adjustment ordering 2', [[2023, 2, 28], [2023, 8, 31]], 180], ['partial repair probe 1', [[2073, 12, 29], [2074, 8, 31]], 242], ['partial repair probe 2', [[2000, 2, 28], [2002, 5, 31]], 813], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[1998, 6, 14], [2000, 2, 29]], 615], ['normal control 2', [[2068, 2, 9], [2070, 10, 13]], 964]], [['regression adjustment ordering 1', [[2096, 2, 29], [2099, 3, 31]], 1110], ['regression adjustment ordering 2', [[2096, 2, 29], [2096, 1, 31]], -30], ['partial repair probe 1', [[2024, 2, 28], [2025, 3, 31]], 393], ['partial repair probe 2', [[2046, 8, 28], [2048, 7, 31]], 693], ['boundary control 1', [[2024, 3, 15], [2024, 9, 15]], 180], ['boundary control 2', [[2024, 2, 29], [2025, 2, 28]], 360], ['normal control 1', [[2052, 12, 30], [2053, 8, 16]], 226], ['normal control 2', [[2049, 9, 25], [2052, 8, 30]], 1055]]]
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 adjustment ordering 112301230Passed
regression adjustment ordering 29090Passed
partial repair probe 1662662Passed
partial repair probe 2663663Passed
boundary control 1360360Passed
boundary control 2180180Passed
normal control 1720720Passed
normal control 2766766Passed

SHA-256 / 6604ec644a255c6bdf5d107d6d89a7d601a1c7d577e8f7c41447ae2d75eb1c05

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

Case digest / 47448b1911c77433d945fe79751a026fd7c8cf02540db8f9c7a499ce38cbf3fe