Skip to content

Conversation

@kbrilla
Copy link

@kbrilla kbrilla commented Jan 18, 2026

AI assistance disclosure

This work was implemented by AI tools (Claude Opus 4.5 and GPT-5.2-Codex) under my orchestration.

Summary

Add a Temporal-based DateAdapter for Angular Material datepicker, supporting date-only, date+time, and timezone-aware date+time use cases.

Fixes #25753

Background

Temporal is a Stage 3 TC39 proposal providing immutable date/time primitives with explicit calendar and timezone semantics. This adapter enables Material datepicker to work with Temporal (native or polyfilled) without relying on JS Date as the internal model. The implementation follows patterns from existing adapters in this repo, especially NativeDateAdapter.

What’s in this PR

Adapter(s)

  • Unified adapter: TemporalDateAdapter with mode:

    • dateTemporal.PlainDate
    • datetimeTemporal.PlainDateTime
    • zonedTemporal.ZonedDateTime
  • Split adapters (API ergonomics; supports common “plain vs zoned” consumption patterns). Both unified and split versions are included so the team can choose the preferred public API shape:

    • PlainTemporalAdapter with mode: 'date' | 'datetime' (PlainDate + PlainDateTime)
    • ZonedDateTimeAdapter with configurable timezone

Configuration

Unified adapter (TemporalDateAdapter / MatTemporalDateAdapterOptions)

  • calendar (default: iso8601): calendar system to use for calculations/formatting.
  • mode (default: date): selects PlainDate, PlainDateTime, or ZonedDateTime.
  • timezone (zoned mode only; default: system timezone): IANA ID like Europe/Warsaw or UTC.
  • firstDayOfWeek (default: locale-derived): overrides the locale-derived week start.
  • overflow (default: reject): strict vs constrained date creation.

Split adapters

PlainTemporalAdapterOptions (for PlainTemporalAdapter)

  • mode (default: datetime): date (PlainDate) or datetime (PlainDateTime).
  • calendar (default: iso8601): calendar system for calculations/formatting.
  • firstDayOfWeek (default: locale-derived): overrides the locale-derived week start.
  • overflow (default: reject): strict vs constrained date creation.

ZonedDateTimeAdapterOptions (for ZonedDateTimeAdapter)

  • calendar (default: iso8601): calendar system for calculations/formatting.
  • timezone (default: system timezone): IANA ID like Europe/Warsaw or UTC.
  • firstDayOfWeek (default: locale-derived): overrides the locale-derived week start.
  • overflow (default: reject): strict vs constrained date creation.

Usage examples

import {provideTemporalAdapter} from '@angular/material-temporal-adapter';

provideTemporalAdapter({mode: 'date'});
provideTemporalAdapter({mode: 'datetime'});
provideTemporalAdapter({mode: 'zoned'}); // default: system timezone
provideTemporalAdapter({mode: 'zoned', timezone: 'UTC'});
import {
  providePlainTemporalAdapter,
  provideZonedDateTimeAdapter,
} from '@angular/material-temporal-adapter/adapter/split';

providePlainTemporalAdapter({mode: 'date'});
providePlainTemporalAdapter({mode: 'datetime'});
provideZonedDateTimeAdapter({timezone: 'Europe/Warsaw'});

Comparison to other adapters

  • Follows the same DateAdapter contract as NativeDateAdapter, MomentDateAdapter, LuxonDateAdapter, and DateFnsAdapter.
  • Uses Intl.DateTimeFormatOptions for display formatting, like NativeDateAdapter, but the backing values are Temporal types instead of Date.
  • No new runtime dependency is added; consumers supply a Temporal implementation (native or polyfill).

Migration patterns

  • From NativeDateAdapter (Date): switch to provideTemporalDateAdapter({mode: 'date'}) and replace Date values with Temporal.PlainDate (e.g. Temporal.PlainDate.from('2024-01-15')).
  • From MomentDateAdapter/LuxonDateAdapter (date+time): use mode: 'datetime' and replace values with Temporal.PlainDateTime (e.g. Temporal.PlainDateTime.from('2024-01-15T12:30')).
  • From timezone-aware usage: use mode: 'zoned' and provide timezone (e.g. {mode: 'zoned', timezone: 'UTC'}), replacing values with Temporal.ZonedDateTime (e.g. Temporal.ZonedDateTime.from('2024-01-15T12:30[UTC]')).
  • Incremental/explicit type choice: use split adapters (PlainTemporalAdapter or ZonedDateTimeAdapter) to keep the date type explicit in APIs and templates.
  • If a Temporal polyfill is needed, load it before bootstrapping the app.
  • If you provide custom MAT_DATE_FORMATS, ensure they are Intl.DateTimeFormatOptions (Temporal ignores parse formats and parses ISO strings only). Supporting custom parse formats would require a custom parsing implementation in the adapter.

Example (custom display formats):

import {MatDateFormats} from '@angular/material/core';
import {provideTemporalDateAdapter, MAT_TEMPORAL_DATE_FORMATS} from '@angular/material-temporal-adapter';

const MY_TEMPORAL_FORMATS = {
  ...MAT_TEMPORAL_DATE_FORMATS,
  display: {
    ...MAT_TEMPORAL_DATE_FORMATS.display,
    dateInput: {year: 'numeric', monath: 'long', day: 'numeric'},
  },
} satisfies MatDateFormats;

bootstrapApplication(AppComponent, {
  providers: [provideTemporalDateAdapter(MY_TEMPORAL_FORMATS, {mode: 'date'})],
});

MAT token notes

  • MAT_DATE_LOCALE: locale string used for formatting and week info.
  • MAT_DATE_FORMATS: must be Intl.DateTimeFormatOptions (parse formats ignored).

Option mapping (approximate)

Existing adapter Option Temporal equivalent Notes
NativeDateAdapter n/a mode: 'date' Temporal PlainDate is the closest analogue to Date for date-only use.
MomentDateAdapter useUtc: true mode: 'zoned', timezone: 'UTC' Use zoned+UTC for UTC-centric workflows.
MomentDateAdapter strict: true overflow: 'reject' Temporal parsing is ISO-only; overflow controls invalid date creation.
LuxonDateAdapter useUtc: true mode: 'zoned', timezone: 'UTC' Similar UTC behavior.
LuxonDateAdapter firstDayOfWeek firstDayOfWeek Same semantics.
LuxonDateAdapter defaultOutputCalendar calendar Calendar used for formatting/operations.
DateFnsAdapter n/a MAT_DATE_LOCALE + MAT_DATE_FORMATS Temporal adapter also uses locale + formats; no adapter-level options.

Notable design choices / dilemmas (with resolution)

1) Temporal typings source (TypeScript not shipping them yet)

TypeScript does not currently ship lib.esnext.temporal in the version used here, so this PR includes local declarations to type the adapter until the repo upgrades to a TS version that includes Temporal libs.

Reference: microsoft/TypeScript#62628

2) API Extractor goldens

API Extractor had trouble resolving the global Temporal namespace in this setup. Additionally, existing adapter packages (moment/luxon/date-fns) don’t have API goldens. For consistency and to avoid introducing a blocked/flaky step, this PR does not add API golden coverage for this adapter package.

3) Formatting implementation note

Intl.DateTimeFormat is used for localization (same overall approach as other adapters). Zoned values are formatted using the configured timezone.

Tests

  • Unit tests cover all mode variants and timezone behavior, plus creation/parsing/formatting/time operations.
  • Ran: pnpm test src/material-temporal-adapter on Chrome/Chromium 144.
    • Result: 138 passed, 2 skipped (Islamic calendar suite skipped because islamic calendar is not supported by the current Temporal implementation).
  • Note: split adapters (PlainTemporalAdapter, ZonedDateTimeAdapter) are not explicitly covered by a dedicated spec yet.

Open questions (feedback requested)

  1. Calendar type: keep calendar as string (max flexibility) vs trying to constrain it (risk of being incomplete/locale-dependent).
  2. Positioning: should this adapter be documented as “ready” like other adapters, or called out as “early” given current ecosystem support for Temporal?
  3. Should mode be required for the unified adapter (no implicit default), or keep the default date mode?

@pullapprove pullapprove bot requested a review from devversion January 18, 2026 02:18
@angular-robot angular-robot bot added the detected: feature PR contains a feature commit label Jan 18, 2026
@kbrilla kbrilla force-pushed the temporal-adapter-25753 branch 5 times, most recently from 14730b7 to fdb2e45 Compare January 18, 2026 02:57
This adds a new date adapter implementation using the native Temporal API
(Stage 3 TC39 proposal). The adapter supports three Temporal date types:
- PlainDate: date-only operations
- PlainDateTime: date and time without timezone
- ZonedDateTime: date and time with timezone awareness

Two approaches are provided for flexibility:
1. Unified adapter (TemporalDateAdapter) with configurable mode
2. Split adapters (PlainDateAdapter, PlainDateTimeAdapter, ZonedDateTimeAdapter)
   for better tree-shaking

Key features:
- Native Temporal API integration (no polyfills required)
- Full calendar system support via Temporal.Calendar
- Configurable first day of week
- ISO 8601 serialization/deserialization
- Static frozen invalid date objects for memory efficiency
- No JS Date interop - Temporal types only

Closes angular#25753
@kbrilla kbrilla force-pushed the temporal-adapter-25753 branch from fdb2e45 to 12f2ea0 Compare January 18, 2026 02:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

detected: feature PR contains a feature commit

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(DATE): Add Temporal Adapter

1 participant