Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsBlogPro
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges
  • Chrome Extension
  • Skill Manager

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Typescript Temporal

ASecurity

Temporal API for TS/JS dates and times. Use when parsing, formatting or comparing dates, computing today, time zones, durations or working days.

59 stars
0 votes
0 copies
0 views
Added 9/29/2026
developmentjavascripttypescriptjavabashnodetestinggitapidatabase

Works with

api

Security Analysis

A100/100

Scanned 9/29/2026

$npx -y skills add laurigates/claude-plugins --skill typescript-temporal --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Typescript Temporal?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Typescript Temporal
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/laurigates-typescript-temporal/badge)](https://www.skillsdirectory.com/skills/laurigates-typescript-temporal)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
Files
SKILL.md
---
created: 2026-09-29
modified: 2026-09-29
reviewed: 2026-09-29
name: typescript-temporal
description: "Temporal API for TS/JS dates and times. Use when parsing, formatting or comparing dates, computing today, time zones, durations or working days."
user-invocable: false
allowed-tools: Glob, Grep, Read, Bash, Edit, Write, TodoWrite, WebFetch, WebSearch
---

# Temporal for Dates and Times

New TypeScript and JavaScript code handles dates and times with `Temporal`.
`Date` packs a calendar date, an instant and the host time zone into one
mutable object. The bugs that follow all type-check: yesterday's date just
after midnight, every date shifted a day west of UTC, weekday indexes off by
one. Temporal gives each meaning its own type, so mixing them becomes a compile
error.

## Pick the type by what the value means

| The value is… | Type | Example |
|---|---|---|
| A calendar date with no time or zone (due date, holiday, timesheet day) | `Temporal.PlainDate` | `Temporal.PlainDate.from("2026-06-15")` |
| A month (billing or reporting period) | `Temporal.PlainYearMonth` | `Temporal.PlainYearMonth.from("2026-02").daysInMonth` is `28` |
| A wall-clock time, or date and time, with no zone ("09:00 every weekday") | `Temporal.PlainTime` / `Temporal.PlainDateTime` | `Temporal.PlainTime.from("09:00")` |
| A moment in time (log timestamp, `created_at`) | `Temporal.Instant` | `Temporal.Now.instant()` |
| A moment shown or bucketed in a place | `Temporal.ZonedDateTime` with an explicit IANA zone | `instant.toZonedDateTimeISO("Europe/Helsinki")` |
| A length of time | `Temporal.Duration` | `Temporal.Duration.from({ hours: 7, minutes: 15 }).total("hours")` is `7.25` |

If answering "which day is this?" needs a time zone, the value is not a
`PlainDate` yet.

## Convert a timestamp to a date once, at the boundary, in a named zone

```ts
// Wrong: the UTC calendar date, not the user's. In Finland (UTC+2/+3) this is
// yesterday from 00:00 to 02:00 local in winter and to 03:00 in summer.
const utcToday = new Date().toISOString().slice(0, 10); // or .split("T")[0]

// Right: one "today" seam for the whole codebase (see Testing for why it
// reads Date.now())
const TZ = "Europe/Helsinki";
export const today = (tz = TZ): Temporal.PlainDate =>
  Temporal.Instant.fromEpochMilliseconds(Date.now()).toZonedDateTimeISO(tz).toPlainDate();
const day = Temporal.Instant.from(apiTimestamp).toZonedDateTimeISO(TZ).toPlainDate();
```

Pass the zone explicitly. With no argument, `Temporal.Now.plainDateISO()` uses
the host zone, which on servers and CI runners is usually UTC. An unknown zone
ID throws `RangeError`.

## Keep calendar dates out of `Date`

The "UTC-midnight `Date`" pattern (`new Date("2026-06-15")` read back with
`getUTC*`) stays correct only while every caller remembers the UTC getters. A
single `getDate()`, `getDay()` or `toLocaleDateString()` reads it in local time,
and west of UTC the date moves back a day: with `TZ=America/New_York`,
`new Date("2026-06-15").getDate()` is `14`. Both getters return `number`, so
the type checker cannot see the mistake. A `PlainDate` has no time or zone to
misread.

## Store and transmit ISO strings

Keep ISO 8601 strings (`"2026-06-15"`, `"2026-06-15T09:00:00Z"`) in JSON,
databases, `chrome.storage` and URLs. Parse at the edge with
`Temporal.<Type>.from(str)`, emit with `.toString()`; `JSON.stringify` produces
the same string through `toJSON`. String input with an impossible date throws
(`PlainDate.from("2026-02-30")` is a `RangeError`), but property-bag input
clamps by default (`{ year: 2026, month: 2, day: 30 }` becomes `2026-02-28`),
so pass `{ overflow: "reject" }` when validating user input.

Interop with legacy `Date` and libraries happens at that same boundary:

| Direction | Call |
|---|---|
| `Date` to `Instant` | `date.toTemporalInstant()` |
| `Instant` to `Date` | `new Date(instant.epochMilliseconds)` |
| Epoch ms to `Instant` | `Temporal.Instant.fromEpochMilliseconds(ms)` |

## Porting from `Date`

| `Date` habit | Temporal |
|---|---|
| `getDay()`: 0 = Sunday … 6 = Saturday | `dayOfWeek`: 1 = Monday … 7 = Sunday; weekend is `d.dayOfWeek >= 6` |
| `getMonth()` is 0-based | `month` is 1-based |
| `a < b`, `a - b` | `Temporal.PlainDate.compare(a, b)`, `a.until(b)`; `<`, `-` and `"" + d` throw `TypeError` because `valueOf` throws |
| `a.getTime() === b.getTime()` | `a.equals(b)`; `===` compares object identity |
| `d.setDate(d.getDate() + 1)` mutates | `d.add({ days: 1 })` returns a new value |
| Jan 31 + 1 month rolls into March | `PlainDate.from("2026-01-31").add({ months: 1 })` is `2026-02-28` |

Template literals call `toString()` and are safe: `` `${date}` ``.

## Testing

A pinned test clock reaches `Temporal.Now` only if the fake-timer library fakes
it. `@sinonjs/fake-timers` added `Temporal.Now.*` faking in 15.4.0
(2026-05-05). Measured on Node 26 after pinning the clock to 2020-01-15:

| Runner | `Temporal.Now.instant()` |
|---|---|
| Vitest 4.1.4, 4.1.6, 4.1.11, `vi.useFakeTimers()` + `vi.setSystemTime()` | real time; only `Date` is pinned |
| Vitest 5.0.0 and 5.0.2 (5.0.0 release notes: "support mocking `Temporal`") | pinned |
| Jest 30.5.2, `jest.useFakeTimers({ now })` | pinned; `@jest/fake-timers` 30.4.0+ requires fake-timers `^15.4.0` |
| Bun 1.4.2, `setSystemTime()` | pinned |

An explicit `toFake` list that leaves out `"Temporal"` also leaves it real. On
runners that do not fake it, code that calls `Temporal.Now.*` directly ignores
the pinned clock and nothing fails loudly. The `today()` seam above reads
`Date.now()`, which every fake-timer version pins, so it behaves the same on
every runner; injecting a clock (`now: () => Temporal.Instant`) works as well.

`bun test` runs in UTC unless `TZ` is set. In UTC, UTC slicing and local
getters agree, so both off-by-one-day bugs above pass. Run date tests under a
zone east and a zone west of UTC.

## Availability

Native support per MDN browser-compat-data (8.1.3, 2026-09-24):

| Runtime | Native from |
|---|---|
| Chrome, Edge | 144 |
| Firefox | 139 |
| Safari | Technology Preview only; not in stable Safari or iOS |
| Node.js | 26.0.0 (earlier majors: polyfill) |
| Deno | 2.7 |
| Bun | 1.4.0 |

- **Older targets**: `temporal-polyfill` (FullCalendar, ~20 kB, defers to native
  Temporal when present). Import `"temporal-polyfill/global"` once at the entry
  point. MDN also lists `@js-temporal/polyfill`; its latest release (0.5.1,
  March 2025) predates Stage 4.
- **Chrome extensions**: set `"minimum_chrome_version": "144"` in the manifest.
  The Web Store then shows older Chrome "Not compatible" instead of the
  extension failing with `ReferenceError: Temporal is not defined`. Existing
  users on older Chrome stop receiving updates.
- **TypeScript**: types ship from TypeScript 6.0. Add `"ESNext"` to `lib`, or
  the granular `"esnext.temporal"` plus `"esnext.date"` (for
  `toTemporalInstant`). TypeScript 5.x has no Temporal types; with the polyfill,
  add `import "temporal-polyfill/types/global"`. The rest of the tsconfig
  belongs to `typescript-strict`.

## When `Date` still appears

New code that reaches for `new Date(`, `Date.now()`, moment, date-fns, dayjs or
luxon uses Temporal instead. The exception is an external API that takes or
returns `Date` (a library signature, `valueAsDate`, a database driver): convert
at that call site and keep Temporal on both sides. The `today()` seam's
`Date.now()` is the other exception. Migrate existing `Date` code
when it is touched for a date bug, not as a drive-by sweep.

## Agentic Optimizations

| Context | Command |
|---|---|
| Runtime has native Temporal | `node -p 'typeof Temporal'` (`object`) |
| Types resolve | `bunx tsc --noEmit` |
| Reproduce a zone bug | `TZ=America/New_York bun test` |

Find the patterns this skill replaces:

```bash
rg -n "toISOString\(\)\.(slice\(0, ?10\)|split\(['\"]T['\"]\))" src
rg -n 'new Date\(|Date\.now\(\)|from "(moment|date-fns|dayjs|luxon)' src
rg -n '\.get(Day|Date|Month|FullYear)\(\)' src
```

## References

- MDN Temporal: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal
- Proposal docs and cookbook: https://tc39.es/proposal-temporal/docs/
- `temporal-polyfill`: https://github.com/fullcalendar/temporal-polyfill
- `minimum_chrome_version`: https://developer.chrome.com/docs/extensions/reference/manifest/minimum-chrome-version

Attribution

laurigateslaurigates
View sourceSee grades on GitHubMore from laurigates →
SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Ship a skill? Prove it's safe.

Free 120-pattern security scan, letter grade, and an embeddable README badge.

Submit a skill

Related Skills

Clean Code

Pragmatic coding standards - concise, direct, no over-engineering, no unnecessary comments

304955 votes

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

286712 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2222 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Writing Plans

Use when you have a spec or requirements for a multi-step task, before touching code

2927051 votes
View all in development →