CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-temporal-polyfill

A lightweight polyfill for Temporal, successor to the JavaScript Date object

Overall
score

96%

Evaluation96%

1.19x

Agent success when using this tile

Overview
Eval results
Files

intl-formatting.mddocs/

International Formatting

Enhanced Intl.DateTimeFormat class with native Temporal type support for formatting dates and times. Provides seamless integration between Temporal objects and internationalization features.

Capabilities

Enhanced DateTimeFormat

Extended version of Intl.DateTimeFormat that supports all Temporal types in addition to legacy Date objects.

/**
 * Enhanced DateTimeFormat class supporting Temporal types
 */
class DateTimeFormat extends Intl.DateTimeFormat {
  /** Creates a new DateTimeFormat instance */
  constructor(locales?: LocalesArgument, options?: Intl.DateTimeFormatOptions);

  /** Formats a Temporal object or Date as a string */
  format(date: TemporalFormattable | Date): string;

  /** Formats a Temporal object or Date into an array of parts */
  formatToParts(date: TemporalFormattable | Date): Intl.DateTimeFormatPart[];

  /** Formats a range between two Temporal objects */
  formatRange(startDate: TemporalFormattable, endDate: TemporalFormattable): string;

  /** Formats a range between two Temporal objects into an array of parts */
  formatRangeToParts(startDate: TemporalFormattable, endDate: TemporalFormattable): Intl.DateTimeFormatPart[];

  /** Returns the supported locales */
  static supportedLocalesOf(locales: LocalesArgument, options?: Intl.SupportedLocalesOptions): string[];
}

Supported Temporal Types

The enhanced DateTimeFormat supports all major Temporal types for formatting.

/**
 * Union type of all Temporal objects that can be formatted
 */
type TemporalFormattable =
  | Instant
  | ZonedDateTime
  | PlainDateTime
  | PlainDate
  | PlainTime
  | PlainYearMonth
  | PlainMonthDay;

type LocalesArgument = string | string[] | undefined;

Formatting Individual Temporal Types

Format specific Temporal types with locale-appropriate output.

Usage Examples:

import { Temporal, Intl } from "temporal-polyfill";

// Create formatter instances
const dateFormatter = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
});

const timeFormatter = new Intl.DateTimeFormat('en-US', {
  hour: 'numeric',
  minute: '2-digit',
  second: '2-digit',
  timeZoneName: 'short'
});

const dateTimeFormatter = new Intl.DateTimeFormat('en-US', {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: 'numeric',
  minute: '2-digit',
  timeZone: 'America/New_York'
});

// Format PlainDate
const plainDate = Temporal.PlainDate.from('2024-03-15');
console.log(dateFormatter.format(plainDate)); // "March 15, 2024"

// Format PlainTime
const plainTime = Temporal.PlainTime.from('14:30:00');
console.log(timeFormatter.format(plainTime)); // "2:30:00 PM GMT"

// Format PlainDateTime
const plainDateTime = Temporal.PlainDateTime.from('2024-03-15T14:30:00');
console.log(dateTimeFormatter.format(plainDateTime)); // "Friday, March 15, 2024 at 2:30 PM EST"

// Format ZonedDateTime
const zonedDateTime = Temporal.ZonedDateTime.from('2024-03-15T14:30:00[America/New_York]');
console.log(dateTimeFormatter.format(zonedDateTime)); // "Friday, March 15, 2024 at 2:30 PM EDT"

// Format Instant
const instant = Temporal.Now.instant();
console.log(dateTimeFormatter.format(instant)); // Current time in New York timezone

// Format PlainYearMonth
const yearMonthFormatter = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long'
});
const yearMonth = Temporal.PlainYearMonth.from('2024-03');
console.log(yearMonthFormatter.format(yearMonth)); // "March 2024"

// Format PlainMonthDay
const monthDayFormatter = new Intl.DateTimeFormat('en-US', {
  month: 'long',
  day: 'numeric'
});
const monthDay = Temporal.PlainMonthDay.from('03-15');
console.log(monthDayFormatter.format(monthDay)); // "March 15"

Localized Formatting

Format Temporal objects in different locales with appropriate cultural conventions.

Usage Examples:

import { Temporal, Intl } from "temporal-polyfill";

const dateTime = Temporal.ZonedDateTime.from('2024-03-15T14:30:00[Europe/Paris]');

// Different locales
const locales = ['en-US', 'fr-FR', 'de-DE', 'ja-JP', 'ar-SA'];

const formatters = locales.map(locale => ({
  locale,
  formatter: new Intl.DateTimeFormat(locale, {
    weekday: 'long',
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
    timeZoneName: 'short'
  })
}));

formatters.forEach(({ locale, formatter }) => {
  console.log(`${locale}: ${formatter.format(dateTime)}`);
});

// Output examples:
// en-US: Friday, March 15, 2024 at 2:30 PM CET
// fr-FR: vendredi 15 mars 2024 à 14:30 CET
// de-DE: Freitag, 15. März 2024 um 14:30 CET
// ja-JP: 2024年3月15日金曜日 14:30 CET
// ar-SA: الجمعة، ١٥ مارس ٢٠٢٤ في ٢:٣٠ م CET

// Currency and number formatting in dates
const businessFormatter = new Intl.DateTimeFormat('en-US', {
  month: 'short',
  day: 'numeric',
  year: 'numeric',
  hour: 'numeric',
  minute: '2-digit',
  timeZone: 'America/New_York'
});

const businessDate = Temporal.ZonedDateTime.from('2024-03-15T16:30:00[America/New_York]');
console.log(`Report generated: ${businessFormatter.format(businessDate)}`);
// "Report generated: Mar 15, 2024 at 4:30 PM"

Format to Parts

Get detailed formatting parts for custom display and analysis.

Usage Examples:

import { Temporal, Intl } from "temporal-polyfill";

const dateTime = Temporal.ZonedDateTime.from('2024-03-15T14:30:00[America/New_York]');

const formatter = new Intl.DateTimeFormat('en-US', {
  weekday: 'long',
  year: 'numeric',
  month: 'long',
  day: 'numeric',
  hour: 'numeric',
  minute: '2-digit',
  timeZoneName: 'short'
});

const parts = formatter.formatToParts(dateTime);
console.log(parts);

// Example output:
// [
//   { type: 'weekday', value: 'Friday' },
//   { type: 'literal', value: ', ' },
//   { type: 'month', value: 'March' },
//   { type: 'literal', value: ' ' },
//   { type: 'day', value: '15' },
//   { type: 'literal', value: ', ' },
//   { type: 'year', value: '2024' },
//   { type: 'literal', value: ' at ' },
//   { type: 'hour', value: '2' },
//   { type: 'literal', value: ':' },
//   { type: 'minute', value: '30' },
//   { type: 'literal', value: ' ' },
//   { type: 'dayPeriod', value: 'PM' },
//   { type: 'literal', value: ' ' },
//   { type: 'timeZoneName', value: 'EDT' }
// ]

// Use parts for custom formatting
function formatWithHighlight(temporal: TemporalFormattable): string {
  const parts = formatter.formatToParts(temporal);
  return parts
    .map(part => {
      if (part.type === 'weekday' || part.type === 'month') {
        return `<strong>${part.value}</strong>`;
      }
      return part.value;
    })
    .join('');
}

console.log(formatWithHighlight(dateTime));
// "<strong>Friday</strong>, <strong>March</strong> 15, 2024 at 2:30 PM EDT"

Range Formatting

Format ranges between two Temporal objects with intelligent range handling.

Usage Examples:

import { Temporal, Intl } from "temporal-polyfill";

const rangeFormatter = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'short',
  day: 'numeric',
  hour: 'numeric',
  minute: '2-digit',
  timeZone: 'America/New_York'
});

// Date range (same day)
const startTime = Temporal.ZonedDateTime.from('2024-03-15T14:30:00[America/New_York]');
const endTime = Temporal.ZonedDateTime.from('2024-03-15T16:00:00[America/New_York]');

console.log(rangeFormatter.formatRange(startTime, endTime));
// "Mar 15, 2024, 2:30 PM – 4:00 PM"

// Date range (different days)
const startDate = Temporal.PlainDate.from('2024-03-15');
const endDate = Temporal.PlainDate.from('2024-03-17');

const dateRangeFormatter = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'short',
  day: 'numeric'
});

console.log(dateRangeFormatter.formatRange(startDate, endDate));
// "Mar 15 – 17, 2024"

// Month range
const startMonth = Temporal.PlainYearMonth.from('2024-01');
const endMonth = Temporal.PlainYearMonth.from('2024-03');

const monthRangeFormatter = new Intl.DateTimeFormat('en-US', {
  year: 'numeric',
  month: 'long'
});

console.log(monthRangeFormatter.formatRange(startMonth, endMonth));
// "January – March 2024"

// Range parts for custom formatting
const rangeParts = rangeFormatter.formatRangeToParts(startTime, endTime);
console.log(rangeParts);

// Example output:
// [
//   { type: 'month', value: 'Mar', source: 'startRange' },
//   { type: 'literal', value: ' ', source: 'startRange' },
//   { type: 'day', value: '15', source: 'startRange' },
//   { type: 'literal', value: ', ', source: 'startRange' },
//   { type: 'year', value: '2024', source: 'startRange' },
//   { type: 'literal', value: ', ', source: 'startRange' },
//   { type: 'hour', value: '2', source: 'startRange' },
//   { type: 'literal', value: ':', source: 'startRange' },
//   { type: 'minute', value: '30', source: 'startRange' },
//   { type: 'literal', value: ' ', source: 'startRange' },
//   { type: 'dayPeriod', value: 'PM', source: 'startRange' },
//   { type: 'literal', value: ' – ', source: 'shared' },
//   { type: 'hour', value: '4', source: 'endRange' },
//   { type: 'literal', value: ':', source: 'endRange' },
//   { type: 'minute', value: '00', source: 'endRange' },
//   { type: 'literal', value: ' ', source: 'endRange' },
//   { type: 'dayPeriod', value: 'PM', source: 'endRange' }
// ]

Business Applications

Common patterns for using Intl formatting in business applications.

Usage Examples:

import { Temporal, Intl } from "temporal-polyfill";

// Meeting scheduler with international support
class MeetingScheduler {
  private readonly formatter: Intl.DateTimeFormat;

  constructor(locale: string, timeZone: string) {
    this.formatter = new Intl.DateTimeFormat(locale, {
      weekday: 'long',
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      hour: 'numeric',
      minute: '2-digit',
      timeZone
    });
  }

  formatMeeting(
    startTime: Temporal.ZonedDateTime,
    duration: Temporal.Duration
  ): string {
    const endTime = startTime.add(duration);
    return this.formatter.formatRange(startTime, endTime);
  }

  formatMeetingInvite(
    startTime: Temporal.ZonedDateTime,
    duration: Temporal.Duration,
    attendeeTimeZones: string[]
  ): string[] {
    return attendeeTimeZones.map(tz => {
      const localStart = startTime.withTimeZone(tz);
      const localEnd = localStart.add(duration);
      const localFormatter = new Intl.DateTimeFormat(undefined, {
        weekday: 'long',
        year: 'numeric',
        month: 'long',
        day: 'numeric',
        hour: 'numeric',
        minute: '2-digit',
        timeZone: tz,
        timeZoneName: 'short'
      });
      return localFormatter.formatRange(localStart, localEnd);
    });
  }
}

// Report generation with localization
class ReportGenerator {
  generateTimeStampedReport(
    data: any,
    locale: string = 'en-US',
    timeZone: string = Temporal.Now.timeZoneId()
  ): object {
    const now = Temporal.Now.zonedDateTimeISO(timeZone);
    const formatter = new Intl.DateTimeFormat(locale, {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      hour: 'numeric',
      minute: '2-digit',
      second: '2-digit',
      timeZoneName: 'short'
    });

    return {
      generatedAt: formatter.format(now),
      timeZone,
      locale,
      data
    };
  }

  formatDateRange(
    startDate: Temporal.PlainDate,
    endDate: Temporal.PlainDate,
    locale: string = 'en-US'
  ): string {
    const formatter = new Intl.DateTimeFormat(locale, {
      year: 'numeric',
      month: 'short',
      day: 'numeric'
    });
    return formatter.formatRange(startDate, endDate);
  }
}

// Event calendar with multiple time zones
class EventCalendar {
  formatEventForTimezone(
    event: {
      start: Temporal.ZonedDateTime,
      end: Temporal.ZonedDateTime,
      title: string
    },
    targetTimeZone: string,
    locale: string = 'en-US'
  ): string {
    const localStart = event.start.withTimeZone(targetTimeZone);
    const localEnd = event.end.withTimeZone(targetTimeZone);

    const formatter = new Intl.DateTimeFormat(locale, {
      month: 'short',
      day: 'numeric',
      hour: 'numeric',
      minute: '2-digit',
      timeZone: targetTimeZone,
      timeZoneName: 'short'
    });

    const timeRange = formatter.formatRange(localStart, localEnd);
    return `${event.title}: ${timeRange}`;
  }
}

Types

// Type definitions for international formatting
type TemporalFormattable =
  | Instant
  | ZonedDateTime
  | PlainDateTime
  | PlainDate
  | PlainTime
  | PlainYearMonth
  | PlainMonthDay;

type LocalesArgument = string | string[] | undefined;

// Enhanced DateTimeFormat interface
interface DateTimeFormat extends Intl.DateTimeFormat {
  format(date: TemporalFormattable | Date): string;
  formatToParts(date: TemporalFormattable | Date): Intl.DateTimeFormatPart[];
  formatRange(startDate: TemporalFormattable, endDate: TemporalFormattable): string;
  formatRangeToParts(startDate: TemporalFormattable, endDate: TemporalFormattable): Intl.DateTimeFormatPart[];
}

// Standard Intl.DateTimeFormatPart with range source information
interface DateTimeFormatPart extends Intl.DateTimeFormatPart {
  source?: 'startRange' | 'endRange' | 'shared';
}

// Standard Intl.DateTimeFormatOptions (extended for Temporal)
interface DateTimeFormatOptions extends Intl.DateTimeFormatOptions {
  // All standard Intl.DateTimeFormatOptions are supported
  // when formatting Temporal objects
}

// Extended Intl namespace
interface IntlExtended extends Intl {
  DateTimeFormat: DateTimeFormat;
}

// Common format part types
type FormatPartType =
  | 'era'
  | 'year'
  | 'month'
  | 'day'
  | 'weekday'
  | 'hour'
  | 'minute'
  | 'second'
  | 'timeZoneName'
  | 'dayPeriod'
  | 'literal'
  | 'fractionalSecond';

Install with Tessl CLI

npx tessl i tessl/npm-temporal-polyfill

docs

calendar-system.md

current-time.md

date-time-classes.md

duration-arithmetic.md

index.md

instant-zoned-datetime.md

intl-formatting.md

tile.json