Booking constraint validation logic.
Pure Elixir functions for validating whether a proposed booking is allowed given a set of constraints, existing events, and availability windows. No database required — works with in-memory data.
Summary
Functions
Returns all events that conflict with a proposed time range.
Checks if a proposed time range overlaps with any existing events.
Counts how many bookings overlap with a given time range.
Snaps a DateTime to the nearest slot boundary.
Snaps a time to the nearest slot boundary based on the slot interval.
Validates a proposed booking against all constraints.
Types
Functions
@spec conflicting_events(DateTime.t(), DateTime.t(), [PhoenixLiveCalendar.Event.t()]) :: [ PhoenixLiveCalendar.Event.t() ]
Returns all events that conflict with a proposed time range.
@spec has_overlap?( DateTime.t(), DateTime.t(), PhoenixLiveCalendar.BookingConfig.t(), [ PhoenixLiveCalendar.Event.t() ] ) :: boolean()
Checks if a proposed time range overlaps with any existing events.
Accounts for buffer times defined in the config.
@spec overlap_count(DateTime.t(), DateTime.t(), [PhoenixLiveCalendar.Event.t()]) :: non_neg_integer()
Counts how many bookings overlap with a given time range.
Used for capacity/seat checking.
@spec snap_datetime_to_slot(DateTime.t(), pos_integer()) :: DateTime.t()
Snaps a DateTime to the nearest slot boundary.
@spec snap_to_slot(Time.t(), pos_integer()) :: Time.t()
Snaps a time to the nearest slot boundary based on the slot interval.
Examples
iex> Constraints.snap_to_slot(~T[10:17:00], 15)
~T[10:15:00]
iex> Constraints.snap_to_slot(~T[10:23:00], 15)
~T[10:15:00]
iex> Constraints.snap_to_slot(~T[10:38:00], 15)
~T[10:30:00]
@spec validate_booking( DateTime.t(), DateTime.t(), PhoenixLiveCalendar.BookingConfig.t(), [PhoenixLiveCalendar.Event.t()], keyword() ) :: validation_result()
Validates a proposed booking against all constraints.
Returns :ok if the booking is valid, or {:error, reason, message} if not.
Parameters
start_dt— Proposed start DateTimeend_dt— Proposed end DateTimeconfig— BookingConfig with constraintsexisting_events— List of existing events to check for overlapsopts— Additional options:now— Current DateTime (default:DateTime.utc_now())resource_id— Resource to check availability againstavailabilities— List of Availability windows
Examples
iex> Constraints.validate_booking(
...> ~U[2026-04-01 10:00:00Z],
...> ~U[2026-04-01 10:30:00Z],
...> %BookingConfig{duration: 30, min_notice: 60},
...> existing_events
...> )
:ok
iex> Constraints.validate_booking(
...> ~U[2026-04-01 10:00:00Z],
...> ~U[2026-04-01 10:15:00Z],
...> %BookingConfig{duration: 30, min_duration: 30},
...> []
...> )
{:error, :too_short, "Booking must be at least 30 minutes"}