FAILURE MAP
← Case archive

FA-76851 / Calendar recurrence rules / Open access

Weekly BYDAY expansion honouring the week-start day: in week order · case 01

With WKST=SU a Sunday instance is emitted after later weekdays, so COUNT keeps the wrong instances.

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

ROOT CAUSE

Weekdays inside one week are emitted in Monday-first numeric order rather than in order from WKST.

THE FAILURE

Weekdays inside one week are emitted in Monday-first numeric order rather than in order from WKST.

Unsuccessful approach: The partial repair `sorted(days, key=lambda w: (w - start.weekday()) % 7)` orders weekdays relative to the dtstart weekday, which still misorders weeks that do not begin at dtstart.

Case contract

FREQ=WEEKLY expansion. byday lists weekday numbers (0=Monday..6=Sunday); an empty list means the weekday of dtstart. Weeks begin on wkst; the week containing dtstart is week 0 and only weeks whose index is a multiple of interval are active. Instances are chronological, never before dtstart, and dtstart itself is an instance only if its weekday is listed. Return the first count ISO dates.

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(dtstart, interval, byday, wkst, count):
    start = dt.date.fromisoformat(dtstart)
    days = sorted(set(byday)) if byday else [start.weekday()]
    week0 = start - dt.timedelta(days=(start.weekday() - wkst) % 7)
    out = []
    for k in range(600):
        week = week0 + dt.timedelta(weeks=k * interval)
        for wd in days:
            d = week + dt.timedelta(days=(wd - wkst) % 7)
            if d < start:
                continue
            if len(out) >= count:
                return out
            out.append(d.isoformat())
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: WKST=SU interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 6, 4],
   ['1997-08-05', '1997-08-17', '1997-08-19', '1997-08-31']),
  ('regression: WKST=MO interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 0, 4],
   ['1997-08-05', '1997-08-10', '1997-08-19', '1997-08-24']),
  ('non-matching dtstart Wednesday',
   ['2024-01-03', 1, [0, 4], 0, 3],
   ['2024-01-05', '2024-01-08', '2024-01-12']),
  ('dtstart mid-week weekly MO,WE,FR',
   ['2024-01-04', 1, [0, 2, 4], 0, 5],
   ['2024-01-05', '2024-01-08', '2024-01-10', '2024-01-12', '2024-01-15']),
  ('empty BYDAY uses dtstart weekday',
   ['2024-02-29', 3, [], 0, 3],
   ['2024-02-29', '2024-03-21', '2024-04-11']),
  ('count zero', ['2024-01-01', 1, [0], 0, 0], []),
  ('WKST=SU weekly SA,SU',
   ['2024-03-09', 1, [5, 6], 6, 4],
   ['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17'])],
 [('non-matching dtstart Wednesday',
   ['2024-01-03', 1, [0, 4], 0, 3],
   ['2024-01-05', '2024-01-08', '2024-01-12']),
  ('dtstart mid-week weekly MO,WE,FR',
   ['2024-01-04', 1, [0, 2, 4], 0, 5],
   ['2024-01-05', '2024-01-08', '2024-01-10', '2024-01-12', '2024-01-15']),
  ('empty BYDAY uses dtstart weekday',
   ['2024-02-29', 3, [], 0, 3],
   ['2024-02-29', '2024-03-21', '2024-04-11']),
  ('count zero', ['2024-01-01', 1, [0], 0, 0], []),
  ('WKST=SU weekly SA,SU',
   ['2024-03-09', 1, [5, 6], 6, 4],
   ['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17']),
  ('interval 3 friday from monday', ['2024-05-06', 3, [4], 0, 3], ['2024-05-10', '2024-05-31', '2024-06-21']),
  ('WKST=WE interval 2 MO,TH',
   ['2024-06-04', 2, [0, 3], 2, 5],
   ['2024-06-13', '2024-06-17', '2024-06-27', '2024-07-01', '2024-07-11'])],
 [('empty BYDAY uses dtstart weekday',
   ['2024-02-29', 3, [], 0, 3],
   ['2024-02-29', '2024-03-21', '2024-04-11']),
  ('count zero', ['2024-01-01', 1, [0], 0, 0], []),
  ('WKST=SU weekly SA,SU',
   ['2024-03-09', 1, [5, 6], 6, 4],
   ['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17']),
  ('interval 3 friday from monday', ['2024-05-06', 3, [4], 0, 3], ['2024-05-10', '2024-05-31', '2024-06-21']),
  ('WKST=WE interval 2 MO,TH',
   ['2024-06-04', 2, [0, 3], 2, 5],
   ['2024-06-13', '2024-06-17', '2024-06-27', '2024-07-01', '2024-07-11']),
  ('single sunday series', ['2024-12-29', 2, [6], 0, 3], ['2024-12-29', '2025-01-12', '2025-01-26']),
  ('dtstart on listed day',
   ['2025-01-06', 2, [0, 3], 0, 4],
   ['2025-01-06', '2025-01-09', '2025-01-20', '2025-01-23'])],
 [('WKST=SU weekly SA,SU',
   ['2024-03-09', 1, [5, 6], 6, 4],
   ['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17']),
  ('interval 3 friday from monday', ['2024-05-06', 3, [4], 0, 3], ['2024-05-10', '2024-05-31', '2024-06-21']),
  ('WKST=WE interval 2 MO,TH',
   ['2024-06-04', 2, [0, 3], 2, 5],
   ['2024-06-13', '2024-06-17', '2024-06-27', '2024-07-01', '2024-07-11']),
  ('single sunday series', ['2024-12-29', 2, [6], 0, 3], ['2024-12-29', '2025-01-12', '2025-01-26']),
  ('dtstart on listed day',
   ['2025-01-06', 2, [0, 3], 0, 4],
   ['2025-01-06', '2025-01-09', '2025-01-20', '2025-01-23']),
  ('WKST=SU interval 2 from saturday',
   ['2025-03-01', 2, [6, 5], 6, 5],
   ['2025-03-01', '2025-03-09', '2025-03-15', '2025-03-23', '2025-03-29']),
  ('regression: WKST=SU interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 6, 4],
   ['1997-08-05', '1997-08-17', '1997-08-19', '1997-08-31'])],
 [('WKST=WE interval 2 MO,TH',
   ['2024-06-04', 2, [0, 3], 2, 5],
   ['2024-06-13', '2024-06-17', '2024-06-27', '2024-07-01', '2024-07-11']),
  ('single sunday series', ['2024-12-29', 2, [6], 0, 3], ['2024-12-29', '2025-01-12', '2025-01-26']),
  ('dtstart on listed day',
   ['2025-01-06', 2, [0, 3], 0, 4],
   ['2025-01-06', '2025-01-09', '2025-01-20', '2025-01-23']),
  ('WKST=SU interval 2 from saturday',
   ['2025-03-01', 2, [6, 5], 6, 5],
   ['2025-03-01', '2025-03-09', '2025-03-15', '2025-03-23', '2025-03-29']),
  ('regression: WKST=SU interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 6, 4],
   ['1997-08-05', '1997-08-17', '1997-08-19', '1997-08-31']),
  ('regression: WKST=MO interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 0, 4],
   ['1997-08-05', '1997-08-10', '1997-08-19', '1997-08-24']),
  ('non-matching dtstart Wednesday',
   ['2024-01-03', 1, [0, 4], 0, 3],
   ['2024-01-05', '2024-01-08', '2024-01-12'])]]
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: WKST=SU interval 2 TU,SU['1997-08-05', '1997-08-19', '1997-08-17', '1997-09-02']['1997-08-05', '1997-08-17', '1997-08-19', '1997-08-31']Failed
regression: WKST=MO interval 2 TU,SU['1997-08-05', '1997-08-10', '1997-08-19', '1997-08-24']['1997-08-05', '1997-08-10', '1997-08-19', '1997-08-24']Passed
non-matching dtstart Wednesday['2024-01-05', '2024-01-08', '2024-01-12']['2024-01-05', '2024-01-08', '2024-01-12']Passed
dtstart mid-week weekly MO,WE,FR['2024-01-05', '2024-01-08', '2024-01-10', '2024-01-12', '2024-01-15']['2024-01-05', '2024-01-08', '2024-01-10', '2024-01-12', '2024-01-15']Passed
empty BYDAY uses dtstart weekday['2024-02-29', '2024-03-21', '2024-04-11']['2024-02-29', '2024-03-21', '2024-04-11']Passed
count zero[][]Passed
WKST=SU weekly SA,SU['2024-03-09', '2024-03-16', '2024-03-10', '2024-03-23']['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17']Failed

SHA-256 / 13b631c6595425088c6d7d58ced9d9da13cde866cd2fc57d78784c7a9a84b097

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(dtstart, interval, byday, wkst, count):
    start = dt.date.fromisoformat(dtstart)
    days = sorted(set(byday)) if byday else [start.weekday()]
    week0 = start - dt.timedelta(days=(start.weekday() - wkst) % 7)
    out = []
    for k in range(600):
        week = week0 + dt.timedelta(weeks=k * interval)
        for wd in sorted(days, key=lambda w: (w - start.weekday()) % 7):
            d = week + dt.timedelta(days=(wd - wkst) % 7)
            if d < start:
                continue
            if len(out) >= count:
                return out
            out.append(d.isoformat())
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: WKST=SU interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 6, 4],
   ['1997-08-05', '1997-08-17', '1997-08-19', '1997-08-31']),
  ('regression: WKST=MO interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 0, 4],
   ['1997-08-05', '1997-08-10', '1997-08-19', '1997-08-24']),
  ('non-matching dtstart Wednesday',
   ['2024-01-03', 1, [0, 4], 0, 3],
   ['2024-01-05', '2024-01-08', '2024-01-12']),
  ('dtstart mid-week weekly MO,WE,FR',
   ['2024-01-04', 1, [0, 2, 4], 0, 5],
   ['2024-01-05', '2024-01-08', '2024-01-10', '2024-01-12', '2024-01-15']),
  ('empty BYDAY uses dtstart weekday',
   ['2024-02-29', 3, [], 0, 3],
   ['2024-02-29', '2024-03-21', '2024-04-11']),
  ('count zero', ['2024-01-01', 1, [0], 0, 0], []),
  ('WKST=SU weekly SA,SU',
   ['2024-03-09', 1, [5, 6], 6, 4],
   ['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17'])],
 [('non-matching dtstart Wednesday',
   ['2024-01-03', 1, [0, 4], 0, 3],
   ['2024-01-05', '2024-01-08', '2024-01-12']),
  ('dtstart mid-week weekly MO,WE,FR',
   ['2024-01-04', 1, [0, 2, 4], 0, 5],
   ['2024-01-05', '2024-01-08', '2024-01-10', '2024-01-12', '2024-01-15']),
  ('empty BYDAY uses dtstart weekday',
   ['2024-02-29', 3, [], 0, 3],
   ['2024-02-29', '2024-03-21', '2024-04-11']),
  ('count zero', ['2024-01-01', 1, [0], 0, 0], []),
  ('WKST=SU weekly SA,SU',
   ['2024-03-09', 1, [5, 6], 6, 4],
   ['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17']),
  ('interval 3 friday from monday', ['2024-05-06', 3, [4], 0, 3], ['2024-05-10', '2024-05-31', '2024-06-21']),
  ('WKST=WE interval 2 MO,TH',
   ['2024-06-04', 2, [0, 3], 2, 5],
   ['2024-06-13', '2024-06-17', '2024-06-27', '2024-07-01', '2024-07-11'])],
 [('empty BYDAY uses dtstart weekday',
   ['2024-02-29', 3, [], 0, 3],
   ['2024-02-29', '2024-03-21', '2024-04-11']),
  ('count zero', ['2024-01-01', 1, [0], 0, 0], []),
  ('WKST=SU weekly SA,SU',
   ['2024-03-09', 1, [5, 6], 6, 4],
   ['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17']),
  ('interval 3 friday from monday', ['2024-05-06', 3, [4], 0, 3], ['2024-05-10', '2024-05-31', '2024-06-21']),
  ('WKST=WE interval 2 MO,TH',
   ['2024-06-04', 2, [0, 3], 2, 5],
   ['2024-06-13', '2024-06-17', '2024-06-27', '2024-07-01', '2024-07-11']),
  ('single sunday series', ['2024-12-29', 2, [6], 0, 3], ['2024-12-29', '2025-01-12', '2025-01-26']),
  ('dtstart on listed day',
   ['2025-01-06', 2, [0, 3], 0, 4],
   ['2025-01-06', '2025-01-09', '2025-01-20', '2025-01-23'])],
 [('WKST=SU weekly SA,SU',
   ['2024-03-09', 1, [5, 6], 6, 4],
   ['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17']),
  ('interval 3 friday from monday', ['2024-05-06', 3, [4], 0, 3], ['2024-05-10', '2024-05-31', '2024-06-21']),
  ('WKST=WE interval 2 MO,TH',
   ['2024-06-04', 2, [0, 3], 2, 5],
   ['2024-06-13', '2024-06-17', '2024-06-27', '2024-07-01', '2024-07-11']),
  ('single sunday series', ['2024-12-29', 2, [6], 0, 3], ['2024-12-29', '2025-01-12', '2025-01-26']),
  ('dtstart on listed day',
   ['2025-01-06', 2, [0, 3], 0, 4],
   ['2025-01-06', '2025-01-09', '2025-01-20', '2025-01-23']),
  ('WKST=SU interval 2 from saturday',
   ['2025-03-01', 2, [6, 5], 6, 5],
   ['2025-03-01', '2025-03-09', '2025-03-15', '2025-03-23', '2025-03-29']),
  ('regression: WKST=SU interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 6, 4],
   ['1997-08-05', '1997-08-17', '1997-08-19', '1997-08-31'])],
 [('WKST=WE interval 2 MO,TH',
   ['2024-06-04', 2, [0, 3], 2, 5],
   ['2024-06-13', '2024-06-17', '2024-06-27', '2024-07-01', '2024-07-11']),
  ('single sunday series', ['2024-12-29', 2, [6], 0, 3], ['2024-12-29', '2025-01-12', '2025-01-26']),
  ('dtstart on listed day',
   ['2025-01-06', 2, [0, 3], 0, 4],
   ['2025-01-06', '2025-01-09', '2025-01-20', '2025-01-23']),
  ('WKST=SU interval 2 from saturday',
   ['2025-03-01', 2, [6, 5], 6, 5],
   ['2025-03-01', '2025-03-09', '2025-03-15', '2025-03-23', '2025-03-29']),
  ('regression: WKST=SU interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 6, 4],
   ['1997-08-05', '1997-08-17', '1997-08-19', '1997-08-31']),
  ('regression: WKST=MO interval 2 TU,SU',
   ['1997-08-05', 2, [1, 6], 0, 4],
   ['1997-08-05', '1997-08-10', '1997-08-19', '1997-08-24']),
  ('non-matching dtstart Wednesday',
   ['2024-01-03', 1, [0, 4], 0, 3],
   ['2024-01-05', '2024-01-08', '2024-01-12'])]]
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: WKST=SU interval 2 TU,SU['1997-08-05', '1997-08-19', '1997-08-17', '1997-09-02']['1997-08-05', '1997-08-17', '1997-08-19', '1997-08-31']Failed
regression: WKST=MO interval 2 TU,SU['1997-08-05', '1997-08-10', '1997-08-19', '1997-08-24']['1997-08-05', '1997-08-10', '1997-08-19', '1997-08-24']Passed
non-matching dtstart Wednesday['2024-01-05', '2024-01-12', '2024-01-08']['2024-01-05', '2024-01-08', '2024-01-12']Failed
dtstart mid-week weekly MO,WE,FR['2024-01-05', '2024-01-12', '2024-01-08', '2024-01-10', '2024-01-19']['2024-01-05', '2024-01-08', '2024-01-10', '2024-01-12', '2024-01-15']Failed
empty BYDAY uses dtstart weekday['2024-02-29', '2024-03-21', '2024-04-11']['2024-02-29', '2024-03-21', '2024-04-11']Passed
count zero[][]Passed
WKST=SU weekly SA,SU['2024-03-09', '2024-03-16', '2024-03-10', '2024-03-23']['2024-03-09', '2024-03-10', '2024-03-16', '2024-03-17']Failed

SHA-256 / 5771c9f3ee1b9734804d93eaf6f1bbe245c5a7bd36f2dd305e5a566775ea111e

HELD IN THE MEMBER ARCHIVE

The verified repair and its recorded checks are member-only.

This mechanism has 7 recorded checks per implementation. The open-access tier publishes the failure and the unsuccessful fix; the repaired source that passes every check, and the observations that prove it, are available to members.

Every case sharing this mechanism uses the same contract and the same repair, so this one record is held back for all of them.

Member access is invitation-based. Sign in with your invited account to inspect the repair.

Sign in to the archive ↗

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

Case digest / 2225ca40dd84ddf916668b8ad75556c9be4295aba30bafb147f9dcfb4db36872