{"version":3,"file":"dates.mjs","names":[],"sources":["../../../../package/common/calendar/dates.ts"],"sourcesContent":["import { CalendarView, type CalendarRange, type CalendarSlot, type CalendarWeekStart } from './calendar.types';\n\n// All arithmetic goes through the local date fields rather than milliseconds: a day with a\n// daylight-saving change is 23 or 25 hours long, so adding 86 400 000 ms lands on the wrong day.\n\nexport const MINUTES_PER_DAY = 1440;\n\nexport function startOfDay(date: Date): Date {\n  return new Date(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\n/** Midnight, `days` calendar days away. */\nexport function addDays(date: Date, days: number): Date {\n  return new Date(date.getFullYear(), date.getMonth(), date.getDate() + days);\n}\n\n/** The same wall-clock time, `days` calendar days away. */\nexport function shiftDays(date: Date, days: number): Date {\n  return new Date(\n    date.getFullYear(),\n    date.getMonth(),\n    date.getDate() + days,\n    date.getHours(),\n    date.getMinutes(),\n    date.getSeconds(),\n    date.getMilliseconds(),\n  );\n}\n\nexport function daysInMonth(year: number, month: number): number {\n  return new Date(year, month + 1, 0).getDate();\n}\n\n/** Midnight, `months` months away. The day is clamped, so 31 January plus one month is the end of February. */\nexport function addMonths(date: Date, months: number): Date {\n  const target = new Date(date.getFullYear(), date.getMonth() + months, 1);\n  const day = Math.min(date.getDate(), daysInMonth(target.getFullYear(), target.getMonth()));\n  return new Date(target.getFullYear(), target.getMonth(), day);\n}\n\nexport function startOfMonth(date: Date): Date {\n  return new Date(date.getFullYear(), date.getMonth(), 1);\n}\n\nexport function startOfWeek(date: Date, weekStartsOn: CalendarWeekStart): Date {\n  return addDays(date, -((date.getDay() - weekStartsOn + 7) % 7));\n}\n\n/** Calendar days from `a` to `b`, whatever their times of day. */\nexport function dayDiff(a: Date, b: Date): number {\n  const utcA = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());\n  const utcB = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());\n  return Math.round((utcB - utcA) / 86_400_000);\n}\n\nexport function isSameDay(a: Date, b: Date): boolean {\n  return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();\n}\n\n// `'2026-09-26'` is also the key for lookups and React keys. Never `getTime()`: where a clock\n// change happens at midnight (America/Santiago), the day's first moment is 01:00, and two paths to\n// the same day disagree.\nexport { dateToDay } from '../utils/day';\n\n/** Wall-clock minutes since midnight. On a 25-hour day the repeated hour maps onto itself. */\nexport function minutesOfDay(date: Date): number {\n  return date.getHours() * 60 + date.getMinutes();\n}\n\n/**\n * The wall-clock time `minutes` after the start of `day`; 1440 is the next midnight. A time the\n * clock skips resolves forwards (02:30 becomes 03:30) and a repeated one to its first occurrence.\n */\nexport function atMinutes(day: Date, minutes: number): Date {\n  return new Date(day.getFullYear(), day.getMonth(), day.getDate(), 0, minutes);\n}\n\n/**\n * `end`, unless a clock change turned the range around: where one end lands in the hour the\n * clocks skip, a wall-clock end can fall on or before its start. Then `minutes` of real time\n * after the start instead.\n */\nexport function endAfter(start: Date, end: Date, minutes: number): Date {\n  return end > start ? end : new Date(start.getTime() + minutes * 60_000);\n}\n\nconst NO_DAYS: ReadonlySet<number> = new Set();\n\n/**\n * The Week view of a narrow calendar: three shown days from the selected one, paged by three, where\n * seven columns would leave no room for a title. Not a `CalendarView`: the switcher still says Week.\n */\nexport const NARROW_WEEK = 'narrowWeek';\n\n/** What the period arithmetic is keyed by: a view, or the narrow calendar's Week. */\nexport type CalendarPeriod = CalendarView | typeof NARROW_WEEK;\n\n/** Shown days in a period that runs from the selected day, or 0 for one fixed to a week or month. */\nfunction rollingDays(period: CalendarPeriod): number {\n  if (period === CalendarView.Day) return 1;\n  return period === NARROW_WEEK ? 3 : 0;\n}\n\n/** Midnight of the first day of the period `date` is in. */\nexport function periodStart(period: CalendarPeriod, date: Date, weekStartsOn: CalendarWeekStart): Date {\n  if (rollingDays(period) > 0) return startOfDay(date);\n  return period === CalendarView.Week ? startOfWeek(date, weekStartsOn) : startOfMonth(date);\n}\n\n/** The days a view shows for `date`. The month view is always six full weeks. */\nexport function periodRange(\n  period: CalendarPeriod,\n  date: Date,\n  weekStartsOn: CalendarWeekStart,\n  hidden: ReadonlySet<number> = NO_DAYS,\n): CalendarRange {\n  const start = periodStart(period, date, weekStartsOn);\n  const rolling = rollingDays(period);\n  if (rolling > 0) return { start, end: addDays(stepShownDays(start, rolling - 1, hidden), 1) };\n  switch (period) {\n    case CalendarView.Month: {\n      const gridStart = startOfWeek(start, weekStartsOn);\n      return { start: gridStart, end: addDays(gridStart, 42) };\n    }\n    case CalendarView.Week:\n      return { start, end: addDays(start, 7) };\n    default:\n      return { start, end: addMonths(start, 1) };\n  }\n}\n\n/** `hiddenDays` as a lookup. A list that leaves no day of the week hides nothing. */\nexport function hiddenDaySet(hiddenDays: readonly number[] | undefined): ReadonlySet<number> {\n  const hidden = new Set(hiddenDays);\n  return hidden.size === 0 || [0, 1, 2, 3, 4, 5, 6].every((day) => hidden.has(day)) ? NO_DAYS : hidden;\n}\n\n/** `date`, or the first shown day from it on in `direction`. */\nexport function shownDay(date: Date, hidden: ReadonlySet<number>, direction: 1 | -1 = 1): Date {\n  let day = startOfDay(date);\n  while (hidden.has(day.getDay())) day = addDays(day, direction);\n  return day;\n}\n\n/** `steps` shown days on from the shown day `date`, or back for a negative count. */\nfunction stepShownDays(date: Date, steps: number, hidden: ReadonlySet<number>): Date {\n  const direction = steps < 0 ? -1 : 1;\n  let day = date;\n  for (let i = 0; i < Math.abs(steps); i++) day = shownDay(addDays(day, direction), hidden, direction);\n  return day;\n}\n\n/** `date` moved by `steps` periods, keeping its place in the period where it can. */\nexport function stepDate(\n  period: CalendarPeriod,\n  date: Date,\n  steps: number,\n  hidden: ReadonlySet<number> = NO_DAYS,\n): Date {\n  const rolling = rollingDays(period);\n  if (rolling > 0) return stepShownDays(date, steps * rolling, hidden);\n  return period === CalendarView.Week ? addDays(date, 7 * steps) : addMonths(date, steps);\n}\n\n/** The shown period plus one on either side: what the swipe pager can reveal. */\nexport function rangeWithNeighbours(\n  period: CalendarPeriod,\n  date: Date,\n  weekStartsOn: CalendarWeekStart,\n  hidden: ReadonlySet<number> = NO_DAYS,\n): CalendarRange {\n  return {\n    start: periodRange(period, stepDate(period, date, -1, hidden), weekStartsOn, hidden).start,\n    end: periodRange(period, stepDate(period, date, 1, hidden), weekStartsOn, hidden).end,\n  };\n}\n\n/** Every day from `first` to `last`, in either order: what creating whole days hands out. */\nexport function allDaySlot(first: Date, last: Date = first): CalendarSlot {\n  const [from, to] = last < first ? [last, first] : [first, last];\n  return { start: startOfDay(from), end: addDays(to, 1), allDay: true };\n}\n\n/**\n * What the header's New event button creates on `day`: in the week and day views the hour from\n * the next full hour of the clock, as calendar apps do, and elsewhere the whole day.\n */\nexport function newEventSlot(period: CalendarPeriod, day: Date, now: Date, slotMinutes: number): CalendarSlot {\n  if (period === CalendarView.Month || period === CalendarView.Agenda) return allDaySlot(day);\n  // 23:00 at the latest, even after 23:00 today: the slot stays on the selected day.\n  const start = atMinutes(day, Math.min(now.getHours() + 1, 23) * 60);\n  // Real time, as a click on empty time makes it: across a clock change an hour on the clock is none or two.\n  const end = new Date(start.getTime() + Math.max(60, slotMinutes) * 60_000);\n  const midnight = addDays(day, 1);\n  return { start, end: end < midnight ? end : midnight, allDay: false };\n}\n\n/** The ISO 8601 week `date` falls in: weeks run Monday to Sunday, and week 1 holds 4 January. */\nexport function weekNumber(date: Date): number {\n  const monday = startOfWeek(date, 1);\n  // A week across New Year belongs to the year of its Thursday.\n  const year = addDays(monday, 3).getFullYear();\n  return dayDiff(startOfWeek(new Date(year, 0, 4), 1), monday) / 7 + 1;\n}\n\n/** Midnight of each of `count` days from `start`. */\nexport function daysFrom(start: Date, count: number): Date[] {\n  return Array.from({ length: count }, (_, i) => addDays(start, i));\n}\n\n/** The days of `count` from `start` that are not hidden: the columns a row of them draws. */\nexport function shownDays(start: Date, count: number, hidden: ReadonlySet<number> = NO_DAYS): Date[] {\n  return daysFrom(start, count).filter((day) => !hidden.has(day.getDay()));\n}\n\nconst KEY_DAYS: Record<string, number> = { ArrowLeft: -1, ArrowRight: 1, ArrowUp: -7, ArrowDown: 7 };\n\nfunction keyTarget(key: string, day: Date, weekStartsOn: CalendarWeekStart): Date | null {\n  if (key in KEY_DAYS) return addDays(day, KEY_DAYS[key]);\n  if (key === 'Home') return startOfWeek(day, weekStartsOn);\n  if (key === 'End') return addDays(startOfWeek(day, weekStartsOn), 6);\n  if (key === 'PageUp') return addMonths(day, -1);\n  if (key === 'PageDown') return addMonths(day, 1);\n  return null;\n}\n\n/**\n * The day a month grid moves to for `key`, or `null` for a key it does not handle. A hidden day\n * gives way to the next shown one in the direction the key points, so the focus stays on a cell.\n */\nexport function gridKeyTarget(\n  key: string,\n  day: Date,\n  weekStartsOn: CalendarWeekStart,\n  hidden: ReadonlySet<number> = NO_DAYS,\n): Date | null {\n  const target = keyTarget(key, day, weekStartsOn);\n  return target && shownDay(target, hidden, key === 'ArrowLeft' || key === 'End' ? -1 : 1);\n}\n"],"mappings":"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"}