# 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](/developers/plugins/build/fetcher/#run-context) gives
`today`, your zone's date when the run starts, often not UTC's:

```sh run
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 output
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 norun="from plugins/harbour-almanac/fetch.mjs, which check runs"
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 |
|---|---|---|---|
| Harbour Almanac, `Pacific/Auckland` | `15 6,18 * * *` | 06:15 and 18:15, every day | 17:15 and 05:15 in New Zealand daylight time, 18:15 and 06:15 outside it |
| DOP Economy, `America/Santo_Domingo` | `30 9,17 * * 1-5` | 09:30 and 17:30, Monday to Friday | 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`](/developers/plugins/publish/submission-errors/#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 records manifest=plugins/harbour-almanac/plugin.json today=2026-10-01 error=PLG-RECORD-AFTER-TODAY
{"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.
