# Time & Time Zones

> Current time in any zone; convert ISO 8601, RFC 2822, HTTP dates and Unix timestamps; DST-aware differences and date arithmetic.

Category: Time & Scheduling · Page: https://util.caruana.me/tools/time/

## API, MCP and A2A

### Current time (`time_now`)

`GET|POST https://util.caruana.me/api/time/now` · MCP tool `time_now` · A2A skill `time_now` · [reference](https://util.caruana.me/reference/time_now/)

Returns the current time from the server's clock when the request is processed (millisecond resolution) as UTC, Unix seconds and milliseconds, and, for each requested zone, the local ISO 8601 time with offset, weekday, ISO week, day of year, zone abbreviation, UTC offset and whether DST is in effect. Use it instead of guessing today's date.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `zones` | list of strings | no | Comma-separated (or JSON array) zones: IANA names (`America/New_York`, `Europe/Paris`, `Asia/Tokyo`), `UTC`, or fixed offsets (`+05:30`, `UTC-03:00`). Abbreviations like `PST` or `CET` are rejected as ambiguous. At most 20. |
| `formats` | list of rfc2822 \| http \| sql \| iso_basic \| iso_week_date \| iso_ordinal \| human | no | Extra renderings added to each zone's `formatted` map: `rfc2822` (`Tue, 13 Oct 2026 14:30:00 +0200`), `http` (HTTP-date, always GMT), `sql` (`2026-10-13 14:30:00.000 +02:00`), `iso_basic` (`20261013T143000+0200`), `iso_week_date` (`2026-W42-2`), `iso_ordinal` (`2026-286`), `human` (`Tuesday, October 13, 2026 at 2:30:00 PM GMT+2`). |
| `pattern` | string (≤ 100 chars) | no | Custom format using Luxon tokens (e.g. `yyyy-MM-dd HH:mm ZZZZ`; quote literal text in single quotes). Added to `formatted.pattern`. English names. |

Privacy: The server receives only the zone names and format options, reads its own clock, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded.

### Convert a time (`time_convert`)

`GET|POST https://util.caruana.me/api/time/convert` · MCP tool `time_convert` · A2A skill `time_convert` · [reference](https://util.caruana.me/reference/time_convert/)

Parses `input` and returns the instant in UTC, as Unix seconds and milliseconds, and in each zone of `zones`. The response says exactly how the input was read: the detected format, the inferred timestamp unit (with what the other units would have meant), and, for a wall-clock time without an offset, the zone used and whether that local time was unique, skipped by a DST gap, or repeated in a DST overlap.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `input` | string (≤ 100 chars) | yes | ISO 8601 / RFC 3339 (`2026-10-13T14:30:00Z`, `2026-10-13T14:30:00+02:00`, `2026-10-13 14:30`, `2026-10-13`, `2026-W42-2`, with an optional RFC 9557 `[Zone/Name]` suffix), RFC 2822, HTTP-date (IMF-fixdate, RFC 850, asctime), a Unix timestamp (seconds, milliseconds, microseconds or nanoseconds; decimals allowed), or `now`. |
| `unit` | auto \| seconds \| milliseconds \| microseconds \| nanoseconds | no | Unit of a numeric Unix timestamp. `auto` infers it from the magnitude: below 10^11 seconds, below 10^14 milliseconds, below 10^17 microseconds, otherwise nanoseconds. Only valid with numeric input. |
| `from_zone` | string (≤ 64 chars) | no | Zone in which to read an input that has no UTC offset (`2026-03-08T02:30`). Required for such input; ignored when the input carries an offset or a Unix timestamp. |
| `disambiguation` | compatible \| earlier \| later \| reject | no | How to resolve a wall-clock time that falls in a DST gap or overlap (same meaning as in JavaScript Temporal). `compatible`: gap → shift forward by the gap length, overlap → earlier instant. `earlier` / `later`: pick that instant (in a gap, the instant before or after the gap). `reject`: fail with 422. |
| `zones` | list of strings | no | Comma-separated (or JSON array) zones to show the instant in: IANA names, `UTC`, or fixed offsets such as `+05:30`. At most 20. |
| `formats` | list of rfc2822 \| http \| sql \| iso_basic \| iso_week_date \| iso_ordinal \| human | no | Extra renderings added to each zone's `formatted` map: `rfc2822` (`Tue, 13 Oct 2026 14:30:00 +0200`), `http` (HTTP-date, always GMT), `sql` (`2026-10-13 14:30:00.000 +02:00`), `iso_basic` (`20261013T143000+0200`), `iso_week_date` (`2026-W42-2`), `iso_ordinal` (`2026-286`), `human` (`Tuesday, October 13, 2026 at 2:30:00 PM GMT+2`). |
| `pattern` | string (≤ 100 chars) | no | Custom format using Luxon tokens (e.g. `yyyy-MM-dd HH:mm ZZZZ`; quote literal text in single quotes). Added to `formatted.pattern`. English names. |

Privacy: The server receives the date-time string and zone names, converts them in memory, and returns the result. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded.

### Difference between two times (`time_diff`)

`GET|POST https://util.caruana.me/api/time/diff` · MCP tool `time_diff` · A2A skill `time_diff` · [reference](https://util.caruana.me/reference/time_diff/)

Computes `end` minus `start`. `total` is exact elapsed time (a day is 86,400,000 ms). `calendar` is the breakdown in years, months, days, hours, minutes, seconds and milliseconds as a calendar in `zone` sees it, counted forward from the earlier instant: adding it to the earlier instant with `time_add` in the same zone gives the later instant exactly. Across a DST change the two differ: 12:00 to 12:00 the next day is `P1D` on the calendar but 23 or 25 hours elapsed. `calendar_days` counts midnights crossed in `zone`.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `start` | string (≤ 100 chars) | yes | Start instant. ISO 8601 / RFC 3339 (`2026-10-13T14:30:00Z`, `2026-10-13T14:30:00+02:00`, `2026-10-13 14:30`, `2026-10-13`, `2026-W42-2`, with an optional RFC 9557 `[Zone/Name]` suffix), RFC 2822, HTTP-date (IMF-fixdate, RFC 850, asctime), a Unix timestamp (seconds, milliseconds, microseconds or nanoseconds; decimals allowed), or `now`. |
| `end` | string (≤ 100 chars) | yes | End instant, same formats as `start`. |
| `zone` | string (≤ 64 chars) | no | Zone for the calendar breakdown and `calendar_days`, and for reading `start`/`end` that have no UTC offset. IANA name, `UTC`, or fixed offset. |
| `unit` | auto \| seconds \| milliseconds \| microseconds \| nanoseconds | no | Unit of a numeric Unix timestamp. `auto` infers it from the magnitude: below 10^11 seconds, below 10^14 milliseconds, below 10^17 microseconds, otherwise nanoseconds. Only valid with numeric input. |
| `disambiguation` | compatible \| earlier \| later \| reject | no | How to resolve a wall-clock time that falls in a DST gap or overlap (same meaning as in JavaScript Temporal). `compatible`: gap → shift forward by the gap length, overlap → earlier instant. `earlier` / `later`: pick that instant (in a gap, the instant before or after the gap). `reject`: fail with 422. |

Privacy: The server receives the two date-time strings and the zone name, computes the difference in memory, and returns it. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded.

### Add or subtract a duration (`time_add`)

`GET|POST https://util.caruana.me/api/time/add` · MCP tool `time_add` · A2A skill `time_add` · [reference](https://util.caruana.me/reference/time_add/)

Applies the duration the way JavaScript Temporal does (`ZonedDateTime.add`): years, months, weeks and days move the wall-clock date in `zone` and keep the wall-clock time, so `P1D` across a DST change lands at the same local time (23 or 25 hours later); a day of the month that does not exist is clamped to the last day (Jan 31 + `P1M` = Feb 28/29); then hours, minutes, seconds and milliseconds are added as exact elapsed time (`PT24H` is always 86,400 s). If the intermediate wall-clock time falls in a DST gap or overlap it is resolved with `disambiguation` and reported in `notes`.

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `input` | string (≤ 100 chars) | yes | Start instant. ISO 8601 / RFC 3339 (`2026-10-13T14:30:00Z`, `2026-10-13T14:30:00+02:00`, `2026-10-13 14:30`, `2026-10-13`, `2026-W42-2`, with an optional RFC 9557 `[Zone/Name]` suffix), RFC 2822, HTTP-date (IMF-fixdate, RFC 850, asctime), a Unix timestamp (seconds, milliseconds, microseconds or nanoseconds; decimals allowed), or `now`. |
| `duration` | string (≤ 64 chars) | no | ISO 8601 duration: `P1D`, `PT36H`, `P1Y2M10DT2H30M`, `P2W`, `-P1M` (negative subtracts), `PT1.5H`. Fractions only on the smallest of hours/minutes/seconds, to millisecond precision. Use either `duration` or `amount` + `amount_unit`. |
| `amount` | integer | no | Signed whole number of `amount_unit` to add (negative subtracts). Alternative to `duration`. |
| `amount_unit` | years \| months \| weeks \| days \| hours \| minutes \| seconds \| milliseconds | no | Unit of `amount`. Required with `amount`. |
| `subtract` | boolean | no | Subtract the duration instead of adding it (negates it). |
| `zone` | string (≤ 64 chars) | no | Zone whose calendar and wall clock govern years/months/weeks/days, used for reading an `input` without offset, and for the output. The offset inside `input` does not choose the zone: pass `America/New_York` to get New York DST behaviour. |
| `unit` | auto \| seconds \| milliseconds \| microseconds \| nanoseconds | no | Unit of a numeric Unix timestamp. `auto` infers it from the magnitude: below 10^11 seconds, below 10^14 milliseconds, below 10^17 microseconds, otherwise nanoseconds. Only valid with numeric input. |
| `disambiguation` | compatible \| earlier \| later \| reject | no | How to resolve a wall-clock time that falls in a DST gap or overlap (same meaning as in JavaScript Temporal). `compatible`: gap → shift forward by the gap length, overlap → earlier instant. `earlier` / `later`: pick that instant (in a gap, the instant before or after the gap). `reject`: fail with 422. |

Privacy: The server receives the date-time, duration and zone name, computes the result in memory, and returns it. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded.


## User guide

The tool sits in a window that works like a desktop app: menus on the top row and the **full-screen** icon at its right (**Esc** leaves full screen). The set-up options described below are in **Settings → Options…**, an overlay, and the **Settings** menu also lists the common choices directly. To keep the options in view instead, choose **View → Inline config**; the page remembers that choice. **Help → Keyboard shortcuts…** lists the keys.

The page has four panels.

1. **Now.** The current time in your browser's zone and in any IANA zones you list, updated every second. It shows the UTC offset, the zone abbreviation, and whether daylight saving time (DST) is in effect.
2. **Convert.** Paste almost any timestamp to see it in UTC, as a Unix timestamp, and in other zones:
   - ISO 8601 / RFC 3339: `2026-10-13T14:30:00Z`, `2026-10-13T14:30:00+02:00`, `2026-10-13 14:30`, `2026-10-13`
   - an email or HTTP date: `Tue, 13 Oct 2026 14:30:00 +0200`
   - a Unix timestamp in seconds, milliseconds, microseconds or nanoseconds
   - the word `now`

   A time without an offset (`2026-03-08 02:30`) is a _wall-clock_ time. It is read in the zone given under **Zone for times without an offset**, which defaults to your browser's zone. The summary line says exactly how the input was read. If the wall-clock time was skipped or repeated by a DST change, it says so.

3. **Difference.** Two times give both the _calendar_ difference (`P1M2D`, "1 month, 2 days later") and the _elapsed_ time (`PT23H`, total hours, seconds and milliseconds). The two differ across a DST change: noon to noon over the spring change is one calendar day, but only 23 hours.
4. **Add.** Add or subtract an ISO 8601 duration such as `P1D`, `PT36H` or `-P1M`. Days, weeks, months and years keep the wall-clock time; hours, minutes and seconds are exact elapsed time.

> **Why "P1D" and "PT24H" differ.** In New York, 2026-03-07 12:00 plus `P1D` is 2026-03-08 12:00 (23 hours later, because clocks moved forward). Plus `PT24H` it is 2026-03-08 13:00. Use days for "same time tomorrow" and hours for "exactly 24 hours later".

**For agents.** Call `time_now` instead of assuming today's date. Pass explicit zones such as `America/New_York`; abbreviations such as `EST` are refused because they are ambiguous. Read `notes` in the response: they list every assumption that was made.

## Specification

**Input formats.** Inputs are tried in this order; the first that matches wins.

| Order | Format         | Rules                                                                                                                                                                                                                                                                                                    |
| ----- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1     | `now`          | Case-insensitive. The time at which the request (or the page) evaluates it.                                                                                                                                                                                                                              |
| 2     | Unix timestamp | An optional sign, digits, and optional decimals (`1791900000`, `-1.5`, `1791900000123`). Any all-digit input is a timestamp. An 8-digit input that is also a valid `YYYYMMDD` date gets a note suggesting `YYYY-MM-DD`.                                                                                  |
| 3     | ISO 8601       | Luxon's ISO parser: calendar, ordinal (`2026-286`) and week (`2026-W42-2`) dates; basic or extended form; fractional seconds; `24:00` (read as the next day's 00:00, with a note). A space may replace `T`, and a space may precede the offset. An RFC 9557 suffix such as `[Europe/Paris]` is honoured. |
| 4     | HTTP-date      | IMF-fixdate, RFC 850 and asctime forms (RFC 9110 §5.6.7), always GMT.                                                                                                                                                                                                                                    |
| 5     | RFC 2822       | Numeric offsets and the obsolete zone names (`GMT`, `EST`, …). The weekday, if present, must match the date, or the input is rejected.                                                                                                                                                                   |

**Unix timestamp units.** With `unit=auto` (the default), the unit follows the magnitude of the integer part:

| Magnitude            | Unit         | Example               |
| -------------------- | ------------ | --------------------- |
| below 10^11          | seconds      | `1791900000`          |
| 10^11 to below 10^14 | milliseconds | `1791900000123`       |
| 10^14 to below 10^17 | microseconds | `1791900000123456`    |
| 10^17 or more        | nanoseconds  | `1791900000123456789` |

Each threshold is 10^11 of the smaller unit (1973-03-03) and the year 5138 in the larger unit, so any date from March 1973 to 5138 is detected correctly whatever its unit. A millisecond timestamp before March 1973 is read as seconds: pass `unit`. An auto-detected unit is always reported in `notes`, together with what the number would mean in the neighbouring units. Pass `unit` to override it. Parsing is exact (big integers), then rounded down to the millisecond, which is the resolution of every result. When digits were dropped, `notes` says so.

**Range.** Instants must lie within ±8.64 × 10^15 ms of 1970-01-01T00:00:00Z (the JavaScript `Date` range, years −271821 to 275760).

**Wall-clock times.** An input without an offset has no meaning until a zone is given.

- **Which zone:** `time_convert` uses `from_zone` and fails without it; `time_diff` and `time_add` use `zone` (default `UTC`). A bracketed `[Zone]` in the input takes precedence.
- **How it resolves:** the resolver finds every instant whose local time in that zone equals the input. The result is reported in `local_time`:

| `local_time` | Meaning                        | `compatible` (default)                  | `earlier`                                 | `later`               | `reject`     |
| ------------ | ------------------------------ | --------------------------------------- | ----------------------------------------- | --------------------- | ------------ |
| `unique`     | One instant                    | that instant                            | that instant                              | that instant          | that instant |
| `gap`        | Skipped (clocks moved forward) | shift forward by the gap: 02:30 → 03:30 | the instant before the gap: 02:30 → 01:30 | as `compatible`       | 422 error    |
| `overlap`    | Repeated (clocks moved back)   | the first occurrence                    | the first occurrence                      | the second occurrence | 422 error    |

These are the rules of JavaScript Temporal's `disambiguation` option. If both an offset and a bracketed zone are present, the offset decides the instant; a mismatch between them is reported in `notes`.

**Zones.**

- **Accepted:**
  - IANA names, with letter case corrected (`america/new_york` → `America/New_York`);
  - `UTC` (also `GMT`, `Z`, `Zulu`);
  - fixed offsets `±H`, `±HH:MM`, `UTC±HH:MM` or `GMT±HHMM`, up to ±18:00, reported as `UTC±HH:MM`.
- **Rejected:**
  - `local` and `system`;
  - unknown names;
  - the abbreviations PST, PDT, EST, EDT, CST, CDT, MST, MDT, BST, CET, CEST, IST, JST, AEST and AEDT, each with a suggested IANA zone. Some of these are valid IANA names, but they are fixed offsets without DST, or now aliases of an unexpected zone.
- **Warnings:**
  - fixed offsets (they never change for DST);
  - legacy names without a `/` (such as `Japan`);
  - `Etc/GMT±N`, whose sign is inverted: `Etc/GMT+5` is UTC−05:00.

**Output for each zone.**

| Field                                          | Definition                                                                                                                                                                          |
| ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `iso`                                          | ISO 8601 with offset; `Z` only when the zone is `UTC` (so `Europe/London` in winter prints `+00:00`); milliseconds only when non-zero                                               |
| `local`, `date`, `time`                        | Wall-clock values without offset, 24-hour clock                                                                                                                                     |
| `offset`, `offset_minutes`                     | Offset from UTC, `±HH:MM` and signed minutes (east positive)                                                                                                                        |
| `abbreviation`, `offset_name`                  | Zone names as `Intl` prints them for `en-US`. Many zones have no English abbreviation and show `GMT+2`; the exact text depends on the ICU version                                   |
| `dst`                                          | Luxon's rule: true when the offset at this instant is greater than the zone's offset at the same time on 1 January or in May of that year; always false for `UTC` and fixed offsets |
| `weekday_number`                               | ISO 8601: Monday = 1 … Sunday = 7                                                                                                                                                   |
| `iso_week`, `iso_week_year`, `iso_week_number` | ISO 8601 week date. Near 1 January the week-numbering year can differ from the calendar year (2027-01-01 is in `2026-W53`)                                                          |
| `day_of_year`                                  | 1–366                                                                                                                                                                               |
| `formatted`                                    | Extra formats on request: `rfc2822`, `http` (always GMT), `sql`, `iso_basic`, `iso_week_date`, `iso_ordinal`, `human`, and a Luxon token `pattern`                                  |

`unix_seconds` is rounded down, so −0.5 s is −1.

**Durations.**

- **Grammar:** `[+|-]P[nY][nM][nW][nD][T[nH][nM][nS]]`, case-insensitive, with at least one component. Weeks may be combined with other units, as ISO 8601-2 and Temporal allow.
- **Fractions:** only on the smallest of hours, minutes and seconds, with `.` or `,`, to whole milliseconds. `PT1.5H` is normalised to `PT1H30M`.
- **Rejected:** fractional years, months, weeks or days (`P1.5D`), because those units have no fixed length.
- **API alternative:** `amount` with `amount_unit` (`years` … `milliseconds`). `subtract=true` negates the duration.

**Addition** (`time_add`) follows `Temporal.ZonedDateTime.prototype.add`:

1. Years and months are added to the wall-clock date in `zone`. A day that does not exist in the target month is clamped to its last day (Jan 31 + `P1M` = Feb 28; Feb 29 + `P1Y` = Feb 28). Clamping is reported in `notes`.
2. Weeks (7 days) and days are added to the wall-clock date.
3. The resulting wall-clock time is resolved in `zone` with `disambiguation`, as in the table above.
4. Hours, minutes, seconds and milliseconds are added as exact elapsed time.

A duration with no calendar part (only `PT…`) never consults the zone's rules. The offset written in `input` does not choose the zone: `2026-03-07T12:00-05:00` plus `P1D` in `UTC` is exactly 24 hours later.

**Differences** (`time_diff`).

- **`total`:** exact elapsed time, end minus start (signed); a day is 86,400,000 ms. Values other than milliseconds are IEEE 754 doubles and may be fractional.
- **`calendar`:** counted forward from the earlier to the later instant in `zone`, largest units first. Months are the largest whole number that does not pass the later instant, then days the same way. The rest is elapsed hours, minutes, seconds and milliseconds. Magnitudes are non-negative; `sign` gives the direction. By construction, `time_add` of `calendar` to the earlier instant in the same zone returns the later instant exactly; the test suite checks this over thousands of random pairs in DST zones.
- **`iso_duration`:** the calendar breakdown as ISO 8601, prefixed with `-` when end is before start (the ISO 8601-2 / Temporal convention), or `PT0S`. Weeks are not used.
- **`iso_duration_exact`:** elapsed time in hours, minutes and seconds only (`PT23H`).
- **`calendar_days`:** the local date of end minus the local date of start in `zone`, ignoring time of day.

**Limits.** Inputs up to 100 characters; up to 20 zones per request; `pattern` up to 100 characters.
