Developers · How it works

Dates and time zones

Your plug-in's today is the date in its manifest's zone, and a record's date is never converted. The mistakes everyone makes once, and how to avoid them.

Your plug-in has one time zone, timezone, a named IANA zone such as Pacific/Auckland: the zone your source publishes in. It decides your today, when your schedule fires and how dates fill your source URLs. It never changes a record's date.

Your today is your zone's date

The run context gives today, your zone's date when the run starts, often not UTC's:

sh
node -e 'const now = new Date("2026-09-30T19:30:00.000Z"); console.log(now.toISOString().slice(0, 10)); console.log(new Intl.DateTimeFormat("en-CA", { timeZone: "Pacific/Auckland" }).format(now));'
text
2026-09-30 2026-10-01

The first line is the mistake everyone makes once: by toISOString() it is still 30 September in Auckland until 13:00 (12:00 in winter), and zones west of UTC err the other way. Take the today you are given; neither the Server's clock nor your machine's is yours. To count days, count on the calendar date at midnight UTC, where every day has 24 hours; there, and only there, toISOString() is right:

js
const addDays = (date, days) => new Date(Date.parse(`${date}T00:00:00Z`) + days * 86_400_000).toISOString().slice(0, 10);

Your schedule fires on your clock

Plug-in
schedule
On its clock
In UTC
Plug-in
Harbour Almanac, Pacific/Auckland
schedule
15 6,18 * * *
On its clock
06:15 and 18:15, every day
In UTC
17:15 and 05:15 in New Zealand daylight time, 18:15 and 06:15 outside it
Plug-in
DOP Economy, America/Santo_Domingo
schedule
30 9,17 * * 1-5
On its clock
09:30 and 17:30, Monday to Friday
In UTC
13:30 and 21:30 all year: no daylight time

Daylight saving moves the UTC time, never yours. A time the clocks skip does not run; a time they repeat runs once, at its first instant. Run after your source publishes: if it posts at 17:00, run at 17:30. {today}, {year} and {month} in a source's url turn at your midnight: Harbour Almanac reads the new year's tide table from midnight in Auckland, thirteen hours before UTC's.

A record's date is never converted

The date you print is the date every reader sees, in every zone: Monday 28 September's rate is on Monday 28 September in Tokyo and in Santo Domingo. Date a record as your source does, in your zone; never convert it into UTC and back, and never write a time into a date. A reader in Santo Domingo sees Auckland's 1 October tide once their own 1 October comes: the app draws no value after the reader's today.

Keep your window inside your file

A snapshot window counts from today, and a stored record a run stops printing inside it is withdrawn after three runs. If your source does not reach as far back as your window, every record between the two starts goes missing on every run. So Harbour Almanac's tide table for a year starts on 1 December of the year before: one starting 1 January would leave late December out, and by the third run some thirty withdrawals would wait for a person. The same holds ahead: the council's window and its source both reach 180 days.

A source split into one file a calendar year never reaches back past 1 January, since a url names only this year's ({year}). Leave snapshot out of such a series: nothing is counted missing, a record printed with new content is still revised, and the first run fills back only to 1 January.

Predictions are not observations

A value is something known, so never after today: Harbour Almanac's fetcher skips the tide table's predictions, since one would refuse the batch with PLG-RECORD-AFTER-TODAY. Only a scheduled event may sit on a future day. A meeting that happened or was cancelled is past news, even when cancelled weeks ahead:

jsonl
{"kind":"event","series":"council","id":"meeting-2026-11-1","date":"2026-11-05","state":"cancelled","title":"Harbour council meeting"}

Harbour Almanac leaves such a meeting out until its day (the stored scheduled one is withdrawn after three runs, and comes back cancelled on its day), and stops on one the source says was held in the future, since that file is broken.