a9script

Docs / Language & functions

a9script authoring pack — date & time functions

The date & time functions, with their arguments and a line of real script each. Called by bare name; nothing is imported. A call marked pauses does real work outside the script: the run is saved, and continues on the next line with the result.

formatDate(date, pattern) — immediate

Writes a date out in a pattern of your own, always in UTC. The tokens are YYYY, MM, DD, HH, mm and ss; every other character of the pattern is written as it stands.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value from new Date(...). A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • pattern (string) — The shape of the answer, e.g. DD.MM.YYYY or YYYY-MM-DD HH:mm:ss. Keep punctuation between the tokens: a word that happens to spell one carries it.

Answers: The formatted text.

log(formatDate(input.event.receivedAt, "YYYY-MM-DD HH:mm"));

addDays(date, days) — immediate

Moves a date forwards or backwards by whole days and answers the new moment, in UTC. The time of day is kept.

  • date (string | number | date) — An ISO date, a datetime that carries its timezone, milliseconds since 1970, or a date value — the same dates formatDate takes.
  • days (number) — How many days to move. A negative count moves back.

Answers: The moved date as an ISO string, e.g. 2026-07-21T00:00:00.000Z.

const due = addDays(input.event.receivedAt, 14);
log("payable by " + formatDate(due, "YYYY-MM-DD"));

dateDiff(from, to) — immediate

How many whole days lie between two dates, counted as UTC calendar days: positive when to is the later one, negative when it is the earlier.

  • from (string | number | date) — The date to count from — an ISO date, milliseconds since 1970, or a date value.
  • to (string | number | date) — The date to count to, in any of the same forms.

Answers: The number of days. Two moments on the same UTC day answer 0; two hours either side of midnight answer 1, because the question is which day each one falls on.

const age = dateDiff("2026-03-01", input.event.receivedAt);
log("the order is " + age + " days old");

now() — pauses

The current moment, in milliseconds. The same clock Date.now() reads, so the two can never disagree — and the reading is kept: a run that is saved and continued later still sees the moment it read.

Answers: Milliseconds since the start of 1970, as a number.

const startedAt = now();
log("started at " + formatDate(startedAt, "YYYY-MM-DD HH:mm:ss"));

today() — pauses

Today’s date in UTC. The day now() falls on, written out.

Answers: The date as YYYY-MM-DD.

log("the report covers " + today());

dateAdd(date, amount, unit, timeZone?) — pauses

Moves a moment forwards by whole units and answers the new moment. Months and years follow the calendar: one month after 31 January is 28 February, because there is no 31 February.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • amount (number) — How many units to move. A whole number; a negative one moves backwards.
  • unit (string) — Which unit to move by: year, month, week, day, hour, minute, second or millisecond — plurals work too.
  • timeZone (string, optional) — Whose calendar to walk, such as Europe/Berlin. Without it, UTC — where a day is always twenty-four hours. With it, a day is a day on that clock: 09:00 in Berlin plus one day is 09:00 the next morning even when the clocks moved overnight, while plus 24 hours is 10:00.

Answers: The new moment as ISO text, written in the timezone that was named — 2026-02-28T10:00:00.000Z when none was.

const due = dateAdd(input.event.receivedAt, 1, "month");
log("payable by " + dateFormat(due, "DD.MM.YYYY"));

const sameTimeTomorrow = dateAdd(input.event.receivedAt, 1, "day", "Europe/Berlin");
log("and again at " + dateFormat(sameTimeTomorrow, "HH:mm", "Europe/Berlin"));

dateSubtract(date, amount, unit, timeZone?) — pauses

Moves a moment backwards by whole units — dateAdd the other way. Note that the two do not always undo each other: one month after 31 January is 28 February, and one month before that is 28 January.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • amount (number) — How many units to move back. A whole number.
  • unit (string) — Which unit to move by: year, month, week, day, hour, minute, second or millisecond — plurals work too.
  • timeZone (string, optional) — Whose calendar to walk, such as Europe/Berlin. Without it, UTC — where a day is always twenty-four hours. With it, a day is a day on that clock: 09:00 in Berlin plus one day is 09:00 the next morning even when the clocks moved overnight, while plus 24 hours is 10:00.

Answers: The new moment as ISO text, written in the timezone that was named.

const since = dateSubtract(today(), 7, "days");
log("looking back to " + since);

dateDifference(from, to, unit, timeZone?) — pauses

How many WHOLE units lie between two moments, counted the same way dateAdd moves them — so adding the answer to from never passes to.

  • from (string | number | date) — The moment to count from. An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • to (string | number | date) — The moment to count to, in any of the same forms.
  • unit (string) — Which unit to count in: year, month, week, day, hour, minute, second or millisecond — plurals work too.
  • timeZone (string, optional) — Whose calendar to walk, such as Europe/Berlin. Without it, UTC — where a day is always twenty-four hours. With it, a day is a day on that clock: 09:00 in Berlin plus one day is 09:00 the next morning even when the clocks moved overnight, while plus 24 hours is 10:00.

Answers: The number of whole units, positive when to is the later moment and negative when it is the earlier one. Anything short of a whole unit is dropped: two moments twenty-three hours apart are 0 days apart. One thing to know about leap days: 29 February to 28 February the next year is 0 years, because the anniversary is 1 March. For calendar days — which day each moment falls on, rather than how much time passed — use dateDiff.

const age = dateDifference(input.event.receivedAt, now(), "day");
log("the order is " + age + " days old");

dateIsBefore(date, other, unit?, timeZone?) — pauses

Is the first moment earlier than the second?

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • other (string | number | date) — The moment to compare against.
  • unit (string, optional) — Compare at this granularity instead of to the millisecond — "day" asks whether the first falls on an earlier day, so two moments on one day answer false. One of year, month, week, day, hour, minute, second or millisecond — plurals work too.
  • timeZone (string, optional) — Whose day (or week, or month) it is — 23:30 UTC is already the next day in Tokyo. Only meaningful together with a unit, and refused without one. Defaults to UTC.

Answers: true or false.

if (dateIsBefore(input.event.receivedAt, today())) {
  log("this one arrived before today");
}

dateIsAfter(date, other, unit?, timeZone?) — pauses

Is the first moment later than the second?

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • other (string | number | date) — The moment to compare against.
  • unit (string, optional) — Compare at this granularity instead of to the millisecond. One of year, month, week, day, hour, minute, second or millisecond — plurals work too.
  • timeZone (string, optional) — Whose day (or week, or month) it is — 23:30 UTC is already the next day in Tokyo. Only meaningful together with a unit, and refused without one. Defaults to UTC.

Answers: true or false.

log(dateIsAfter("2026-07-14", "2026-07-13"));

dateIsEqual(date, other, unit?, timeZone?) — pauses

Are the two the same moment? With a unit, whether they fall in the same one — which is how to ask “were these on the same day” without comparing text.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • other (string | number | date) — The moment to compare against.
  • unit (string, optional) — Compare at this granularity: "day" answers true for any two moments on the same day — whose day it is depends on the timezone. One of year, month, week, day, hour, minute, second or millisecond — plurals work too.
  • timeZone (string, optional) — Whose day (or week, or month) it is — 23:30 UTC is already the next day in Tokyo. Only meaningful together with a unit, and refused without one. Defaults to UTC.

Answers: true or false.

if (dateIsEqual(input.event.receivedAt, now(), "day")) {
  log("it arrived today");
}

dateIsBetween(date, start, end) — pauses

Is the moment inside the window? Both edges count as inside, so a window built from dateStartOf and dateEndOf holds every moment of the period and no more.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • start (string | number | date) — The first moment of the window.
  • end (string | number | date) — The last moment of the window.

Answers: true or false. A window whose start is after its end is refused rather than answering false for everything, which would look exactly like an empty window.

const from = dateStartOf(today(), "month");
const to = dateEndOf(today(), "month");
log(dateIsBetween(input.event.receivedAt, from, to));

dateStartOf(date, unit, timeZone?) — pauses

The first moment of the day, month, year or other unit the date falls in. A week starts on Monday.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • unit (string) — Which period: year, month, week, day, hour, minute, second or millisecond — plurals work too.
  • timeZone (string, optional) — A timezone name such as Europe/Vienna, or UTC. Defaults to UTC — never the machine’s own, so the same script answers the same everywhere. A name nobody has is refused rather than quietly read as UTC.

Answers: The moment as ISO text, written in the timezone that was asked for. On a day whose clocks moved, this is that day’s real first moment — 2026-03-29T00:00:00.000+01:00 in Vienna, even though the day ends on +02:00.

const monthStart = dateStartOf(now(), "month", "Europe/Vienna");
log("the month began at " + monthStart);

dateEndOf(date, unit, timeZone?) — pauses

The LAST millisecond of the day, month, year or other unit the date falls in.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • unit (string) — Which period: year, month, week, day, hour, minute, second or millisecond — plurals work too.
  • timeZone (string, optional) — A timezone name such as Europe/Vienna, or UTC. Defaults to UTC — never the machine’s own, so the same script answers the same everywhere. A name nobody has is refused rather than quietly read as UTC.

Answers: The moment as ISO text in the timezone that was asked for, e.g. 2026-02-28T23:59:59.999Z — the last millisecond, so dateIsBetween with it covers the whole period.

const monthEnd = dateEndOf(now(), "month");
log("the month runs until " + monthEnd);

dateParse(text, pattern, timeZone?) — pauses

Reads a date somebody else wrote, by the pattern it was written in — the way to take 14.07.2026 from a file or a form and turn it into a moment.

  • text (string) — The text to read. It must match the pattern exactly, with nothing left over.
  • pattern (string) — The shape the text is in, from the number tokens YYYY, MM, DD, HH, mm, ss and SSS; everything else in the pattern must appear in the text as it stands. What the pattern does not name starts the period, so YYYY-MM reads as the first of that month at midnight, and a pattern that names no part of a date at all is refused rather than answering 1970. Month names and two-digit years can be written but not read — a name needs a language and 26 cannot say which century it means.
  • timeZone (string, optional) — Which timezone the text is written in — 2026-07-14 12:30 means a different moment in Vienna than in UTC. Defaults to UTC. A wall clock that never happened, because the clocks jumped over it, answers the moment the day reached it; one that happened twice answers the first of them.

Answers: The moment as ISO text, written in the timezone it was read in.

const paidAt = dateParse("14.07.2026", "DD.MM.YYYY", "Europe/Vienna");
log("paid at " + paidAt);

dateFormat(date, pattern, timeZone?, locale?) — pauses

Writes a moment out in a pattern of your own, in the timezone you name — the same tokens formatDate uses, plus milliseconds and the names of months and days.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • pattern (string) — The shape of the answer. YYYY and YY for the year, MM for the month and MMM/MMMM for its name, DD for the day and DDD/DDDD for the weekday’s name, HH, mm, ss and SSS for the time. Every other character is written as it stands.
  • timeZone (string, optional) — A timezone name such as Europe/Vienna, or UTC. Defaults to UTC — never the machine’s own, so the same script answers the same everywhere. A name nobody has is refused rather than quietly read as UTC.
  • locale (string, optional) — Which language the month and weekday names are written in — a language tag such as de-DE. Defaults to en-US, stated rather than taken from the machine, so the same script writes the same date everywhere. A tag nobody has data for is refused rather than quietly written in English.

Answers: The written text.

log(dateFormat(now(), "DDDD, DD. MMMM YYYY", "Europe/Vienna", "de-DE"));

dateGet(date, unit, timeZone?) — pauses

Reads one part of a date as a number — the year, the month, the day, the time, or which day of the week it is.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • unit (string) — Which part: year, month (1–12), day, hour, minute, second, millisecond, or weekday (1 for Monday through 7 for Sunday).
  • timeZone (string, optional) — A timezone name such as Europe/Vienna, or UTC. Defaults to UTC — never the machine’s own, so the same script answers the same everywhere. A name nobody has is refused rather than quietly read as UTC.

Answers: The part as a number. Which one it is depends on the timezone: 23:30 UTC is already the next day, with a different weekday, in Vienna.

if (dateGet(now(), "weekday", "Europe/Vienna") === 1) {
  log("it is Monday in Vienna");
}

dateSet(date, unit, value, timeZone?) — pauses

Answers the same moment with one part changed — the way to say “nine o’clock that morning” or “the first of that month”.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • unit (string) — Which part to change: year, month, day, hour, minute, second or millisecond. The weekday cannot be set — move the date with dateAdd instead.
  • value (number) — What to set it to. A value the part cannot hold is refused by name — the 31st of a month with 30 days says so, rather than quietly becoming the 1st of the next one. Changing the MONTH to a shorter one pulls the day back to its last day, exactly as dateAdd does.
  • timeZone (string, optional) — A timezone name such as Europe/Vienna, or UTC. Defaults to UTC — never the machine’s own, so the same script answers the same everywhere. A name nobody has is refused rather than quietly read as UTC.

Answers: The new moment as ISO text, written in the timezone that was asked for.

const nine = dateSet(dateStartOf(now(), "day", "Europe/Vienna"), "hour", 9, "Europe/Vienna");
log("the reminder goes out at " + nine);

isWeekend(date, timeZone?) — pauses

Is the date a Saturday or a Sunday?

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • timeZone (string, optional) — A timezone name such as Europe/Vienna, or UTC. Defaults to UTC — never the machine’s own, so the same script answers the same everywhere. A name nobody has is refused rather than quietly read as UTC.

Answers: true or false. In the timezone asked for: Friday evening in UTC is already Saturday in Tokyo.

if (isWeekend(now(), "Europe/Vienna")) {
  log("holding this until Monday");
}

isLeapYear(date, timeZone?) — pauses

Does the year this date falls in have a 29 February?

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • timeZone (string, optional) — A timezone name such as Europe/Vienna, or UTC. Defaults to UTC — never the machine’s own, so the same script answers the same everywhere. A name nobody has is refused rather than quietly read as UTC.

Answers: true or false, by the full rule — 2000 was a leap year, 1900 was not. Which year the date falls in depends on the timezone: 23:30 UTC on New Year’s Eve is already the new year further east.

log(isLeapYear("2024-05-05"));

daysInMonth(date, timeZone?) — pauses

How many days the month this date falls in has.

  • date (string | number | date) — An ISO date (2026-07-14), a datetime that carries its timezone (2026-07-14T12:30:00Z), milliseconds since 1970, or a date value. A datetime without a timezone is refused: it would mean a different moment on every machine that read it.
  • timeZone (string, optional) — A timezone name such as Europe/Vienna, or UTC. Defaults to UTC — never the machine’s own, so the same script answers the same everywhere. A name nobody has is refused rather than quietly read as UTC.

Answers: The number of days — 28, 29, 30 or 31.

const days = daysInMonth(now());
log("this month has " + days + " days");
Rendered from docs/guide/authoring-pack-functions-datetime.md in the product's own repository, at build time. Found a problem on this page? Write to the address in the footer.