Resolve applicable per-energy prices for a URPX rate plan at a given instant.
Given a coerced rate plan (urpx.coerce/coerce-rate-plan) and a
ZonedDateTime, resolve-prices returns the season, the matched TOU period
(if any), and one entry per active per-energy ledger (commodity, distribution,
public benefits, etc.). Each ledger entry carries a :tiers vector — for
block-tiered ledgers (CPAU E-1) every tier's bound and unit-price is
surfaced so the caller can pick the active one based on cumulative usage;
for non-tiered ledgers the vector has a single entry.
Skipped:
:urpx/hasCalculationMethod is set
(recurring fixed charges like a monthly customer charge — these are
subscription-style and don't apply to instantaneous lookup).Day types honored (URPX ontology): urpx:allDays, urpx:weekdayDays, urpx:weekendDays, urpx:customDays. Holiday inclusion is expressed by the TimeBracket's optional urpx:includeHolidays / urpx:includeNonHolidays booleans (both default to true when omitted). Holiday detection sources, in priority order:
:holiday? predicate in opts (highest precedence — overrides).urpx.holidays/derive-predicate.Plans without a HolidayCalendar and called without :holiday? retain the
pre-existing 'every date is a non-holiday' fallback — so brackets that
opt out of non-holidays (urpx:includeNonHolidays = false) will never match.
Resolve applicable per-energy prices for a URPX rate plan at a given instant.
Given a coerced rate plan (`urpx.coerce/coerce-rate-plan`) and a
ZonedDateTime, `resolve-prices` returns the season, the matched TOU period
(if any), and one entry per active per-energy ledger (commodity, distribution,
public benefits, etc.). Each ledger entry carries a `:tiers` vector — for
block-tiered ledgers (CPAU E-1) every tier's bound and unit-price is
surfaced so the caller can pick the active one based on cumulative usage;
for non-tiered ledgers the vector has a single entry.
Skipped:
- PriceDefinitions whose own `:urpx/hasCalculationMethod` is set
(recurring fixed charges like a monthly customer charge — these are
subscription-style and don't apply to instantaneous lookup).
Day types honored (URPX ontology): urpx:allDays, urpx:weekdayDays,
urpx:weekendDays, urpx:customDays. Holiday inclusion is expressed by the
TimeBracket's optional urpx:includeHolidays / urpx:includeNonHolidays
booleans (both default to true when omitted). Holiday detection sources,
in priority order:
1. Explicit `:holiday?` predicate in opts (highest precedence — overrides).
2. The plan's embedded urpx:HolidayCalendar, derived via
`urpx.holidays/derive-predicate`.
3. Fallback: every date treated as a non-holiday.
Plans without a HolidayCalendar and called without `:holiday?` retain the
pre-existing 'every date is a non-holiday' fallback — so brackets that
opt out of non-holidays (urpx:includeNonHolidays = false) will never match.(has-energy-ledger? resolved)True if resolved carries any ledger measured in urpx:energy, which is the
precondition for marginal-unit-rate returning a number.
Ask this before calling it when sweeping a corpus. Plenty of real schedules price nothing per kWh (street lighting, fixed service charges), and for those a marginal per-kWh rate is not a small number, it is a question that does not apply.
True if `resolved` carries any ledger measured in urpx:energy, which is the precondition for `marginal-unit-rate` returning a number. Ask this before calling it when sweeping a corpus. Plenty of real schedules price nothing per kWh (street lighting, fixed service charges), and for those a marginal per-kWh rate is not a small number, it is a question that does not apply.
(marginal-unit-rate resolved)(marginal-unit-rate resolved opts)The marginal $/kWh at the start of a billing period: summed across all energy ledgers, and within each ledger the sum of its ADDITIVE rate components plus the LOWEST rung of its block tier ladder, if it has one.
A ledger is used for two different things in practice. A block tier ladder is mutually exclusive, so only the marginal rung counts; an additive rate stack (a delivery ledger carrying transmission, distribution, public purpose and the rest) is cumulative, so all of it counts. urpx:serviceTierNumber is what separates them: a numbered tier belongs to a ladder, a bare one is a component. URPX has no vocabulary saying which a ledger is, so this is the signal available rather than the signal designed, and a ledger mixing both contributes its components plus the lowest rung of that ladder.
A ledger is treated as energy iff its referenced MetricInput's urpx:measurementKind is urpx:energy. Per-meter, per-customer, per-day, or per-month service charges (urpx:profileMetricInput, or non-energy measurementKinds) are excluded — they have a different shape and don't belong in a $/kWh sum.
Useful for price signaling: when you don't know a customer's usage but want a single $/kWh number, this is the marginal rate at zero baseline usage.
REFUSES, rather than returning a number, in six cases, each carrying a
named :reason in its ex-data so a sweeping consumer can classify:
:no-energy-ledger this plan prices nothing per kWh, so the question does not apply to it. 0M would be indistinguishable from a plan whose energy ledgers genuinely sum to zero :unvaluable-marginal-tier a marginal tier could not be valued. A nil unit price reads to a consumer as free, and for a discount that is the worst available answer :unselected-alternatives an energy ledger carries mutually-exclusive price alternatives with no caller selection, AND either at least one of the withheld prices would have entered this sum, or nothing is recorded about what was withheld. Withholding a price the sum would not have taken costs the caller nothing and does not refuse; not KNOWING what was withheld is not evidence that it did not matter, so that refuses :condition-gated-tier a price in the sum is gated by a urpx:ConditionExpression this library does not evaluate, so including it would report an assumption as a number. Pass :assume-conditions-hold? true for the every-condition-true upper bound :all-energy-tiers-excluded every energy tier was event-driven, recurring, or both. Free is not the answer either :outside-version-window the version used does not describe this instant
The alternatives case is why composing a PG&E plan with its vintage-PCIA
modifier needs :profile-alternatives. Taking the first of eighteen mutually
exclusive prices added an arbitrary 0.02973 to the rate; the eighteen span
0.06391/kWh, so there is no default worth inventing.
EVENT-DRIVEN PRICES ARE EXCLUDED BY DEFAULT. A Price carrying
urpx:hasDynamicPricing applies only while its event is called: a critical
peak charge is real but exceptional, capped around 50 hours a summer against
roughly 2,200 summer hours. Summing it unconditionally answered what would this cost during an event', which read as the ordinary rate. The default is now the non-event baseline, which is what a price signal means bythe next
kWh'. Unlike the alternatives above there IS a defensible default here,
because no-event-today is the ordinary state rather than an arbitrary pick.
Pass {:include-dynamic-pricing? true} for the during-event upper bound.
Either way :urpx.resolved/dynamic-pricing on the tier says which prices
are event-driven, so a caller can compute both.
The marginal $/kWh at the start of a billing period: summed across all
**energy** ledgers, and within each ledger the sum of its ADDITIVE rate
components plus the LOWEST rung of its block tier ladder, if it has one.
A ledger is used for two different things in practice. A block tier ladder
is mutually exclusive, so only the marginal rung counts; an additive rate
stack (a delivery ledger carrying transmission, distribution, public purpose
and the rest) is cumulative, so all of it counts. urpx:serviceTierNumber is
what separates them: a numbered tier belongs to a ladder, a bare one is a
component. URPX has no vocabulary saying which a ledger is, so this is the
signal available rather than the signal designed, and a ledger mixing both
contributes its components plus the lowest rung of that ladder.
A ledger is treated as energy iff its referenced MetricInput's
urpx:measurementKind is urpx:energy. Per-meter, per-customer, per-day, or
per-month service charges (urpx:profileMetricInput, or non-energy
measurementKinds) are excluded — they have a different shape and don't
belong in a $/kWh sum.
Useful for price signaling: when you don't know a customer's usage but want
a single $/kWh number, this is the marginal rate at zero baseline usage.
REFUSES, rather than returning a number, in six cases, each carrying a
named `:reason` in its ex-data so a sweeping consumer can classify:
:no-energy-ledger this plan prices nothing per kWh, so the
question does not apply to it. 0M would be
indistinguishable from a plan whose energy
ledgers genuinely sum to zero
:unvaluable-marginal-tier a marginal tier could not be valued. A nil
unit price reads to a consumer as free, and
for a discount that is the worst available
answer
:unselected-alternatives an energy ledger carries mutually-exclusive
price alternatives with no caller selection,
AND either at least one of the withheld prices
would have entered this sum, or nothing is
recorded about what was withheld. Withholding
a price the sum would not have taken costs the
caller nothing and does not refuse; not
KNOWING what was withheld is not evidence that
it did not matter, so that refuses
:condition-gated-tier a price in the sum is gated by a
urpx:ConditionExpression this library does not
evaluate, so including it would report an
assumption as a number. Pass
:assume-conditions-hold? true for the
every-condition-true upper bound
:all-energy-tiers-excluded every energy tier was event-driven, recurring,
or both. Free is not the answer either
:outside-version-window the version used does not describe this
instant
The alternatives case is why composing a PG&E plan with its vintage-PCIA
modifier needs `:profile-alternatives`. Taking the first of eighteen mutually
exclusive prices added an arbitrary 0.02973 to the rate; the eighteen span
0.06391/kWh, so there is no default worth inventing.
EVENT-DRIVEN PRICES ARE EXCLUDED BY DEFAULT. A Price carrying
urpx:hasDynamicPricing applies only while its event is called: a critical
peak charge is real but exceptional, capped around 50 hours a summer against
roughly 2,200 summer hours. Summing it unconditionally answered `what would
this cost during an event', which read as the ordinary rate. The default is
now the non-event baseline, which is what a price signal means by `the next
kWh'. Unlike the alternatives above there IS a defensible default here,
because no-event-today is the ordinary state rather than an arbitrary pick.
Pass `{:include-dynamic-pricing? true}` for the during-event upper bound.
Either way `:urpx.resolved/dynamic-pricing` on the tier says which prices
are event-driven, so a caller can compute both.(measurement-kinds resolved)The set of urpx:measurementKind @ids present across resolved's ledgers.
What a caller usually wants to know, having found that a plan prices nothing per kWh, is what it DOES price in, so this answers with the kinds rather than a bare yes or no and lets a sweeping consumer route instead of merely skip.
May be empty. CPAU E-14 is the case: a street lighting schedule whose charges are all per-lamp-per-month and per-fixture-per-month, filed as recurring charges with their own CalculationMethod, which price resolution excludes. Nothing resolves, so nothing is reported, and an empty set says exactly that.
The set of urpx:measurementKind @ids present across `resolved`'s ledgers. What a caller usually wants to know, having found that a plan prices nothing per kWh, is what it DOES price in, so this answers with the kinds rather than a bare yes or no and lets a sweeping consumer route instead of merely skip. May be empty. CPAU E-14 is the case: a street lighting schedule whose charges are all per-lamp-per-month and per-fixture-per-month, filed as recurring charges with their own CalculationMethod, which price resolution excludes. Nothing resolves, so nothing is reported, and an empty set says exactly that.
(plan-zone doc)The ZoneId a plan or modifier declares as urpx:timezoneIdentifier, or nil.
THE ZONE EVERY BOUND IN A URPX DOCUMENT IS WRITTEN IN. urpx:effectiveDateTime, urpx:expirationDateTime and every TOU bracket are naive wall-clock readings: measured across the production corpus, all 89 carry no offset. They are wall-clock IN THIS ZONE, so reading them in any other is reading a different moment.
Present on all 72 production filings, on the urpx:RatePlan in 39 and the urpx:RatePlanModifier in 41, so both halves of a composition can be zoned.
The ZoneId a plan or modifier declares as urpx:timezoneIdentifier, or nil. THE ZONE EVERY BOUND IN A URPX DOCUMENT IS WRITTEN IN. urpx:effectiveDateTime, urpx:expirationDateTime and every TOU bracket are naive wall-clock readings: measured across the production corpus, all 89 carry no offset. They are wall-clock IN THIS ZONE, so reading them in any other is reading a different moment. Present on all 72 production filings, on the urpx:RatePlan in 39 and the urpx:RatePlanModifier in 41, so both halves of a composition can be zoned.
(require-zone doc)The plan's declared zone, or REFUSE.
A document that states no zone states no moment: its bounds are wall-clock readings with nothing to anchor them, so whether an instant falls inside one has no answer. Falling back to the caller's zone is what this replaces, and it silently made the answer depend on how the caller spelled the instant.
The plan's declared zone, or REFUSE. A document that states no zone states no moment: its bounds are wall-clock readings with nothing to anchor them, so whether an instant falls inside one has no answer. Falling back to the caller's zone is what this replaces, and it silently made the answer depend on how the caller spelled the instant.
(resolve-modifier-prices modifier zdt)Resolve every per-energy unit price that modifier (a coerced
urpx:RatePlanModifier) contributes at zdt.
Returns the same shape as resolve-prices minus the season/TOU keys
(modifier ledgers are flat add-ons): {:urpx.resolved/ledgers [...]}.
When the modifier carries its own urpx:hasTOUSchedule, PDs with :urpx/touPeriodNumber are gated against the modifier's resolved period. Without an own TOUSchedule and no base context, every applicable Price is surfaced — matrix-style modifiers (e.g. CPAU E-HRA — 4x4 reserve level x generation tier driven by external state) rely on this so the caller can select the active value, since the URPX condition vocabulary (operator + label conditionValue) is not yet specified in a way that's programmatically interpretable. Filter the returned :tiers vector on :urpx.resolved/price-id when needed.
Resolve every per-energy unit price that `modifier` (a coerced
urpx:RatePlanModifier) contributes at `zdt`.
Returns the same shape as `resolve-prices` minus the season/TOU keys
(modifier ledgers are flat add-ons): {:urpx.resolved/ledgers [...]}.
When the modifier carries its own urpx:hasTOUSchedule, PDs with
:urpx/touPeriodNumber are gated against the modifier's resolved period.
Without an own TOUSchedule and no base context, every applicable Price
is surfaced — matrix-style modifiers (e.g. CPAU E-HRA — 4x4 reserve
level x generation tier driven by external state) rely on this so the
caller can select the active value, since the URPX condition
vocabulary (operator + label conditionValue) is not yet specified in a
way that's programmatically interpretable. Filter the returned :tiers
vector on :urpx.resolved/price-id when needed.(resolve-prices rate-plan zdt)(resolve-prices rate-plan zdt opts)Resolve all per-energy unit prices that apply to rate-plan at zdt
(a java.time.ZonedDateTime in the rate plan's local timezone).
Optional opts map:
:holiday? predicate (LocalDate -> bool) used to evaluate a bracket's
urpx:includeHolidays / urpx:includeNonHolidays gates. A plain
set of LocalDates works (sets are functions). When omitted,
a predicate is derived from the plan's embedded
urpx:HolidayCalendar via urpx.holidays/derive-predicate;
if neither is available, every date is treated as a
non-holiday — so brackets that opt out of non-holidays
(urpx:includeNonHolidays = false, e.g. holiday-only off-peak
overrides) will never match.
:profile-alternatives a {<ProfileAlternatives @id> <ProfileAlternative @id>} map driving customer-profile-driven price selection, keyed by the ProfileAlternatives CONTAINER. It scopes both Prices.hasPriceSetAlternatives (income-tier CARE/FERA/Standard, vintage-PCIA departure year, voltage configuration) and PriceDefinition.referencesProfileAlternative.
A dimension the caller says nothing about is NOT skipped: its
prices are withheld, the ledger carries
:urpx.resolved/unselected-alternatives, and marginal-unit-rate
refuses if withholding them would make its sum short. A value
the keyed container does not list is refused outright, since
compared for equality a stale id is indistinguishable from a
deliberate choice of some other cell.
Programmatic ConditionExpression evaluation against a customer
profile is a future enhancement; a consumer names the cell.
Examples of :holiday?:
;; Most rate plans embed urpx:HolidayCalendar — no opts needed: (resolve-prices plan zdt)
;; Static set — overrides any embedded calendar: (def us-federal-2026 #{(LocalDate/of 2026 1 1) ; New Year's Day (LocalDate/of 2026 1 19) ; Martin Luther King Jr. Day (LocalDate/of 2026 2 16) ; Presidents Day (LocalDate/of 2026 5 25) ; Memorial Day (LocalDate/of 2026 6 19) ; Juneteenth (LocalDate/of 2026 7 3) ; Independence Day (observed) (LocalDate/of 2026 9 7) ; Labor Day (LocalDate/of 2026 11 11) ; Veterans Day (LocalDate/of 2026 11 26) ; Thanksgiving (LocalDate/of 2026 12 25)}) ; Christmas (resolve-prices plan zdt {:holiday? us-federal-2026})
;; Composed predicate — if you maintain a calendar elsewhere: (resolve-prices plan zdt {:holiday? (fn [d] (calendar/observed? :us-federal d))})
Returns: {:urpx.resolved/season-name String or nil :urpx.resolved/tou-period-name String or nil :urpx.resolved/tou-period-number int or nil :urpx.resolved/ledgers [LedgerResult]}
where each LedgerResult is: {:urpx.resolved/ledger-id String :urpx.resolved/ledger-name String :urpx.resolved/ledger-type Ref :urpx.resolved/metric-input-id String (when the ledger references one) :urpx.resolved/metric-input-type Ref (e.g. urpx:measuredMetricInput) :urpx.resolved/measurement-kind Ref (e.g. urpx:energy — present when the MetricInput carries one; marginal-unit-rate filters on this) :urpx.resolved/unit-of-measure Ref (e.g. urpx:kWh) :urpx.resolved/tiers [TierResult] :urpx.resolved/unselected-alternatives [String] (present iff prices on this ledger belong to alternatives the caller chose none of) :urpx.resolved/withheld-prices [WithheldPrice] (what those alternatives would have priced, enough of it to say whether a marginal sum is actually short)}
and each TierResult is: {:urpx.resolved/price-definition-id String :urpx.resolved/price-id String :urpx.resolved/price-name String :urpx.resolved/unit-price BigDecimal :urpx.resolved/tier-number int (only on block-tiered ledgers) :urpx.resolved/tier-name String :urpx.resolved/tier-lower-bound BigDecimal :urpx.resolved/tier-upper-bound BigDecimal :urpx.resolved/tier-lower-bound-operator Ref :urpx.resolved/tier-upper-bound-operator Ref :urpx.resolved/dynamic-pricing Map (present iff the source Price carries urpx:hasDynamicPricing — a CPP Event Charge, RTP price, or other event-driven component; consumers that need a non-event marginal rate filter on the absence of this key) :urpx.resolved/condition-gated [Gate] (present iff the source Price or its PriceDefinition carries a urpx:ConditionExpression this library does not evaluate, so the price fired on an ASSUMPTION that its predicate holds. Each Gate carries :gate-id, :gate-name and :gate-on, the last being :price or :price-definition. Not excluded from any aggregate: see marginal-unit-rate)}
For non-tiered ledgers, :tiers has a single entry with no tier-* keys. For block-tiered ledgers (e.g. CPAU E-1), :tiers carries every tier sorted by tier-number — the caller chooses based on cumulative billing-period usage.
Resolve all per-energy unit prices that apply to `rate-plan` at `zdt`
(a java.time.ZonedDateTime in the rate plan's local timezone).
Optional `opts` map:
:holiday? predicate `(LocalDate -> bool)` used to evaluate a bracket's
urpx:includeHolidays / urpx:includeNonHolidays gates. A plain
set of LocalDates works (sets are functions). When omitted,
a predicate is derived from the plan's embedded
urpx:HolidayCalendar via `urpx.holidays/derive-predicate`;
if neither is available, every date is treated as a
non-holiday — so brackets that opt out of non-holidays
(urpx:includeNonHolidays = false, e.g. holiday-only off-peak
overrides) will never match.
:profile-alternatives a {<ProfileAlternatives @id> <ProfileAlternative @id>}
map driving customer-profile-driven price selection, keyed by
the ProfileAlternatives CONTAINER. It scopes both
Prices.hasPriceSetAlternatives (income-tier CARE/FERA/Standard,
vintage-PCIA departure year, voltage configuration) and
PriceDefinition.referencesProfileAlternative.
A dimension the caller says nothing about is NOT skipped: its
prices are withheld, the ledger carries
:urpx.resolved/unselected-alternatives, and marginal-unit-rate
refuses if withholding them would make its sum short. A value
the keyed container does not list is refused outright, since
compared for equality a stale id is indistinguishable from a
deliberate choice of some other cell.
Programmatic ConditionExpression evaluation against a customer
profile is a future enhancement; a consumer names the cell.
Examples of `:holiday?`:
;; Most rate plans embed urpx:HolidayCalendar — no opts needed:
(resolve-prices plan zdt)
;; Static set — overrides any embedded calendar:
(def us-federal-2026
#{(LocalDate/of 2026 1 1) ; New Year's Day
(LocalDate/of 2026 1 19) ; Martin Luther King Jr. Day
(LocalDate/of 2026 2 16) ; Presidents Day
(LocalDate/of 2026 5 25) ; Memorial Day
(LocalDate/of 2026 6 19) ; Juneteenth
(LocalDate/of 2026 7 3) ; Independence Day (observed)
(LocalDate/of 2026 9 7) ; Labor Day
(LocalDate/of 2026 11 11) ; Veterans Day
(LocalDate/of 2026 11 26) ; Thanksgiving
(LocalDate/of 2026 12 25)}) ; Christmas
(resolve-prices plan zdt {:holiday? us-federal-2026})
;; Composed predicate — if you maintain a calendar elsewhere:
(resolve-prices plan zdt {:holiday? (fn [d] (calendar/observed? :us-federal d))})
Returns:
{:urpx.resolved/season-name String or nil
:urpx.resolved/tou-period-name String or nil
:urpx.resolved/tou-period-number int or nil
:urpx.resolved/ledgers [LedgerResult]}
where each LedgerResult is:
{:urpx.resolved/ledger-id String
:urpx.resolved/ledger-name String
:urpx.resolved/ledger-type Ref
:urpx.resolved/metric-input-id String (when the ledger references one)
:urpx.resolved/metric-input-type Ref (e.g. urpx:measuredMetricInput)
:urpx.resolved/measurement-kind Ref (e.g. urpx:energy — present when
the MetricInput carries one;
marginal-unit-rate filters on this)
:urpx.resolved/unit-of-measure Ref (e.g. urpx:kWh)
:urpx.resolved/tiers [TierResult]
:urpx.resolved/unselected-alternatives [String]
(present iff prices on this
ledger belong to alternatives
the caller chose none of)
:urpx.resolved/withheld-prices [WithheldPrice]
(what those alternatives would
have priced, enough of it to
say whether a marginal sum is
actually short)}
and each TierResult is:
{:urpx.resolved/price-definition-id String
:urpx.resolved/price-id String
:urpx.resolved/price-name String
:urpx.resolved/unit-price BigDecimal
:urpx.resolved/tier-number int (only on block-tiered ledgers)
:urpx.resolved/tier-name String
:urpx.resolved/tier-lower-bound BigDecimal
:urpx.resolved/tier-upper-bound BigDecimal
:urpx.resolved/tier-lower-bound-operator Ref
:urpx.resolved/tier-upper-bound-operator Ref
:urpx.resolved/dynamic-pricing Map (present iff the source Price
carries urpx:hasDynamicPricing
— a CPP Event Charge, RTP
price, or other event-driven
component; consumers that
need a non-event marginal
rate filter on the absence
of this key)
:urpx.resolved/condition-gated [Gate] (present iff the source Price
or its PriceDefinition
carries a
urpx:ConditionExpression
this library does not
evaluate, so the price fired
on an ASSUMPTION that its
predicate holds. Each Gate
carries :gate-id, :gate-name
and :gate-on, the last being
:price or :price-definition.
Not excluded from any
aggregate: see
marginal-unit-rate)}
For non-tiered ledgers, :tiers has a single entry with no tier-* keys.
For block-tiered ledgers (e.g. CPAU E-1), :tiers carries every tier sorted
by tier-number — the caller chooses based on cumulative billing-period usage.(resolve-prices-with-modifiers rate-plan modifiers zdt)(resolve-prices-with-modifiers rate-plan modifiers zdt opts)Resolve prices for rate-plan at zdt and append ledger results for
each modifier in modifiers whose :urpx/applicableToRatePlan list
includes the base plan's @id. Modifiers whose target lists don't include
the plan are silently skipped.
Returns the resolve-prices shape with :urpx.resolved/ledgers extended
by the modifier ledgers.
Resolve prices for `rate-plan` at `zdt` and append ledger results for each modifier in `modifiers` whose :urpx/applicableToRatePlan list includes the base plan's @id. Modifiers whose target lists don't include the plan are silently skipped. Returns the `resolve-prices` shape with :urpx.resolved/ledgers extended by the modifier ledgers.
(version-covers? version t zone)True if version describes the instant t: in effect by then, and not
expired.
t is a ZonedDateTime and the document's naive bounds are anchored in zone
first, so the comparison is between MOMENTS. Comparing wall clocks instead
answers a different question, and was how the same instant asked in two zones
resolved differently: an LA-midnight expiry covered 2025-12-31T20:00-08:00
and refused the identical 2026-01-01T04:00Z.
Public as the single reader of a version window, so a caller asking whether a snapshot is current gets the same answer this namespace uses to resolve it.
A version stating no effective instant covers everything. It cannot be ordered against anything and nothing says it has not started, so refusing it would reject a conformant document over a case the vocabulary permits.
True if `version` describes the instant `t`: in effect by then, and not expired. `t` is a ZonedDateTime and the document's naive bounds are anchored in `zone` first, so the comparison is between MOMENTS. Comparing wall clocks instead answers a different question, and was how the same instant asked in two zones resolved differently: an LA-midnight expiry covered 2025-12-31T20:00-08:00 and refused the identical 2026-01-01T04:00Z. Public as the single reader of a version window, so a caller asking whether a snapshot is current gets the same answer this namespace uses to resolve it. A version stating no effective instant covers everything. It cannot be ordered against anything and nothing says it has not started, so refusing it would reject a conformant document over a case the vocabulary permits.
(version-effective-at version zone)The instant version takes effect, as a ZonedDateTime in zone, or nil if
it does not say.
ZONE REQUIRED, NOT OPTIONAL. The document states a naive wall-clock reading; it names a moment only once anchored, and every zone anchors it to a different one. An optional zone would make the unzoned call the easy one and the wrong one, which is how this namespace came to compare a caller's wall clock against a publisher's. urpx:effectiveDateTime is preferred; urpx:effectiveDate is read as the start of that day. Both are sh:minCount 0, so a conformant version may state neither, and this returns nil rather than inventing an epoch for it.
Public as THE SINGLE READER of a version's effective instant. urpx.arrangements re-derived this and got it subtly wrong, requiring urpx:effectiveDateTime and so silently dropping a conformant version that states only urpx:effectiveDate. Two namespaces disagreeing about the same date is the drift this prevents.
The instant `version` takes effect, as a ZonedDateTime in `zone`, or nil if it does not say. ZONE REQUIRED, NOT OPTIONAL. The document states a naive wall-clock reading; it names a moment only once anchored, and every zone anchors it to a different one. An optional zone would make the unzoned call the easy one and the wrong one, which is how this namespace came to compare a caller's wall clock against a publisher's. urpx:effectiveDateTime is preferred; urpx:effectiveDate is read as the start of that day. Both are sh:minCount 0, so a conformant version may state neither, and this returns nil rather than inventing an epoch for it. Public as THE SINGLE READER of a version's effective instant. urpx.arrangements re-derived this and got it subtly wrong, requiring urpx:effectiveDateTime and so silently dropping a conformant version that states only urpx:effectiveDate. Two namespaces disagreeing about the same date is the drift this prevents.
(version-expired-by? version t zone)True if version states a hard expiration at or before t.
t is a ZonedDateTime and the bound is anchored in zone before comparing,
so the answer is about one MOMENT rather than about two wall clocks that may
be in different places.
Public alongside version-effective-at, and for the same reason: it is the
single reader of a version's end bound.
urpx:expirationDate / urpx:expirationDateTime only. The version shapes also carry urpx:expectedEndDate and urpx:expectedEndDateTime, which are deliberately NOT read here: an expected end anticipates a successor rather than terminating anything, so a version past its expected end is still the one in force until a later version exists. Treating the two alike would silently drop a plan that outlived its own forecast.
True if `version` states a hard expiration at or before `t`. `t` is a ZonedDateTime and the bound is anchored in `zone` before comparing, so the answer is about one MOMENT rather than about two wall clocks that may be in different places. Public alongside `version-effective-at`, and for the same reason: it is the single reader of a version's end bound. urpx:expirationDate / urpx:expirationDateTime only. The version shapes also carry urpx:expectedEndDate and urpx:expectedEndDateTime, which are deliberately NOT read here: an expected end anticipates a successor rather than terminating anything, so a version past its expected end is still the one in force until a later version exists. Treating the two alike would silently drop a plan that outlived its own forecast.
(version-expires-at version zone)The instant version stops applying, as a ZonedDateTime in zone, or nil.
Named rather than inlined because it was re-derived at three call sites and the resolved marks emitted it as a naive LocalDateTime while the comparison used something else. One reader, one type.
The instant `version` stops applying, as a ZonedDateTime in `zone`, or nil. Named rather than inlined because it was re-derived at three call sites and the resolved marks emitted it as a naive LocalDateTime while the comparison used something else. One reader, one type.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |