Skip to content

fix!: define deterministic operating hours for Location service - #687

Open
igrigorik wants to merge 1 commit into
feat/locationfrom
fix/location-schemaorg-hours
Open

fix!: define deterministic operating hours for Location service#687
igrigorik wants to merge 1 commit into
feat/locationfrom
fix/location-schemaorg-hours

Conversation

@igrigorik

@igrigorik igrigorik commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

The current Location PR (#687) introduces weekly and exceptional operating hours,
but leaves several wire and evaluation semantics ambiguous. In particular,
closures rely on an artificial midnight interval, open_now depends on an
implicit server clock, exception date bounds are unclear, and the specification
does not define timezone, overnight, DST, overlap, or precedence behavior.

Close those gaps with a UCP-native schedule model informed by Schema.org's
OpeningHoursSpecification:
https://schema.org/OpeningHoursSpecification

Schema.org is design input only. UCP owns the field names, values, and
evaluation rules defined here.

Make weekly intervals explicit and reusable:

   "hours": [
     {
       "day": "tuesday",
       "opens": "09:00",
       "closes": "12:00"
     },
     {
       "day": "tuesday",
       "opens": "13:00",
       "closes": "21:00"
     }
   ]

Rename open and close to opens and closes, and define day as a
stable UCP weekday identifier rather than localized display text. Multiple
entries for one day represent split shifts, and an interval whose closing time
is earlier than its opening time continues into the next local date.

Refactor the shared time interval schema so opens and closes are an
optional but inseparable pair. Weekly hours require both fields, while
exception hours may omit both to represent a full closure. Reject the ambiguous
00:00 to 00:00 pair and reserve 00:00 to 23:59 as the full-local-day
sentinel.

Replace the previous exception shape:

   {
     "from": "2026-11-26",
     "through": "2026-11-27",
     "label": "Thanksgiving",
     "open": "00:00",
     "close": "00:00"
   }

with inclusive local-date bounds and an actual closure representation:

   {
     "title": "Thanksgiving",
     "valid_from": "2026-11-26",
     "valid_through": "2026-11-26"
   }

Rename from, through, and label to valid_from, valid_through, and
title. Treat title as optional presentation metadata that does not affect
schedule evaluation. Allow timed exceptions with paired opens and closes,
including multiple entries with identical bounds for split shifts.

Define every returned schedule in the Location's Business-owned IANA timezone.
Require timezone whenever regular or exception hours are present, and keep
the canonical schedule independent of the requesting Platform or Buyer's
timezone.

Specify deterministic evaluation:

  • convert an exact instant into each Location's local date, weekday, and time
  • use half-open timed intervals, except for the reserved full-day sentinel
  • let overnight intervals carry into the following local date
  • replace regular hours with exception hours at local midnight
  • treat omitted weekdays as having no interval starting that day
  • treat absent schedules as unknown rather than closed
  • evaluate DST gaps and folds pointwise without shifting nonexistent times
  • reject equal time pairs and intersecting non-identical exception ranges as Business conformance errors where JSON Schema cannot express the constraint

Remove the redundant open_now filter. It makes results depend on an implicit
processing clock and creates undefined precedence when combined with
open_at. Require one caller-supplied RFC 3339 instant instead:

   "filters": {
     "hours": {
       "open_at": "2026-05-18T17:00:00Z"
     }
   }

Require open_at to include Z or a numeric offset. The offset identifies the
instant only; the Business still evaluates that instant using each candidate
Location's authoritative IANA timezone. Keep the nested hours filter open so
extensions can add qualifiers without changing the standard predicate.

Move complete Search and Lookup examples into the transport-neutral capability
documents. Cover hours with serviceability and amenities, inventory with
distance, split shifts, full closures, and partial Lookup success there.

Reduce REST and MCP examples to equivalent binding envelopes that link to the
same canonical payload examples. This keeps both transports on equal footing,
avoids duplicating domain semantics, and prevents one binding's examples from
becoming more complete or authoritative than the other. Preserve MCP's
required meta["ucp-agent"].profile contract while separating protocol
metadata from the Location request.

This is a breaking correction to the Location PR's draft wire shape:

  • open becomes opens
  • close becomes closes
  • from becomes valid_from
  • through becomes valid_through
  • label becomes title
  • open_now is removed
  • full closures omit both time fields instead of using 00:00 to 00:00

   The current Location PR introduces weekly and exceptional operating hours,
   but leaves several wire and evaluation semantics ambiguous. In particular,
   closures rely on an artificial midnight interval, `open_now` depends on an
   implicit server clock, exception date bounds are unclear, and the specification
   does not define timezone, overnight, DST, overlap, or precedence behavior.

   Close those gaps with a UCP-native schedule model informed by Schema.org's
   OpeningHoursSpecification:
   https://schema.org/OpeningHoursSpecification

   Schema.org is design input only. UCP owns the field names, values, and
   evaluation rules defined here.

   Make weekly intervals explicit and reusable:

       "hours": [
         {
           "day": "tuesday",
           "opens": "09:00",
           "closes": "12:00"
         },
         {
           "day": "tuesday",
           "opens": "13:00",
           "closes": "21:00"
         }
       ]

   Rename `open` and `close` to `opens` and `closes`, and define `day` as a
   stable UCP weekday identifier rather than localized display text. Multiple
   entries for one day represent split shifts, and an interval whose closing time
   is earlier than its opening time continues into the next local date.

   Refactor the shared time interval schema so `opens` and `closes` are an
   optional but inseparable pair. Weekly hours require both fields, while
   exception hours may omit both to represent a full closure. Reject the ambiguous
   `00:00` to `00:00` pair and reserve `00:00` to `23:59` as the full-local-day
   sentinel.

   Replace the previous exception shape:

       {
         "from": "2026-11-26",
         "through": "2026-11-27",
         "label": "Thanksgiving",
         "open": "00:00",
         "close": "00:00"
       }

   with inclusive local-date bounds and an actual closure representation:

       {
         "title": "Thanksgiving",
         "valid_from": "2026-11-26",
         "valid_through": "2026-11-26"
       }

   Rename `from`, `through`, and `label` to `valid_from`, `valid_through`, and
   `title`. Treat `title` as optional presentation metadata that does not affect
   schedule evaluation. Allow timed exceptions with paired `opens` and `closes`,
   including multiple entries with identical bounds for split shifts.

   Define every returned schedule in the Location's Business-owned IANA timezone.
   Require `timezone` whenever regular or exception hours are present, and keep
   the canonical schedule independent of the requesting Platform or Buyer's
   timezone.

   Specify deterministic evaluation:

   - convert an exact instant into each Location's local date, weekday, and time
   - use half-open timed intervals, except for the reserved full-day sentinel
   - let overnight intervals carry into the following local date
   - replace regular hours with exception hours at local midnight
   - treat omitted weekdays as having no interval starting that day
   - treat absent schedules as unknown rather than closed
   - evaluate DST gaps and folds pointwise without shifting nonexistent times
   - reject equal time pairs and intersecting non-identical exception ranges as
     Business conformance errors where JSON Schema cannot express the constraint

   Remove the redundant `open_now` filter. It makes results depend on an implicit
   processing clock and creates undefined precedence when combined with
   `open_at`. Require one caller-supplied RFC 3339 instant instead:

       "filters": {
         "hours": {
           "open_at": "2026-05-18T17:00:00Z"
         }
       }

   Require `open_at` to include `Z` or a numeric offset. The offset identifies the
   instant only; the Business still evaluates that instant using each candidate
   Location's authoritative IANA timezone. Keep the nested hours filter open so
   extensions can add qualifiers without changing the standard predicate.

   Move complete Search and Lookup examples into the transport-neutral capability
   documents. Cover hours with serviceability and amenities, inventory with
   distance, split shifts, full closures, and partial Lookup success there.

   Reduce REST and MCP examples to equivalent binding envelopes that link to the
   same canonical payload examples. This keeps both transports on equal footing,
   avoids duplicating domain semantics, and prevents one binding's examples from
   becoming more complete or authoritative than the other. Preserve MCP's
   required `meta["ucp-agent"].profile` contract while separating protocol
   metadata from the Location request.

   This is a breaking correction to the Location PR's draft wire shape:

   - `open` becomes `opens`
   - `close` becomes `closes`
   - `from` becomes `valid_from`
   - `through` becomes `valid_through`
   - `label` becomes `title`
   - `open_now` is removed
   - full closures omit both time fields instead of using `00:00` to `00:00`
@igrigorik igrigorik added this to the 2026-08-14 milestone Aug 5, 2026
@igrigorik
igrigorik requested a review from jingyli August 5, 2026 14:06
@igrigorik igrigorik self-assigned this Aug 5, 2026
@igrigorik igrigorik added the TC review Ready for TC review label Aug 5, 2026
"exception_hours": [
{
"title": "Thanksgiving",
"valid_from": "2026-11-26",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add some kind of description as well , with the title for the exception hours ?

"business might close early due to Thanksgiving or something like that " ,

at times description could be useful

{
"ucp": {
"version": "{{ ucp_version }}",
"capabilities": {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also add in expiry_at after this platform or agent should pull new info, this could help agent understand that the location info is stale and they need to pull new information ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

TC review Ready for TC review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants