FA-77081 / Calendar recurrence rules / Open access
Occurrence test and next-occurrence lookup for an anchored N-weekly series: exhausted series · case 01
After the final occurrence the lookup invents one more occurrence.
ROOT CAUSE
The series-exhausted test is off by one.
VERIFIED REPAIR
At the exhausted series decision use `idx >= count`, leaving the other decision sites of the model unchanged.
Unsuccessful approach: The partial repair `idx >= count - 1` declares the series exhausted one occurrence early.
Case contract
A series occurs every interval_weeks weeks starting at anchor, for count occurrences. For a query date return [is_occurrence, next occurrence strictly after the query or None, 0-based index of that next occurrence or None]. Dates before the anchor are never occurrences; the next occurrence after a query before the anchor is the anchor itself.
Why this case matters
Recurring calendar series are expanded into concrete instances for display, reminders and conflict checks.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import datetime as dt
import calendar
N = 1
observations = []
def solve(anchor, interval_weeks, count, query):
a = dt.date.fromisoformat(anchor)
q = dt.date.fromisoformat(query)
period = 7 * interval_weeks
diff = (q - a).days
is_occ = diff >= 0 and diff % period == 0 and diff // period < count
if diff < 0:
idx = 0
else:
idx = diff // period + 1
if idx > count:
return [is_occ, None, None]
return [is_occ, (a + dt.timedelta(days=idx * period)).isoformat(), idx]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: aligned date before anchor', ['2024-01-05', 2, 10, '2023-12-22'], [False, '2024-01-05', 0]),
('query on anchor', ['2024-01-05', 2, 10, '2024-01-05'], [True, '2024-01-19', 1]),
('query on later occurrence', ['2024-01-05', 2, 10, '2024-02-02'], [True, '2024-02-16', 3]),
('day before occurrence', ['2024-01-05', 2, 10, '2024-01-18'], [False, '2024-01-19', 1]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('query on later occurrence', ['2024-01-05', 2, 10, '2024-02-02'], [True, '2024-02-16', 3]),
('day before occurrence', ['2024-01-05', 2, 10, '2024-01-18'], [False, '2024-01-19', 1]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('three periods before anchor weekly', ['2024-03-04', 1, 5, '2024-02-12'], [False, '2024-03-04', 0]),
('beyond series end', ['2024-03-04', 1, 2, '2024-04-30'], [False, None, None]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None])],
[('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('three periods before anchor weekly', ['2024-03-04', 1, 5, '2024-02-12'], [False, '2024-03-04', 0]),
('beyond series end', ['2024-03-04', 1, 2, '2024-04-30'], [False, None, None]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3]),
('regression: aligned date before anchor', ['2024-01-05', 2, 10, '2023-12-22'], [False, '2024-01-05', 0]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None])]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: aligned date before anchor | [False, '2024-01-05', 0] | [False, '2024-01-05', 0] | Passed |
| query on anchor | [True, '2024-01-19', 1] | [True, '2024-01-19', 1] | Passed |
| query on later occurrence | [True, '2024-02-16', 3] | [True, '2024-02-16', 3] | Passed |
| day before occurrence | [False, '2024-01-19', 1] | [False, '2024-01-19', 1] | Passed |
| one period after last | [False, None, None] | [False, None, None] | Passed |
| query on last occurrence | [True, '2024-02-16', 3] | [True, None, None] | Failed |
| far before anchor | [False, '2024-01-05', 0] | [False, '2024-01-05', 0] | Passed |
| penultimate occurrence | [True, '2024-07-19', 3] | [True, '2024-07-19', 3] | Passed |
SHA-256 / 01e57c21512d72bf9c53ad89c61584317e3387e226d97a46c699ef7eac44513a
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import datetime as dt
import calendar
N = 1
observations = []
def solve(anchor, interval_weeks, count, query):
a = dt.date.fromisoformat(anchor)
q = dt.date.fromisoformat(query)
period = 7 * interval_weeks
diff = (q - a).days
is_occ = diff >= 0 and diff % period == 0 and diff // period < count
if diff < 0:
idx = 0
else:
idx = diff // period + 1
if idx >= count - 1:
return [is_occ, None, None]
return [is_occ, (a + dt.timedelta(days=idx * period)).isoformat(), idx]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: aligned date before anchor', ['2024-01-05', 2, 10, '2023-12-22'], [False, '2024-01-05', 0]),
('query on anchor', ['2024-01-05', 2, 10, '2024-01-05'], [True, '2024-01-19', 1]),
('query on later occurrence', ['2024-01-05', 2, 10, '2024-02-02'], [True, '2024-02-16', 3]),
('day before occurrence', ['2024-01-05', 2, 10, '2024-01-18'], [False, '2024-01-19', 1]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('query on later occurrence', ['2024-01-05', 2, 10, '2024-02-02'], [True, '2024-02-16', 3]),
('day before occurrence', ['2024-01-05', 2, 10, '2024-01-18'], [False, '2024-01-19', 1]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('three periods before anchor weekly', ['2024-03-04', 1, 5, '2024-02-12'], [False, '2024-03-04', 0]),
('beyond series end', ['2024-03-04', 1, 2, '2024-04-30'], [False, None, None]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None])],
[('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('three periods before anchor weekly', ['2024-03-04', 1, 5, '2024-02-12'], [False, '2024-03-04', 0]),
('beyond series end', ['2024-03-04', 1, 2, '2024-04-30'], [False, None, None]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3]),
('regression: aligned date before anchor', ['2024-01-05', 2, 10, '2023-12-22'], [False, '2024-01-05', 0]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None])]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: aligned date before anchor | [False, '2024-01-05', 0] | [False, '2024-01-05', 0] | Passed |
| query on anchor | [True, '2024-01-19', 1] | [True, '2024-01-19', 1] | Passed |
| query on later occurrence | [True, '2024-02-16', 3] | [True, '2024-02-16', 3] | Passed |
| day before occurrence | [False, '2024-01-19', 1] | [False, '2024-01-19', 1] | Passed |
| one period after last | [False, None, None] | [False, None, None] | Passed |
| query on last occurrence | [True, None, None] | [True, None, None] | Passed |
| far before anchor | [False, '2024-01-05', 0] | [False, '2024-01-05', 0] | Passed |
| penultimate occurrence | [True, None, None] | [True, '2024-07-19', 3] | Failed |
SHA-256 / 71f82d897f606dea2398b58d2e4057fa3166cd55a5cfe80f4037eaefa3ac444b
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import datetime as dt
import calendar
N = 1
observations = []
def solve(anchor, interval_weeks, count, query):
a = dt.date.fromisoformat(anchor)
q = dt.date.fromisoformat(query)
period = 7 * interval_weeks
diff = (q - a).days
is_occ = diff >= 0 and diff % period == 0 and diff // period < count
if diff < 0:
idx = 0
else:
idx = diff // period + 1
if idx >= count:
return [is_occ, None, None]
return [is_occ, (a + dt.timedelta(days=idx * period)).isoformat(), idx]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: aligned date before anchor', ['2024-01-05', 2, 10, '2023-12-22'], [False, '2024-01-05', 0]),
('query on anchor', ['2024-01-05', 2, 10, '2024-01-05'], [True, '2024-01-19', 1]),
('query on later occurrence', ['2024-01-05', 2, 10, '2024-02-02'], [True, '2024-02-16', 3]),
('day before occurrence', ['2024-01-05', 2, 10, '2024-01-18'], [False, '2024-01-19', 1]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('query on later occurrence', ['2024-01-05', 2, 10, '2024-02-02'], [True, '2024-02-16', 3]),
('day before occurrence', ['2024-01-05', 2, 10, '2024-01-18'], [False, '2024-01-19', 1]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, None, None]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None]),
('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3])],
[('far before anchor', ['2024-01-05', 3, 4, '2023-11-01'], [False, '2024-01-05', 0]),
('weekly mid week', ['2024-03-04', 1, 5, '2024-03-13'], [False, '2024-03-18', 2]),
('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('three periods before anchor weekly', ['2024-03-04', 1, 5, '2024-02-12'], [False, '2024-03-04', 0]),
('beyond series end', ['2024-03-04', 1, 2, '2024-04-30'], [False, None, None]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None])],
[('four-weekly aligned', ['2024-03-04', 4, 6, '2024-04-29'], [True, '2024-05-27', 3]),
('aligned before anchor weekly', ['2024-03-04', 1, 5, '2024-02-26'], [False, '2024-03-04', 0]),
('two periods before anchor', ['2024-01-05', 2, 10, '2023-12-08'], [False, '2024-01-05', 0]),
('three periods before anchor weekly', ['2024-03-04', 1, 5, '2024-02-12'], [False, '2024-03-04', 0]),
('beyond series end', ['2024-03-04', 1, 2, '2024-04-30'], [False, None, None]),
('penultimate occurrence', ['2024-06-07', 2, 4, '2024-07-05'], [True, '2024-07-19', 3]),
('regression: aligned date before anchor', ['2024-01-05', 2, 10, '2023-12-22'], [False, '2024-01-05', 0]),
('query on last occurrence', ['2024-01-05', 2, 3, '2024-02-02'], [True, None, None])]]
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: aligned date before anchor | [False, '2024-01-05', 0] | [False, '2024-01-05', 0] | Passed |
| query on anchor | [True, '2024-01-19', 1] | [True, '2024-01-19', 1] | Passed |
| query on later occurrence | [True, '2024-02-16', 3] | [True, '2024-02-16', 3] | Passed |
| day before occurrence | [False, '2024-01-19', 1] | [False, '2024-01-19', 1] | Passed |
| one period after last | [False, None, None] | [False, None, None] | Passed |
| query on last occurrence | [True, None, None] | [True, None, None] | Passed |
| far before anchor | [False, '2024-01-05', 0] | [False, '2024-01-05', 0] | Passed |
| penultimate occurrence | [True, '2024-07-19', 3] | [True, '2024-07-19', 3] | Passed |
SHA-256 / 1372313a02f93a8a372fa0a45fbd12a2373c9a54fd134260e0d7535168e8e982
Verification & scope
Stipulated, bounded recurrence-expansion model evaluated offline on explicit fixtures; it is not a complete iCalendar implementation and makes no claim of standards conformance. 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:49:22.290974+00:00.
Case digest / 1616c4101daa5cec6036aadb155ff9f16437a42405700379ed034490c28b2913