Problem
@date is one-way: it parses a date string into a Unix timestamp (modules/std/src/helpers/date.ts). There is no helper going the other way, so a raw timestamp read from a contract can never be turned into something a human — or a model — can read.
This shows up most in the chat panel. Ask it "when does foo.eth expire" and it can fetch the expiry fine, but the answer comes back as a bare 1801234567, or as a calendar date the model computed in its head and got wrong. Same for vesting cliffs, GIVpower lock ends, stream end dates, governance deadlines — anything stored as a Unix timestamp.
3acf51c mitigated this by putting the current Unix timestamp in the chat system prompt, so the model can reason by subtraction instead of calendar arithmetic. That makes the arithmetic easier, but it is still the model doing it, and the result is not verifiable.
Proposal
Add a timestamp → string formatter to std, so the model (and users) can offload the conversion to the interpreter and read the answer out of a print, the same way balances already work:
load std
print @date:format(@date(now +30d))
# => "2026-08-30 14:02 UTC"
Two shapes worth weighing:
- A separate helper, e.g.
@date:format(timestamp format?). Keeps @date's return type (number) clean and its argument list untouched.
- An option on
@date, e.g. a --format flag. Fewer names to learn, but it makes the return type depend on an option, which the arg-def/codegen layer may not model well.
I lean towards the separate helper for the return-type reason.
Open questions:
- Output format. A fixed ISO-ish UTC string is the safe default and keeps docs examples deterministic; a
format argument (strftime-like, or a few named presets) is friendlier but adds surface.
- Relative durations.
@date:since / @date:until returning something like "about 7 months" would answer the "when does it expire" question directly, in one call. Could be a follow-up.
- Timezone. Formatting in UTC only is the deterministic choice — local time would make test fixtures and doc examples machine-dependent.
Notes
- Docs are generated from test docCases, so examples need to be self-contained and deterministic — feeding a fixed timestamp rather than
@date(now).
- Once this lands, the chat system prompt in
apps/evmcrispr-terminal/src/ai/useChatAgent.ts should point at it, so the model calls the helper instead of doing the arithmetic itself.
Problem
@dateis one-way: it parses a date string into a Unix timestamp (modules/std/src/helpers/date.ts). There is no helper going the other way, so a raw timestamp read from a contract can never be turned into something a human — or a model — can read.This shows up most in the chat panel. Ask it "when does
foo.ethexpire" and it can fetch the expiry fine, but the answer comes back as a bare1801234567, or as a calendar date the model computed in its head and got wrong. Same for vesting cliffs, GIVpower lock ends, stream end dates, governance deadlines — anything stored as a Unix timestamp.3acf51c mitigated this by putting the current Unix timestamp in the chat system prompt, so the model can reason by subtraction instead of calendar arithmetic. That makes the arithmetic easier, but it is still the model doing it, and the result is not verifiable.
Proposal
Add a timestamp → string formatter to
std, so the model (and users) can offload the conversion to the interpreter and read the answer out of aprint, the same way balances already work:Two shapes worth weighing:
@date:format(timestamp format?). Keeps@date's return type (number) clean and its argument list untouched.@date, e.g. a--formatflag. Fewer names to learn, but it makes the return type depend on an option, which the arg-def/codegen layer may not model well.I lean towards the separate helper for the return-type reason.
Open questions:
formatargument (strftime-like, or a few named presets) is friendlier but adds surface.@date:since/@date:untilreturning something like"about 7 months"would answer the "when does it expire" question directly, in one call. Could be a follow-up.Notes
@date(now).apps/evmcrispr-terminal/src/ai/useChatAgent.tsshould point at it, so the model calls the helper instead of doing the arithmetic itself.