FAILURE MAP
← Case archive

FA-76906 / Calendar recurrence rules / Open access

Monthly ordinal BYDAY such as 2TU or -1FR: missing occurrence · case 01

A 5th-Monday series reports the 4th Monday in months without a fifth one.

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

ROOT CAUSE

A nonexistent fifth occurrence falls back to the previous week instead of skipping the month.

THE FAILURE

A nonexistent fifth occurrence falls back to the previous week instead of skipping the month.

Unsuccessful approach: The partial repair `if day < 1 or day > dim:; break` stops the whole series at the first month without the occurrence.

Case contract

FREQ=MONTHLY;BYDAY=<ordinal><weekday>. weekday is 0=Monday..6=Sunday; ordinal 1..5 counts from the start of the month and -1..-5 from the end. A month lacking that occurrence is skipped and does not consume COUNT. Months step by interval from the dtstart month; instances before dtstart are dropped. 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, ordinal, weekday, count):
    start = dt.date.fromisoformat(dtstart)
    out = []
    for k in range(600):
        if len(out) >= count:
            return out
        mi = start.month - 1 + k * interval
        y, m = start.year + mi // 12, mi % 12 + 1
        dim = calendar.monthrange(y, m)[1]
        first_wd = dt.date(y, m, 1).weekday()
        last_wd = dt.date(y, m, dim).weekday()
        if ordinal > 0:
            day = 1 + (weekday - first_wd) % 7 + 7 * (ordinal - 1)
        else:
            day = dim - (last_wd - weekday) % 7 + 7 * (ordinal + 1)
        if day > dim:
            day -= 7
        d = dt.date(y, m, day)
        if d < start:
            continue
        out.append(d.isoformat())
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: second tuesday',
   ['2024-01-01', 1, 2, 1, 4],
   ['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']),
  ('last friday', ['2024-01-01', 1, -1, 4, 4], ['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26']),
  ('second to last sunday',
   ['2024-02-01', 1, -2, 6, 4],
   ['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']),
  ('fifth monday skips months', ['2024-01-01', 1, 5, 0, 3], ['2024-01-29', '2024-04-29', '2024-07-29']),
  ('interval 2 first saturday',
   ['2024-11-01', 2, 1, 5, 4],
   ['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']),
  ('first-month occurrence before dtstart',
   ['2024-03-20', 1, 1, 2, 3],
   ['2024-04-03', '2024-05-01', '2024-06-05']),
  ('third wednesday quarterly',
   ['2023-12-01', 3, 3, 2, 4],
   ['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18'])],
 [('second to last sunday',
   ['2024-02-01', 1, -2, 6, 4],
   ['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']),
  ('fifth monday skips months', ['2024-01-01', 1, 5, 0, 3], ['2024-01-29', '2024-04-29', '2024-07-29']),
  ('interval 2 first saturday',
   ['2024-11-01', 2, 1, 5, 4],
   ['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']),
  ('first-month occurrence before dtstart',
   ['2024-03-20', 1, 1, 2, 3],
   ['2024-04-03', '2024-05-01', '2024-06-05']),
  ('third wednesday quarterly',
   ['2023-12-01', 3, 3, 2, 4],
   ['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']),
  ('fourth sunday', ['2025-02-10', 1, 4, 6, 4], ['2025-02-23', '2025-03-23', '2025-04-27', '2025-05-25']),
  ('last monday dtstart on it', ['2024-05-27', 1, -1, 0, 3], ['2024-05-27', '2024-06-24', '2024-07-29'])],
 [('interval 2 first saturday',
   ['2024-11-01', 2, 1, 5, 4],
   ['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']),
  ('first-month occurrence before dtstart',
   ['2024-03-20', 1, 1, 2, 3],
   ['2024-04-03', '2024-05-01', '2024-06-05']),
  ('third wednesday quarterly',
   ['2023-12-01', 3, 3, 2, 4],
   ['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']),
  ('fourth sunday', ['2025-02-10', 1, 4, 6, 4], ['2025-02-23', '2025-03-23', '2025-04-27', '2025-05-25']),
  ('last monday dtstart on it', ['2024-05-27', 1, -1, 0, 3], ['2024-05-27', '2024-06-24', '2024-07-29']),
  ('count zero', ['2024-01-01', 1, 1, 0, 0], []),
  ('fifth friday interval 1',
   ['2024-02-01', 1, 5, 4, 4],
   ['2024-03-29', '2024-05-31', '2024-08-30', '2024-11-29'])],
 [('third wednesday quarterly',
   ['2023-12-01', 3, 3, 2, 4],
   ['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']),
  ('fourth sunday', ['2025-02-10', 1, 4, 6, 4], ['2025-02-23', '2025-03-23', '2025-04-27', '2025-05-25']),
  ('last monday dtstart on it', ['2024-05-27', 1, -1, 0, 3], ['2024-05-27', '2024-06-24', '2024-07-29']),
  ('count zero', ['2024-01-01', 1, 1, 0, 0], []),
  ('fifth friday interval 1',
   ['2024-02-01', 1, 5, 4, 4],
   ['2024-03-29', '2024-05-31', '2024-08-30', '2024-11-29']),
  ('regression: second tuesday',
   ['2024-01-01', 1, 2, 1, 4],
   ['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']),
  ('last friday', ['2024-01-01', 1, -1, 4, 4], ['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26'])],
 [('last monday dtstart on it', ['2024-05-27', 1, -1, 0, 3], ['2024-05-27', '2024-06-24', '2024-07-29']),
  ('count zero', ['2024-01-01', 1, 1, 0, 0], []),
  ('fifth friday interval 1',
   ['2024-02-01', 1, 5, 4, 4],
   ['2024-03-29', '2024-05-31', '2024-08-30', '2024-11-29']),
  ('regression: second tuesday',
   ['2024-01-01', 1, 2, 1, 4],
   ['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']),
  ('last friday', ['2024-01-01', 1, -1, 4, 4], ['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26']),
  ('second to last sunday',
   ['2024-02-01', 1, -2, 6, 4],
   ['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']),
  ('fifth monday skips months', ['2024-01-01', 1, 5, 0, 3], ['2024-01-29', '2024-04-29', '2024-07-29'])]]
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: second tuesday['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']Passed
last friday['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26']['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26']Passed
second to last sunday['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']Passed
fifth monday skips months['2024-01-29', '2024-02-26', '2024-03-25']['2024-01-29', '2024-04-29', '2024-07-29']Failed
interval 2 first saturday['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']Passed
first-month occurrence before dtstart['2024-04-03', '2024-05-01', '2024-06-05']['2024-04-03', '2024-05-01', '2024-06-05']Passed
third wednesday quarterly['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']Passed

SHA-256 / 2754b130bc9693cdecf4a686ed7ebd78b0c1f65d9f2cbd4bac74c2ae7a8699b8

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, ordinal, weekday, count):
    start = dt.date.fromisoformat(dtstart)
    out = []
    for k in range(600):
        if len(out) >= count:
            return out
        mi = start.month - 1 + k * interval
        y, m = start.year + mi // 12, mi % 12 + 1
        dim = calendar.monthrange(y, m)[1]
        first_wd = dt.date(y, m, 1).weekday()
        last_wd = dt.date(y, m, dim).weekday()
        if ordinal > 0:
            day = 1 + (weekday - first_wd) % 7 + 7 * (ordinal - 1)
        else:
            day = dim - (last_wd - weekday) % 7 + 7 * (ordinal + 1)
        if day < 1 or day > dim:
            break
        d = dt.date(y, m, day)
        if d < start:
            continue
        out.append(d.isoformat())
    return out
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: second tuesday',
   ['2024-01-01', 1, 2, 1, 4],
   ['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']),
  ('last friday', ['2024-01-01', 1, -1, 4, 4], ['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26']),
  ('second to last sunday',
   ['2024-02-01', 1, -2, 6, 4],
   ['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']),
  ('fifth monday skips months', ['2024-01-01', 1, 5, 0, 3], ['2024-01-29', '2024-04-29', '2024-07-29']),
  ('interval 2 first saturday',
   ['2024-11-01', 2, 1, 5, 4],
   ['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']),
  ('first-month occurrence before dtstart',
   ['2024-03-20', 1, 1, 2, 3],
   ['2024-04-03', '2024-05-01', '2024-06-05']),
  ('third wednesday quarterly',
   ['2023-12-01', 3, 3, 2, 4],
   ['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18'])],
 [('second to last sunday',
   ['2024-02-01', 1, -2, 6, 4],
   ['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']),
  ('fifth monday skips months', ['2024-01-01', 1, 5, 0, 3], ['2024-01-29', '2024-04-29', '2024-07-29']),
  ('interval 2 first saturday',
   ['2024-11-01', 2, 1, 5, 4],
   ['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']),
  ('first-month occurrence before dtstart',
   ['2024-03-20', 1, 1, 2, 3],
   ['2024-04-03', '2024-05-01', '2024-06-05']),
  ('third wednesday quarterly',
   ['2023-12-01', 3, 3, 2, 4],
   ['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']),
  ('fourth sunday', ['2025-02-10', 1, 4, 6, 4], ['2025-02-23', '2025-03-23', '2025-04-27', '2025-05-25']),
  ('last monday dtstart on it', ['2024-05-27', 1, -1, 0, 3], ['2024-05-27', '2024-06-24', '2024-07-29'])],
 [('interval 2 first saturday',
   ['2024-11-01', 2, 1, 5, 4],
   ['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']),
  ('first-month occurrence before dtstart',
   ['2024-03-20', 1, 1, 2, 3],
   ['2024-04-03', '2024-05-01', '2024-06-05']),
  ('third wednesday quarterly',
   ['2023-12-01', 3, 3, 2, 4],
   ['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']),
  ('fourth sunday', ['2025-02-10', 1, 4, 6, 4], ['2025-02-23', '2025-03-23', '2025-04-27', '2025-05-25']),
  ('last monday dtstart on it', ['2024-05-27', 1, -1, 0, 3], ['2024-05-27', '2024-06-24', '2024-07-29']),
  ('count zero', ['2024-01-01', 1, 1, 0, 0], []),
  ('fifth friday interval 1',
   ['2024-02-01', 1, 5, 4, 4],
   ['2024-03-29', '2024-05-31', '2024-08-30', '2024-11-29'])],
 [('third wednesday quarterly',
   ['2023-12-01', 3, 3, 2, 4],
   ['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']),
  ('fourth sunday', ['2025-02-10', 1, 4, 6, 4], ['2025-02-23', '2025-03-23', '2025-04-27', '2025-05-25']),
  ('last monday dtstart on it', ['2024-05-27', 1, -1, 0, 3], ['2024-05-27', '2024-06-24', '2024-07-29']),
  ('count zero', ['2024-01-01', 1, 1, 0, 0], []),
  ('fifth friday interval 1',
   ['2024-02-01', 1, 5, 4, 4],
   ['2024-03-29', '2024-05-31', '2024-08-30', '2024-11-29']),
  ('regression: second tuesday',
   ['2024-01-01', 1, 2, 1, 4],
   ['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']),
  ('last friday', ['2024-01-01', 1, -1, 4, 4], ['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26'])],
 [('last monday dtstart on it', ['2024-05-27', 1, -1, 0, 3], ['2024-05-27', '2024-06-24', '2024-07-29']),
  ('count zero', ['2024-01-01', 1, 1, 0, 0], []),
  ('fifth friday interval 1',
   ['2024-02-01', 1, 5, 4, 4],
   ['2024-03-29', '2024-05-31', '2024-08-30', '2024-11-29']),
  ('regression: second tuesday',
   ['2024-01-01', 1, 2, 1, 4],
   ['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']),
  ('last friday', ['2024-01-01', 1, -1, 4, 4], ['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26']),
  ('second to last sunday',
   ['2024-02-01', 1, -2, 6, 4],
   ['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']),
  ('fifth monday skips months', ['2024-01-01', 1, 5, 0, 3], ['2024-01-29', '2024-04-29', '2024-07-29'])]]
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: second tuesday['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']['2024-01-09', '2024-02-13', '2024-03-12', '2024-04-09']Passed
last friday['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26']['2024-01-26', '2024-02-23', '2024-03-29', '2024-04-26']Passed
second to last sunday['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']['2024-02-18', '2024-03-24', '2024-04-21', '2024-05-19']Passed
fifth monday skips months['2024-01-29']['2024-01-29', '2024-04-29', '2024-07-29']Failed
interval 2 first saturday['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']['2024-11-02', '2025-01-04', '2025-03-01', '2025-05-03']Passed
first-month occurrence before dtstart['2024-04-03', '2024-05-01', '2024-06-05']['2024-04-03', '2024-05-01', '2024-06-05']Passed
third wednesday quarterly['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']['2023-12-20', '2024-03-20', '2024-06-19', '2024-09-18']Passed

SHA-256 / 0958bc877e75b86c4dc85a1f813c58adec22c8417863af80f67ccd8621c75bda

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

Case digest / bca335ad0d6866e115fbf38097f2f2d60df5463c2663f163080022fd48af7554