Replace dayjs-backed now in template execution with Temporal-compatible current time support
Summary
Accord Project templates currently receive a runtime now value during drafting and execution. In the current stack this appears to be backed by dayjs, via @accordproject/markdown-template and @accordproject/template-engine.
Now that TC39 Temporal is Stage 4, it may be worth introducing Temporal-compatible current time support in template-engine, either as a new template variable or as a long-term replacement path for the existing now.
Current Behavior
APAP and other consumers of the library call the template engine like this:
const templateArchiveProcessor = new TemplateArchiveProcessor(apTemplate);
const draftResult = await templateArchiveProcessor.draft(agreement.data, format, {});
TemplateArchiveProcessor already exposes deterministic current-time parameters:
draft(data, format, options, currentTime?)
trigger(data, request, state?, currentTime?, utcOffset?)
init(data, currentTime?, utcOffset?)
From the current dependency stack, now is injected downstream in @accordproject/markdown-template roughly as:
```ts
data.now = now ? now : dayjs();
with temporalNow derived from the existing currentTime parameter, preserving deterministic execution while giving template authors access to modern date/time semantics.
Motivation
- Temporal gives a more precise and standard model for contract/template time handling:
- exact instants via Temporal.Instant
- explicit time zones via Temporal.ZonedDateTime
- date-only and time-only values via Temporal.PlainDate, Temporal.PlainTime, etc.
- safer DST-aware arithmetic
- strict string parsing and serialisation
- no reliance on legacy Date behaviour
This is particularly relevant for legal agreement templates where “now”, effective dates, delivery windows, time zones, and duration arithmetic need deterministic and auditable semantics.
Compatibility Concern
This should probably not be a direct silent replacement of now.
Existing templates may rely on dayjs-style behaviour, for example:
{{% return now.toLocaleString() %}}
or other dayjs methods. Replacing now with a Temporal object would likely be breaking.
Possible Approach
Introduce Temporal support additively first:
temporalNow // Temporal.Instant or Temporal.ZonedDateTime
now // existing dayjs-compatible value
Potential options:
- Add
temporalNow alongside now.
- Allow engine options to choose the runtime time representation.
- Keep
now dayjs-compatible for the 2.x line.
- Consider migrating
now itself only in a future major version.
- Accept an injected current-time provider so runtimes can supply deterministic
Temporal.Instant / Temporal.ZonedDateTime values.
Open Questions
- Should
temporalNow be a Temporal.Instant, Temporal.ZonedDateTime, or both?
- Should templates default to UTC or receive an explicit IANA time zone?
- Should
currentTime remain an ISO string at the public API boundary?
- Should
utcOffset be replaced or complemented by an IANA time zone identifier?
- Should Temporal support require Node 26+, or should
@js-temporal/polyfill be used while Node 22/24 are still supported?
Suggested Direction
A non-breaking first step would be to expose a new Temporal-compatible value in template expressions while keeping the existing now behaviour unchanged.
For example:
{{% return temporalNow.toString() %}}
with temporalNow derived from the existing currentTime parameter, preserving deterministic execution while giving template authors access to modern date/time semantics.
Replace dayjs-backed
nowin template execution with Temporal-compatible current time supportSummary
Accord Project templates currently receive a runtime
nowvalue during drafting and execution. In the current stack this appears to be backed bydayjs, via@accordproject/markdown-templateand@accordproject/template-engine.Now that TC39 Temporal is Stage 4, it may be worth introducing Temporal-compatible current time support in
template-engine, either as a new template variable or as a long-term replacement path for the existingnow.Current Behavior
APAP and other consumers of the library call the template engine like this:
TemplateArchiveProcessor already exposes deterministic current-time parameters:
draft(data, format, options, currentTime?)
trigger(data, request, state?, currentTime?, utcOffset?)
init(data, currentTime?, utcOffset?)
with
temporalNowderived from the existingcurrentTimeparameter, preserving deterministic execution while giving template authors access to modern date/time semantics.Motivation
This is particularly relevant for legal agreement templates where “now”, effective dates, delivery windows, time zones, and duration arithmetic need deterministic and auditable semantics.
Compatibility Concern
This should probably not be a direct silent replacement of
now.Existing templates may rely on dayjs-style behaviour, for example:
{{% return now.toLocaleString() %}}or other dayjs methods. Replacing
nowwith a Temporal object would likely be breaking.Possible Approach
Introduce Temporal support additively first:
Potential options:
temporalNowalongsidenow.nowdayjs-compatible for the 2.x line.nowitself only in a future major version.Temporal.Instant/Temporal.ZonedDateTimevalues.Open Questions
temporalNowbe aTemporal.Instant,Temporal.ZonedDateTime, or both?currentTimeremain an ISO string at the public API boundary?utcOffsetbe replaced or complemented by an IANA time zone identifier?@js-temporal/polyfillbe used while Node 22/24 are still supported?Suggested Direction
A non-breaking first step would be to expose a new Temporal-compatible value in template expressions while keeping the existing
nowbehaviour unchanged.For example:
{{% return temporalNow.toString() %}}with
temporalNowderived from the existingcurrentTimeparameter, preserving deterministic execution while giving template authors access to modern date/time semantics.