日本語 · English

US Election Day, Black Friday and the Sunday after Thanksgiving: dates relative to a computed date

Translated from the canonical Japanese page recipes/us-election-day-and-thanksgiving.md.

US Election Day is “the Tuesday after the first Monday in November”, Thanksgiving is “the fourth Thursday in November”, Black Friday is the day after it and Cyber Monday the Monday after that. Each is “n days from a date that another rule decides”, and the statutes and customs define them in exactly that order of words. Kairos lets you write them in that order: derive the earlier date first, then feed that stream into the next rule (closure).

What happens with existing recurrence rules

Writing it in Kairos, in the order the law states it

Election Day: pick “the first Monday in November” within the year window, then the next day:

# eval: 2024-01-01..2029-01-01 tz: America/New_York
premise US {
  calendar-system: Gregorian
  tz: "America/New_York"
  wkst: Sun
}
@US
firstMonNov = everyDay |> filter(d => weekday(d) == Mon and month(d) == 11) |> within(year) |> first
firstMonNov |> shift(+1, unit: day)
#=> 2024-11-05 2025-11-04 2026-11-03 2027-11-02 2028-11-07

Federal elections are held in even-numbered years only, so a filter on the year gives “federal Election Day” (odd years are state and local elections):

# eval: 2024-01-01..2031-01-01 tz: America/New_York
premise US {
  calendar-system: Gregorian
  tz: "America/New_York"
  wkst: Sun
}
@US
firstMonNov = everyDay |> filter(d => weekday(d) == Mon and month(d) == 11) |> within(year) |> first
firstMonNov |> shift(+1, unit: day) |> filter(d => year(d) mod 2 == 0)
#=> 2024-11-05 2026-11-03 2028-11-07 2030-11-05

Black Friday: the day after Thanksgiving (the fourth Thursday in November). With Thanksgiving bound, it is one line:

# eval: 2024-01-01..2028-01-01 tz: America/New_York
premise US {
  calendar-system: Gregorian
  tz: "America/New_York"
  wkst: Sun
}
@US
thanksgiving = everyDay |> filter(d => weekday(d) == Thu and month(d) == 11) |> within(month) |> nth(4)
thanksgiving |> shift(+1, unit: day)
#=> 2024-11-29 2025-11-28 2026-11-27 2027-11-26

Cyber Monday: four days after Thanksgiving:

# eval: 2024-01-01..2028-01-01 tz: America/New_York
premise US {
  calendar-system: Gregorian
  tz: "America/New_York"
  wkst: Sun
}
@US
thanksgiving = everyDay |> filter(d => weekday(d) == Thu and month(d) == 11) |> within(month) |> nth(4)
thanksgiving |> shift(+4, unit: day)
#=> 2024-12-02 2025-12-01 2026-11-30 2027-11-29

The Sunday after Thanksgiving: roll forward from Thanksgiving onto Sundays. 2024 gives December 1: the month boundary comes out right, because the derived stream is simply the input of the next rule and month edges play no part:

# eval: 2024-01-01..2028-01-01 tz: America/New_York
premise US {
  calendar-system: Gregorian
  tz: "America/New_York"
  wkst: Sun
}
@US
thanksgiving = everyDay |> filter(d => weekday(d) == Thu and month(d) == 11) |> within(month) |> nth(4)
thanksgiving |> roll(Following, on: (everyDay |> filter(d => weekday(d) == Sun)))
#=> 2024-12-01 2025-11-30 2026-11-29 2027-11-28

Scope

Try it in your browser

Election Day · Black Friday · Cyber Monday · the Sunday after Thanksgiving