FA-77066 / Calendar recurrence rules / Open access
Occurrence test and next-occurrence lookup for an anchored N-weekly series: occurrence limit · case 01
The date one period after the final occurrence is still reported as an occurrence.
ROOT CAUSE
The COUNT bound on the occurrence index is inclusive.
VERIFIED REPAIR
At the occurrence limit decision use `diff // period < count`, leaving the other decision sites of the model unchanged.
Unsuccessful approach: The partial repair `diff < count * 7` bounds the day difference as if the series were weekly.
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])],
[('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])],
[('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])],
[('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]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, 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]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, 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 | [True, None, None] | [False, None, None] | Failed |
| 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 |
SHA-256 / ec8ea8fb45433e37ad1c88122faf7697d58471adab4ba726902d55c74e83bacc
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 < count * 7
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])],
[('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])],
[('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])],
[('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]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, 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]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, 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 | [False, None, None] | [True, None, None] | Failed |
| far before anchor | [False, '2024-01-05', 0] | [False, '2024-01-05', 0] | Passed |
SHA-256 / 048839ac33ceee79b2547e3f4312fa59bae21fc0e9750cc18aa3040160cccd79
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])],
[('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])],
[('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])],
[('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]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, 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]),
('one period after last', ['2024-01-05', 2, 3, '2024-02-16'], [False, 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 |
SHA-256 / 12f961f6053532745f492a5b10e8e63e04de7c6a6059d4ec2e85f22f0ee203f0
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:21.977654+00:00.
Case digest / 0cccd0f10168070cf574af76a084fad841116c60b98c91701206aa3015f638ca