> ## Documentation Index
> Fetch the complete documentation index at: https://api.simkl.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Dates and timezones

> How Simkl returns dates and what to do in your app.

All API dates are returned in **ISO 8601** format with the **`Z` (UTC / GMT)** timezone marker:

```
2015-03-15T15:30:11Z
```

<Note>
  Some legacy endpoints still return dates in `GMT-05:00` (New York) — this is being phased out, but for now treat the timezone field as authoritative rather than assuming UTC.
</Note>

Convert to the user's local timezone in your app — don't assume the server's locale.

<CodeGroup>
  ```js Node theme={"theme":{"light":"github-light","dark":"vesper"}}
  const utc = new Date('2015-03-15T15:30:11Z');
  const local = utc.toLocaleString();        // user's locale
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"vesper"}}
  from datetime import datetime, timezone
  utc = datetime.fromisoformat('2015-03-15T15:30:11+00:00')
  local = utc.astimezone()                    # system local timezone
  ```
</CodeGroup>

## Per-endpoint format matrix

Different endpoints use different date formats depending on whether
the field is a user activity timestamp, a broadcast schedule, or a
catalog release date. Use the per-field reference below to know what
to parse for each one.

**The four formats you'll meet:**

| Format                | Example                     | When                                                                                                                                                               |
| --------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`ISO_UTC_Z`**       | `2026-05-13T16:08:00Z`      | Default for user-generated timestamps (activities, watchlist, playback, ratings, profile). Convert to the user's profile timezone for display.                     |
| **`ISO_WITH_OFFSET`** | `2010-10-31T21:00:00-05:00` | Broadcast schedules — the offset is the **originating network's** timezone (US shows are `-05:00`, anime are `+09:00`). Use it as authoritative; don't assume UTC. |
| **`DATE_ONLY`**       | `2010-07-15`                | Catalog release dates (year-month-day, no time).                                                                                                                   |
| **`YEAR_ONLY`**       | `2010`                      | Integer year-of-release on every catalog item.                                                                                                                     |

Plus the `1970-01-01T00:00:01Z` placeholder for *"watched, date unknown"* — see [the section below](#very-long-time-ago-placeholder).

### Endpoint → field → format

| Endpoint                                                                      | Field(s)                                                                                  | Format                | Notes                                                                                   |
| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | --------------------- | --------------------------------------------------------------------------------------- |
| [`GET /movies/{id}`](/api-reference/simkl/get-movie)                          | `released`, `release_dates[*].results[*].release_date`                                    | `DATE_ONLY`           | Catalog release; no time component.                                                     |
| [`GET /tv/{id}`](/api-reference/simkl/get-tv-show)                            | `first_aired`, `last_aired`                                                               | `ISO_UTC_Z`           | Series-level air-window markers.                                                        |
| [`GET /anime/{id}`](/api-reference/simkl/get-anime)                           | `first_aired`, `last_aired`                                                               | `ISO_UTC_Z`           | Same as TV.                                                                             |
| [`GET /tv/episodes/{id}`](/api-reference/simkl/get-tv-episodes)               | `[*].date`                                                                                | `ISO_WITH_OFFSET`     | Per-episode air time in the **network's local timezone** (e.g. `-05:00` for US shows).  |
| [`GET /anime/episodes/{id}`](/api-reference/simkl/get-anime-episodes)         | `[*].date`                                                                                | `ISO_WITH_OFFSET`     | Per-episode air time in **`+09:00` (Asia/Tokyo)** for the canonical Japanese broadcast. |
| [`GET /tv/airing`](/api-reference/simkl/get-tv-airing)                        | `[*].date`                                                                                | `ISO_WITH_OFFSET`     | Originating network's timezone.                                                         |
| [`GET /anime/airing`](/api-reference/simkl/get-anime-airing)                  | `[*].date`                                                                                | `ISO_WITH_OFFSET`     | Asia/Tokyo for Japan-originated anime.                                                  |
| [`GET /sync/activities`](/api-reference/simkl/get-activities)                 | every `*_at` field (`all`, `tv_shows.completed`, `anime.dropped`, etc. — 27 fields total) | `ISO_UTC_Z`           | All UTC; convert to profile timezone for display.                                       |
| [`GET /sync/all-items/{type}`](/api-reference/simkl/get-all-items)            | `<type>[*].added_to_watchlist_at`, `[*].last_watched_at`, `[*].user_rated_at`             | `ISO_UTC_Z`           | All user-activity timestamps in UTC.                                                    |
| [`GET /sync/playback/{type}`](/api-reference/simkl/get-playback-sessions)     | `[*].paused_at`                                                                           | `ISO_UTC_Z`           | Pause-event timestamp in UTC.                                                           |
| [`POST /users/settings`](/api-reference/simkl/get-user-settings)              | `user.joined_at`                                                                          | `ISO_UTC_Z`           | Account creation date.                                                                  |
| [`GET /calendar/{type}.json`](/api-reference/calendar) *(CDN)*                | `[*].date`                                                                                | `ISO_WITH_OFFSET`     | Episode-level air time.                                                                 |
| [`GET /calendar/{type}.json`](/api-reference/calendar) *(CDN)*                | `[*].release_date`                                                                        | `DATE_ONLY`           | Catalog release date alongside the air-time entry.                                      |
| [`GET /calendar/{year}/{month}/{type}.json`](/api-reference/calendar) *(CDN)* | same as rolling calendar                                                                  | mixed                 | Same shape, different month window.                                                     |
| Every catalog endpoint                                                        | `year`                                                                                    | `YEAR_ONLY` (integer) | Release year as an int — convenient for filtering.                                      |

<Note>
  **`ISO_WITH_OFFSET` is authoritative — don't strip it.** The
  broadcast `date` field on episode/airing/calendar endpoints encodes
  the originating network's timezone deliberately. Stripping the offset
  and assuming UTC will silently shift airing schedules by hours (US
  shows by 5, Japanese anime by 9). Always parse the timezone too.
</Note>

<Tip>
  **Missing dates** show up as `null` (Type 4 — "data not on file"),
  not as the literal string `"null"` or epoch. See [Null and missing
  values](/conventions/null-values) for the full nullability model.
  The `1970-01-01T00:00:01Z` placeholder is **a different concept** —
  it explicitly means "the user watched this but doesn't remember when",
  not "we don't know" — see [the dedicated section
  below](#very-long-time-ago-placeholder).
</Tip>

## User timezone preference

Each Simkl member has a **configurable timezone** on their profile, set at [simkl.com/settings/](https://simkl.com/settings/) (with the date and time format alongside it). Third-party apps can read the user's timezone via [`POST /users/settings`](/api-reference/simkl/get-user-settings) — it's returned as the `account.timezone` field, an IANA timezone name (e.g. `"America/New_York"`, `"Europe/Madrid"`, `"Asia/Tokyo"`):

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "user": { … },
  "account": {
    "id": 12345,
    "timezone": "Europe/Madrid",
    "type": "vip"
  }
}
```

**Use the profile timezone for absolute timestamps when the user is signed in.** That's the single source of truth that keeps Simkl-rendered dates consistent across the user's phone, tablet, TV, and the simkl.com web UI.

| Scenario                                                          | What to use                                                              |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Authenticated user, absolute date (`"Watched on Apr 15 at 8 PM"`) | `account.timezone` from `POST /users/settings`.                          |
| Authenticated user, relative date (`"Watched 2 hours ago"`)       | Device timezone is fine — relative deltas don't depend on absolute zone. |
| Unauthenticated / pre-login                                       | Device timezone as a fallback.                                           |

<CodeGroup>
  ```js Node theme={"theme":{"light":"github-light","dark":"vesper"}}
  // Cache account.timezone after one call to /users/settings.
  const PROFILE_TZ = 'America/New_York';

  new Date('2026-05-13T16:08:00Z').toLocaleString('en-US', {
    timeZone: PROFILE_TZ,
    dateStyle: 'medium',
    timeStyle: 'short',
  });
  // → "May 13, 2026, 12:08 PM"
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"vesper"}}
  from datetime import datetime
  from zoneinfo import ZoneInfo

  PROFILE_TZ = ZoneInfo('America/New_York')   # from account.timezone

  dt = datetime.fromisoformat('2026-05-13T16:08:00+00:00')
  dt.astimezone(PROFILE_TZ).strftime('%Y-%m-%d %I:%M %p')
  # → "2026-05-13 12:08 PM"
  ```
</CodeGroup>

The user can change the setting at any time on simkl.com, but it's overwhelmingly **set-and-forget** in practice — don't re-fetch `POST /users/settings` on a timer or on every app launch / wake. Instead, gate the re-fetch on [`/sync/activities`](/api-reference/simkl/get-activities), which returns a `settings.all` timestamp alongside the per-list timestamps. The pattern matches every other Sync surface:

```js theme={"theme":{"light":"github-light","dark":"vesper"}}
// On app launch / when you'd otherwise hit /users/settings:
const a = await getActivities();
if (a.settings.all !== local.lastSettingsSync) {
  const settings = await fetchUsersSettings();
  local.timezone        = settings.account.timezone;
  local.lastSettingsSync = a.settings.all;
}
```

That way most launches do **zero** extra calls (the activities check is the cheap "is anything new?" gate you're running for the rest of the sync loop anyway), and you only re-fetch the heavier settings payload on the rare occasion the user actually changed something.

<Note>
  The simkl.com settings UI also lets the user pick a **date format** (e.g. `MM/DD/YYYY`) and a **time format** (12-hour AM/PM vs 24-hour). Those preferences are **not currently exposed** by `POST /users/settings` — only `account.timezone` and `account.type` come back. Until they're added to the API, render dates and times using the device's locale conventions (e.g. `'en-US'` for AM/PM, `'en-GB'` for 24-hour) inside the user's profile timezone.
</Note>

## "Very long time ago" placeholder

```
1970-01-01T00:00:01Z
```

Simkl uses this near-epoch timestamp as a **deliberate placeholder** meaning *"the user watched this but doesn't remember when"* — typically because they watched the title years ago and don't recall the precise date. It is **not** a NULL, a bug, or missing data. It can appear on any field that carries a watch timestamp: `watched_at`, `last_watched_at`, per-episode `watched_at` inside `seasons[].episodes[]`, and similar.

**On simkl.com,** the "When did you watch this?" date picker offers it as a smart-suggestion card labelled **"Very long time ago"** with the helper line *"I don't remember"*. That's the canonical user-facing label — clients should mirror it (translated to the user's locale).

### Writing the placeholder

When a user picks the "Very long time ago / I don't remember" option in your app's date picker, POST `1970-01-01T00:00:01Z` on the `watched_at` field. This works on every write endpoint that accepts watch timestamps — [`POST /sync/history`](/api-reference/simkl/add-to-history), [`POST /sync/watched`](/api-reference/simkl/get-watched), and so on:

```json theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "movies": [
    { "ids": { "simkl": 53536 }, "watched_at": "1970-01-01T00:00:01Z" }
  ]
}
```

Simkl stores this as a *"watched, date unknown"* entry and treats it consistently across the API and the simkl.com UI.

### Reading the placeholder

Any `watched_at` / `last_watched_at` value at or near `1970-01-01T00:00:01Z` is the placeholder. Detection rule: **any timestamp before `2000-01-01` is effectively the "date unknown" placeholder.** That guard handles both the canonical epoch+1 second value and any near-epoch variations from legacy data.

Render it in your UI as **"Very long time ago"** (matching simkl.com's label) or a localized equivalent — **never** as the literal `January 1, 1970`, which looks like a parsing error or a corrupt date to end users.

```js Node theme={"theme":{"light":"github-light","dark":"vesper"}}
function formatWatchedAt(iso) {
  const d = new Date(iso);
  if (d.getFullYear() < 2000) return 'Very long time ago';
  return d.toLocaleDateString();
}
```

```python Python theme={"theme":{"light":"github-light","dark":"vesper"}}
from datetime import datetime, timezone

def format_watched_at(iso: str) -> str:
    dt = datetime.fromisoformat(iso.replace('Z', '+00:00'))
    if dt.year < 2000:
        return 'Very long time ago'
    return dt.astimezone().strftime('%Y-%m-%d')
```

### Date picker UX — mirror simkl.com

The simkl.com "When did you watch this?" date picker is the reference UX for date selection across the Simkl ecosystem. Apps that surface their own date pickers should mirror its three-tier structure:

**Common times** (quick-pick buttons):

* *Just now* — current timestamp
* *Today* — today at the current time
* *Yesterday* — yesterday at the current time
* *Last week* — 7 days ago at the current time

**Smart suggestions** (context-aware cards):

* *Episode Air Date* — pre-fills the episode's original broadcast date (label: "Original broadcast"). Useful when a user watched live or wants to record a viewing at the air-date for stats / archival purposes.
* *Very long time ago* — the `1970-01-01T00:00:01Z` placeholder. Helper line: *"I don't remember"*. The option that maps to this documented sentinel.

**Recently used** (history): the last two or three timestamps the user picked across recent date selections, surfaced so they don't have to re-build a complex date if they're tagging multiple items at the same time.

Plus the full calendar grid for explicit picks. Third-party apps that build a "When did you watch this?" experience without the *Very long time ago* option force users into picking a fake date — that's worse data for everyone (the user's history, Simkl's stats, your app's later analytics) than just recording an honest *"I don't remember"*.

### Why this exists

Many users adopt Simkl long after they've watched hundreds of titles over the years. They want to record those watches honestly without inventing fake dates. The placeholder lets a client offer the *"I don't remember"* option that maps to a stable, documented server value.

simkl.com renders these entries with the "Very long time ago" label in history lists, sorts them after dated entries, and never displays the literal `1970-01-01` to end users — clients should do the same.
