Listing management: fields, sorting, and availability.

Listings come in two types:

  • Standard — fixed capacity with optional date
  • Daily — date-based booking with holiday exclusions

Functions

f
addDays

Add days to a YYYY-MM-DD date string

f
addMonthsIso

Add N months to an ISO timestamp, clamping to the last day of the target month. e.g. 2026-01-31 + 1mo → 2026-02-28 Preserves the time component (hour/minute/second/ms). Zero months returns the input with canonical ISO string formatting.

f
bookedRangeLabel

The human-readable label for one booking's actual span: the stored [date, endDate) range when a multi-day range is stored, the listing's fixed duration when only a start date is (legacy rows written before end dates were stored), else the single booked day. "" when there is no date. The ONE booked-range renderer the confirmation email, the /t ticket cards, and the collapsed package displays share, so they can never disagree about a booking's stay.

f
bookedSpanDays

The whole day count of a stored [start_at, end_at) booking range — the customisable day count the buyer chose. A missing or degenerate range is 1.

f
calendarGridDates

Build the calendar grid for a YYYY-MM month as a flat list of YYYY-MM-DD strings. The grid is whole Monday→Sunday weeks spanning the month plus one extra full week on each side, so adjacent-month context is always visible.

f
dateRange

Generate a range of YYYY-MM-DD date strings from start to end (inclusive)

f
daysAgo

Compute how many days ago an listing started, relative to today in the configured timezone. Returns null if the listing date is today or in the future, or if the date is empty/invalid. For past listings, returns a positive integer (1 = yesterday).

f
formatDateLabel

Format a YYYY-MM-DD date for display. Returns "Monday 15 March 2026"

f
formatDateLongLabel

Format a UTC ISO datetime as a date-only label in the configured timezone, e.g. "Monday 15 June 2026" — no time. Returns "" for an empty/invalid input. Used where a stored timestamp should read as a plain published date (the public news post page).

f
formatDateRangeLabel

Format a booking's stored [start_at, end_at) ISO range as a human label. 1-day bookings collapse to formatDateLabel; multi-day bookings use the compact English range formatter (inclusive — subtracts 1 day from end_at, which is the first midnight after the booked window).

f
formatDateRangeLabelCompactEn

Compact English date-range formatter. Uses an en dash () for ranges.

f
formatDatetimeLabel

Format an ISO datetime string for display in the given timezone. Returns e.g. "Monday 15 June 2026 at 14:00 BST"

f
formatDatetimeShort

Compact ISO datetime formatter for table cells. Returns e.g. "07/04/2026 14:00" in the configured timezone.

f
formatMonthLabel

Format a YYYY-MM month string for display, e.g. "July 2026".

f
formatTimeAgo

Human "time ago" label for a past ISO timestamp, relative to nowMsValue (epoch ms), via Intl.RelativeTimeFormat in the largest whole unit that applies — e.g. "now", "5 minutes ago", "yesterday", "2 days ago". Returns null for an unparseable or future timestamp.

f
getAvailableDates

Compute available booking dates for a daily listing. Filters by bookable days of the week and excludes holidays. For listings with duration_days > 1, excludes start dates whose full range would hit a non-bookable day or extend past the booking window.

f
getBookableStartDates

Available start dates for a daily listing's booking/date pickers. Customisable-days listings use single-day availability — the span is chosen separately and validated at submit time — so every individually-bookable start is offered; other listings use their fixed duration.

f
getNextBookableDate

Get the next available booking date for a daily listing. More efficient than getAvailableDates()[0] — stops at first match. Returns null if no bookable dates are available.

f
isBookingRangeValid

Whether booking days consecutive days starting on date is valid for a daily listing: every day must be a bookable weekday, fall outside all holidays, and stay within the listing's booking window. Used to enforce the visitor's chosen span on "customisable days" listings at submit time, where the day count isn't known when the date list is rendered.

f
listingDateToCalendarDate

Convert a UTC ISO datetime to a YYYY-MM-DD calendar date in the given timezone. Returns null if the input is empty or invalid. Used by the calendar view to map standard listing dates to calendar days.

f
loadSortedListings

Load all listings with holidays and return them sorted. With a filter, only the listings it keeps are returned; without one, every listing is returned.

f
mergeListingFields

Determine which contact fields to collect for multiple listings. Returns the union of all field settings, sorted by canonical CONTACT_FIELDS order.

f
monthsAround

List every YYYY-MM month within yearsEitherSide years of the given month's year, in ascending order. e.g. monthsAround("2026-03", 5) runs from "2021-01" to "2031-12". Used to populate the calendar's month picker.

f
normalizeDatetime

Normalize datetime-local "YYYY-MM-DDTHH:MM" to full UTC ISO string. The input is interpreted as local time in the given timezone and converted to UTC.

f
parseDateMs

Parse a date/timestamp string to epoch milliseconds, or null when it doesn't parse — the safe wrapper around Date.parse, whose own failure mode (NaN) is easy to let leak into arithmetic by accident.

f
parseIsoDateParam

Parse a user-supplied YYYY-MM-DD value (a ?date= query param): the string when well-formed and a real calendar date, else null. The UTC round-trip rejects rolled-over impossibilities like 2025-02-30.

f
parseListingFields

Parse a comma-separated fields string into individual ContactField names

f
shiftMonth

Shift a YYYY-MM month string by delta months (negative goes backwards). Crosses year boundaries: shiftMonth("2026-12", 1) → "2027-01".

f
sortListings

Sort listings in unified 3-tier order. Takes anything carrying the values the order is built from, so a narrow picker read sorts the same way a full listing record does.

f
startOfHour

Round a date down to the start of the current hour for cache-stable signatures

f
widestDatedEntry

The dated entry whose booked range ends last — the stay covering a whole package bundle — or null when every entry is date-less (a standard package). A dated entry with no stored end (a single-day booking, or a legacy row) sorts below any ranged stay. Shared by the collapsed email/SVG displays and the /t package card, so every surface picks the SAME representative stay.

f
withRequiredEmail

Ensure "email" is included in an listing fields setting

Interfaces