{
  "_about": "Conformance vectors for per-playlist-item schedule evaluation. The contract: every implementation (JS server/web, Kotlin, Tizen JS) must agree with every vector. If an implementation disagrees, the implementation is wrong.",
  "_conventions": {
    "days": "active days as a set of integers 0-6, where 0=Sunday .. 6=Saturday (matches JS Date.getDay()).",
    "time": "start/end are local wall-clock HH:MM (24h). A window is [start, end): start inclusive, end exclusive (so adjacent dayparts 07:00-11:00 and 11:00-14:00 do not overlap at 11:00). end may be \"24:00\" meaning end-of-day (whole day when paired with 00:00).",
    "overnight": "if start > end the window crosses midnight: active [start,24:00) on the start day and [00:00,end) on the next day. The day-of-week AND date-range tests apply to the day the window STARTED (a Fri 22:00-02:00 block is active Sat 01:00 because it anchors to Friday).",
    "dates": "start_date/end_date are local YYYY-MM-DD, inclusive on both ends, evaluated against the device-LOCAL calendar date. null start_date = no lower bound; null end_date = no upper bound.",
    "block_match": "within a block, day AND date AND time must all pass. blocks OR together. >=1 matching block = active. zero blocks = always active.",
    "evaluation": "take utc_now, convert to device-local via the IANA timezone, then test the wall-clock block(s)."
  },
  "vectors": [
    { "description": "plain daytime window, inside (Syd Fri 08:00 in Mon-Fri 07:00-11:00)", "utc_now": "2026-06-11T22:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [1,2,3,4,5], "start": "07:00", "end": "11:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "plain daytime window, outside after end (Syd Thu 12:00)", "utc_now": "2026-06-11T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [1,2,3,4,5], "start": "07:00", "end": "11:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "time start is INCLUSIVE (Syd Fri 07:00 exactly)", "utc_now": "2026-06-11T21:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [1,2,3,4,5], "start": "07:00", "end": "11:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "time end is EXCLUSIVE (Syd Fri 11:00 exactly -> out)", "utc_now": "2026-06-12T01:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [1,2,3,4,5], "start": "07:00", "end": "11:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "overnight wrap, before midnight on the start day (Syd Fri 22:30, Fri 22:00-02:00)", "utc_now": "2026-06-12T12:30:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [5], "start": "22:00", "end": "02:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "overnight wrap, after midnight anchored to start day (Syd Sat 01:30 belongs to Fri block)", "utc_now": "2026-06-12T15:30:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [5], "start": "22:00", "end": "02:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "overnight wrap, after midnight but anchor day (yesterday=Sat) not active (Syd Sun 01:30, block Fri)", "utc_now": "2026-06-13T15:30:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [5], "start": "22:00", "end": "02:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "overnight wrap, end is exclusive at 02:00 (Syd Sat 02:00 exactly -> out)", "utc_now": "2026-06-12T16:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [5], "start": "22:00", "end": "02:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "overnight wrap, before midnight on a non-active day (Syd Thu 22:30, block Fri)", "utc_now": "2026-06-11T12:30:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [5], "start": "22:00", "end": "02:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "date range: day before start_date (Syd local 06-11, range 06-12..06-14)", "utc_now": "2026-06-11T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "00:00", "end": "24:00", "start_date": "2026-06-12", "end_date": "2026-06-14" }], "expected": false },
    { "description": "date range: on start_date inclusive (Syd local 06-12)", "utc_now": "2026-06-12T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "00:00", "end": "24:00", "start_date": "2026-06-12", "end_date": "2026-06-14" }], "expected": true },
    { "description": "date range: on end_date inclusive (Syd local 06-14)", "utc_now": "2026-06-14T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "00:00", "end": "24:00", "start_date": "2026-06-12", "end_date": "2026-06-14" }], "expected": true },
    { "description": "date range: day after end_date (Syd local 06-15)", "utc_now": "2026-06-15T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "00:00", "end": "24:00", "start_date": "2026-06-12", "end_date": "2026-06-14" }], "expected": false },
    { "description": "null start_date = no lower bound (Syd local 2026-01-01, end 06-14)", "utc_now": "2026-01-01T01:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "00:00", "end": "24:00", "start_date": null, "end_date": "2026-06-14" }], "expected": true },
    { "description": "null end_date = no upper bound (Syd local 2026-12-31, start 06-12)", "utc_now": "2026-12-31T01:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "00:00", "end": "24:00", "start_date": "2026-06-12", "end_date": null }], "expected": true },
    { "description": "both dates null = unbounded (always, given day/time ok)", "utc_now": "2026-06-11T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "00:00", "end": "24:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "date in range but wrong weekday (Syd Fri 06-12, block Mon only)", "utc_now": "2026-06-12T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [1], "start": "00:00", "end": "24:00", "start_date": "2026-06-12", "end_date": "2026-06-14" }], "expected": false },
    { "description": "right weekday but date out of range (Syd Fri 06-19, block Fri but range 06-12..06-12)", "utc_now": "2026-06-19T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [5], "start": "00:00", "end": "24:00", "start_date": "2026-06-12", "end_date": "2026-06-12" }], "expected": false },
    { "description": "multiple blocks OR: second block matches (Syd Sat 10:00)", "utc_now": "2026-06-13T00:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [1,2,3,4,5], "start": "07:00", "end": "11:00", "start_date": null, "end_date": null }, { "days": [0,6], "start": "09:00", "end": "13:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "multiple blocks OR: none match (Syd Sat 14:00)", "utc_now": "2026-06-13T04:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [1,2,3,4,5], "start": "07:00", "end": "11:00", "start_date": null, "end_date": null }, { "days": [0,6], "start": "09:00", "end": "13:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "zero blocks = always on", "utc_now": "2026-06-11T02:00:00Z", "timezone": "Australia/Sydney", "blocks": [], "expected": true },
    { "description": "tz correctness: same UTC, Sydney -> 09:00 in window", "utc_now": "2026-06-11T23:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "09:00", "end": "17:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "tz correctness: same UTC, Berlin -> 01:00 out of window", "utc_now": "2026-06-11T23:00:00Z", "timezone": "Europe/Berlin", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "09:00", "end": "17:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "tz correctness: same UTC, Chicago -> 18:00 out of window", "utc_now": "2026-06-11T23:00:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "09:00", "end": "17:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "tz correctness: different UTC, Chicago -> 11:00 in window", "utc_now": "2026-06-11T16:00:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "09:00", "end": "17:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "tz correctness: different UTC, Berlin -> 18:00 out of window", "utc_now": "2026-06-11T16:00:00Z", "timezone": "Europe/Berlin", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "09:00", "end": "17:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "tz correctness: different UTC, Sydney -> 02:00 out of window", "utc_now": "2026-06-11T16:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "09:00", "end": "17:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "DST US spring-forward: after the gap, local 03:00 CDT in 03:00-04:00", "utc_now": "2026-03-08T08:00:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "03:00", "end": "04:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "DST US spring-forward: before the gap, local 01:30 CST out of 03:00-04:00", "utc_now": "2026-03-08T07:30:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "03:00", "end": "04:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "DST US spring-forward: window inside the 02:00-03:00 hole is unreachable (local 03:00)", "utc_now": "2026-03-08T08:00:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "02:15", "end": "02:45", "start_date": null, "end_date": null }], "expected": false },
    { "description": "DST US fall-back: first 01:30 (CDT) in 01:00-02:00", "utc_now": "2026-11-01T06:30:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "01:00", "end": "02:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "DST US fall-back: repeated 01:30 (CST) still in 01:00-02:00", "utc_now": "2026-11-01T07:30:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "01:00", "end": "02:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "DST US fall-back: after the repeated hour, local 02:30 out of 01:00-02:00", "utc_now": "2026-11-01T08:30:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "01:00", "end": "02:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "DST AU spring-forward: after the gap, Sydney local 03:30 AEDT in 03:00-04:00", "utc_now": "2026-10-03T16:30:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "03:00", "end": "04:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "DST AU spring-forward: before the gap, Sydney local 01:30 AEST out of 03:00-04:00", "utc_now": "2026-10-03T15:30:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "03:00", "end": "04:00", "start_date": null, "end_date": null }], "expected": false },
    { "description": "DST AU fall-back: first 02:30 (AEDT) in 02:00-03:00", "utc_now": "2026-04-04T15:30:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "02:00", "end": "03:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "DST AU fall-back: repeated 02:30 (AEST) still in 02:00-03:00", "utc_now": "2026-04-04T16:30:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "02:00", "end": "03:00", "start_date": null, "end_date": null }], "expected": true },
    { "description": "device-local date AFTER utc date (UTC 06-11 15:00 = Syd 06-12 01:00); date range tests local date", "utc_now": "2026-06-11T15:00:00Z", "timezone": "Australia/Sydney", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "00:00", "end": "06:00", "start_date": "2026-06-12", "end_date": "2026-06-12" }], "expected": true },
    { "description": "device-local date BEFORE utc date (UTC 06-12 03:00 = Chicago 06-11 22:00); date range tests local date", "utc_now": "2026-06-12T03:00:00Z", "timezone": "America/Chicago", "blocks": [{ "days": [0,1,2,3,4,5,6], "start": "20:00", "end": "23:00", "start_date": "2026-06-11", "end_date": "2026-06-11" }], "expected": true }
  ]
}
