docs← Back to article

Markdown for LLMs

Time: effective, legal time, duration, editions

The source Markdown for this article. Copy it into your assistant or download it as a text file.

Download this articlePlain text ↗
# Time: effective, legal time, duration, editions

**In one sentence:** time answers the question "as of which date the case is
computed": `effective` switches rules on and off by legal time
(`legal_time`), `days_between` measures duration in days, and an
edition anchor (`@source` + `in_force`) dates a node by the act's
applicability. Take them when a norm holds "from … to …", a period counts
in days, or an article enters into force later than the case.

Legal time switches norms on and off, durations are measured in days, and edition anchors date nodes by the act's applicability.

## 1. When to take it and when not to

| Instead of | Selection rule |
|---|---|
| Rule `effective` vs target `window` | "Norm holds from 1 March" — `effective [@2026-03-01, infinity);` on the rule: outside the window there is no position at all. "Perform by 31 March" — target `window`: the position exists (`PENDING`/`UNDETERMINED`), the deadline has/has not passed. Different answers: "no norm yet" vs "period not started yet" |
| `effective` vs edition anchor | The rule's own commencement date — `effective`. The act's commencement date — edition lifecycle (`in_force`), to whose fragment `@source` leads: the node is dated by the anchor. A rule without `effective` but anchored to a future edition does not participate at an early date |
| `days_between` vs date addition | Duration is only measured: `days_between(due, paid) >= 30`. `due + 30 days` — refusal `LDC-E2108` (a date does not add a magnitude); `days_between(due, paid) >= 30 days` — the same refusal (`Quantity` vs `Integer`). A period is built by the period step (`add_calendar_period`) |
| `Date` vs `Instant` | Civil date (`@2026-03-01`) — to a date; instant with offset (`@2026-03-01T09:00:00+05:00`) — to an instant. Mixing: statically `LDC-E2108`, in an execution term — `TYPE_ERROR`. Instants compare by moment, not by spelling |
| Fact-date comparison in the body vs `governs` | Retroactivity in an act whose cases are written on the surface — by comparing the fact date in the body (so does the corpus). `governs event_time …` executes in the engine, but the surface does not record the axis onto a fact (`assert … { valid …; }` writes only `valid_time`), and a `.lawtest` case will never support such a rule |

## Grammar: time and dating (EBNF verbatim from the grammar)

Time literals — dates, instants, their bodies, and the offset (where text and grammar differ, the grammar is canonical):

```ebnf
temporal_literal        = date_literal | time_literal | datetime_literal ;
date_literal            = "@", digit, digit, digit, digit, "-", digit, digit, "-", digit, digit ;
time_literal            = "@", digit, digit, ":", digit, digit, ":", digit, digit,
                          [ ".", digit, { digit } ] ;
datetime_literal        = date_literal_body, "T", time_literal_body,
                          ( "Z" | offset ) ;
date_literal_body       = "@", digit, digit, digit, digit, "-", digit, digit, "-", digit, digit ;
time_literal_body       = digit, digit, ":", digit, digit, ":", digit, digit,
                          [ ".", digit, { digit } ] ;
offset                  = ( "+" | "-" ), digit, digit, ":", digit, digit ;
```

Intervals — brackets and endpoints (`infinity`/`-infinity` are legal):

```ebnf
interval_expression     = expression ;
interval_literal        = interval_left, interval_endpoint, ",",
                          interval_endpoint, interval_right ;
interval_left           = "[" | "(" ;
interval_right          = "]" | ")" ;
interval_endpoint       = expression | "infinity" | "-infinity" ;
```

Applicability qualifiers — the rule validity window and the regulated-event window:

```ebnf
effective_clause        = "effective", interval_literal, ";" ;
governs_clause          = "governs", identifier, interval_literal, ";" ;
```

Edition lifecycle events — dating nodes through the anchor:

```ebnf
lifecycle_item          = "adopted", temporal_literal, ";"
                        | "published", temporal_literal, ";"
                        | "in_force", interval_literal, ";"
                        | "suspended", interval_literal, ";"
                        | "restored", temporal_literal, ";"
                        | "repealed", temporal_literal, ";"
                        | "expired", temporal_literal, ";"
                        | "invalidated", temporal_literal, ";" ;
```
## 2. Minimal example

Package `research.constructs.time_effective`: a liability regime in force since
2026. Full code — the example package.

```law
rule PenaltyRegime2026 strict {
    effective [@2026-01-01, infinity);
    for p: Payer;
    when registered_payer(p);
    then penalty_regime_applies(p);
}
```

Case facts: `registered_payer(alpha)`. Query:
`evaluate truth(penalty_regime_applies(alpha))`.

Observed engine answer (installed `law`, semantics `law.core/0.2`):

```text
law test research.constructs.time_effective: мир research.constructs.time_effective
  ok   [research.constructs.time_effective#authored] tests/01-regime-active.lawtest / urn:query:research-time-01
  ok   [research.constructs.time_effective#authored] tests/02-regime-inapplicable.lawtest / urn:query:research-time-02
итого: 2 проверено, 2 прошли, 0 не прошли, 0 не исполнены; код 0
```

`law engine check` — `check OK`, no warnings. Sensitivity:
changing `legal_time` from `@2026-06-01` to `@2025-06-01` with the same facts
changes the answer from `TRUE_ONLY` to `NEITHER` — the rule does not participate
(`NOT_APPLICABLE`), not "condition false"; removing the
`registered_payer` fact at a date inside the window gives the same `NEITHER` by
another route (body silence). The example is not vacuous.

Nearest wrong outcome: cutting the `effective` clause — the 2025 case
answers `TRUE_ONLY` instead of `NEITHER` (counterfactual: the window decides).

## 3. Example by domain

- **Law:** finite disability-benefit window (package `kz.corpus.socialcode`, Kazakhstan) —
  three rules with `effective [@2023-07-01, @2027-01-01)`: a window with an end,
  not just a start (see `corpus-forms.md`).
- **Law:** "no earlier than N days" as a guard (package `tr.constitution`, Türkiye) —
  `days_between(submitted, year_start) >= 75` and the mirrored `< 75`:
  a pair of thresholds on one measure.
- **Standard/protocol:** timely attestation (package `us.code.apportionment`, United States) —
  `days_between(receipt, sent) <= 15`: "within fifteen
  calendar days" as a measured-duration comparison.
- **Law:** period by calendar step (package `intl.vienna.succession_treaties`, international) —
  `during [appointed, add_calendar_period(appointed, 60 calendar_day)]`:
  months and years are not measured but built by the period step.
- **Teaching case:** `research.constructs.time_overdue` — the "thirty or
  more days" threshold (`days_between(due, paid) >= 30`): 41 days — `TRUE_ONLY`,
  22 days — `NEITHER` (the rule stays silent, it does not refuse).
- **Law:** detention over four days (package `tr.constitution`, Türkiye) —
  `days_between(started, until) > 4` in a defeated rule with two
  `unless` provisos — delivery to a judge and extension under emergency/war:
  the same duration measure inside a more complex norm.

## 4. How the engine answers

Table — observed runs of this directory's examples (installed
`law`, semantics `law.core/0.2`):

| Legal time | Facts | Question | Answer | Why |
|---|---|---|---|---|
| `@2026-06-01` | `registered_payer` | `penalty_regime_applies` | `TRUE_ONLY` | `effective` window contains the legal time |
| `@2025-06-01` | same | same | `NEITHER` | `effective` starts `@2026-01-01` — rule does not participate |
| `@2026-03-01` | due 10.01, paid 20.02 | `overdue_30` | `TRUE_ONLY` | `days_between` = 41 ≥ 30 |
| `@2026-03-01` | due 10.01, paid 01.02 | `overdue_30` | `NEITHER` | 22 < 30 — silent, not refusing |

- Four context axes: `legal_time` (the law in force),
  `decision_time` (result moment), `knowledge_time` (data boundary),
  `timezone`. No axis is taken from the system clock.
- A window without `legal_time` gives the rule `NOT_APPLICABLE`: answer `NEITHER`,
  `why_not` names the candidate with this status.
- A node with anchors is excluded when ALL its dating editions are
  inapplicable at `legal_time`: the document carries issue
  `EDITION_NOT_APPLICABLE` (info), `why_not` — `NOT_APPLICABLE`.
  An edition with only `adopted`/`published` or `timeless` is outside
  dating: the node is excluded on no date.
- Duration: `days_between` gives an `Integer` of calendar days,
  negative on reversed order; hours and minutes —
  `hours_between`, `minutes_between` over `Instant`.

Documented outcomes (earlier scenarios):

| Law date | Facts | Question | Answer | Why |
|---|---|---|---|---|
| `@2025-06-01` | two memberships, one licence | `licensed(acme, trade)` | `FALSE_ONLY` | closure in-window — explicit negative |
| `@2026-03-01` | same | same | `NEITHER` | closure window closed |
| `@2026-03-01` | same, closure `effective` cut out | same | `FALSE_ONLY` | counterfactual: the window decides |
| `@2026-03-01` | `debt_settled` | `penalty_waived` | `NEITHER` + `EDITION_NOT_APPLICABLE` | the 2027 edition is `not_yet` |
| `@2027-03-01` | same | same | `TRUE_ONLY` | edition in force |
| `@2026-03-01` | same, `@source` anchor cut out | same | `TRUE_ONLY` | counterfactual: the anchor dates the node |
| — | `days_between(@2026-02-20, @2026-01-10)` | DATA | `-41` | sign follows argument order |
| — | `days_between(@2026-01-10, @2026-02-20T00:00:00Z)` | DATA | `TYPE_ERROR` | `Date` versus `Instant` |
| `@2026-03-01` | filing `2025-12-31T23:30:00Z` | `filed_before_2026` | `NEITHER` | by moment — 04:30 1 January Almaty |
| `@2026-03-01` | filing `2025-12-31T18:00:00Z` | `filed_before_2026` | `TRUE_ONLY` | 23:00 31 December Almaty |

## 5. Common mistakes

1. Rule silent on every date: check `legal_time` against `effective` and
   against the `in_force` of the edition `@source` leads to
   (`pitfalls.md`, item 1).
2. `Date` vs `Instant`: `check` — `LDC-E2108`, execution —
   `TYPE_ERROR` (`pitfalls.md`, item 2).
3. "30 days" as a magnitude: `days_between(…) >= 30 days` and `due + 30
   days` — `LDC-E2108` (`pitfalls.md`, item 3).
4. Name in a window bound: `effective [start, infinity)` with an undeclared
   name — `LDC-E1305`; a bare name in a target window — `LDC-E2115`
   (`pitfalls.md`, item 4).
5. `timeless` together with lifecycle — `LDC-E1305`: two incompatible models of
   one edition (`pitfalls.md`, item 5).
6. `during`/`overlaps`/`before`/`after` as predicates — `LDC-E2102`
   (RESERVED); time conditions are written as date
   comparisons (`pitfalls.md`, item 6).
7. Term window bound without a period policy — `MISSING_POLICY`
   (`pitfalls.md`, item 7).

## 6. References

- Tutorials: [real article source](/tutorials/real-article-source/),
  [pinned edition](/tutorials/pinned-edition/), [closed world](/tutorials/closed-world/).
- Neighbouring pages: [sources](/constructs/sources/) (dating through the anchor), [negation and truth statuses](/constructs/negation-and-status/) (closure window), [expressions and quantities](/constructs/expressions-quantities/) (duration measurement), [strict rules](/constructs/rule-strict/) (rule validity window).