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

calendar-system.mddocs/

Calendar System

Specialized classes for working with calendar-specific date components like year-month and month-day combinations. These classes handle partial date representations and calendar-aware operations.

Capabilities

PlainYearMonth

Represents a year and month combination in a calendar system. Ideal for monthly reports, subscription periods, or any year-month specific operations.

/**
 * Represents a year and month combination in a calendar
 */
class PlainYearMonth {
  /** Creates a PlainYearMonth from various inputs */
  static from(item: PlainYearMonthLike | string, options?: AssignmentOptions): PlainYearMonth;
  /** Compares two year-months and returns -1, 0, or 1 */
  static compare(one: PlainYearMonthLike, two: PlainYearMonthLike): ComparisonResult;

  // Year-month component properties
  readonly calendarId: string;
  readonly year: number;
  readonly month: number;
  readonly monthCode: string;
  readonly era: string | undefined;
  readonly eraYear: number | undefined;

  // Calendar-computed properties
  readonly daysInMonth: number;
  readonly daysInYear: number;
  readonly monthsInYear: number;
  readonly inLeapYear: boolean;

  // Year-month manipulation methods
  /** Returns a new PlainYearMonth with the duration added */
  add(duration: DurationLike, options?: ArithmeticOptions): PlainYearMonth;
  /** Returns a new PlainYearMonth with the duration subtracted */
  subtract(duration: DurationLike, options?: ArithmeticOptions): PlainYearMonth;
  /** Returns a new PlainYearMonth with the specified fields changed */
  with(yearMonthLike: PlainYearMonthLike, options?: AssignmentOptions): PlainYearMonth;

  // Comparison and difference methods
  /** Returns the duration from this year-month until the other year-month */
  until(other: PlainYearMonthLike, options?: DifferenceOptions<YearMonthUnit>): Duration;
  /** Returns the duration from the other year-month since this year-month */
  since(other: PlainYearMonthLike, options?: DifferenceOptions<YearMonthUnit>): Duration;
  /** Tests if this year-month equals another year-month */
  equals(other: PlainYearMonthLike): boolean;

  // Conversion methods
  /** Combines this year-month with a day to create a PlainDate */
  toPlainDate(day: PlainDateLike): PlainDate;

  // String representation methods
  /** Returns a locale-formatted string representation */
  toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
  /** Returns an ISO 8601 string representation */
  toString(options?: ShowCalendarOption): string;
  /** Returns a JSON representation (same as toString) */
  toJSON(): string;
  /** Throws a TypeError when used in comparison operators */
  valueOf(): never;
}

Usage Examples:

import { Temporal } from "temporal-polyfill";

// Creating year-months
const currentMonth = Temporal.Now.plainDateISO().toPlainYearMonth();
const fromString = Temporal.PlainYearMonth.from('2024-03');
const fromObject = Temporal.PlainYearMonth.from({ year: 2024, month: 3 });

// Monthly operations
const nextMonth = currentMonth.add({ months: 1 });
const lastYear = currentMonth.subtract({ years: 1 });
const sameMonthNextYear = currentMonth.with({ year: currentMonth.year + 1 });

// Calendar information
console.log(currentMonth.daysInMonth);   // e.g., 31 for March
console.log(currentMonth.inLeapYear);    // true/false
console.log(currentMonth.monthsInYear);  // 12 for Gregorian calendar

// Creating full dates from year-month
const firstOfMonth = currentMonth.toPlainDate({ day: 1 });
const lastOfMonth = currentMonth.toPlainDate({ day: currentMonth.daysInMonth });

// Monthly comparisons
const monthsBetween = fromString.until(nextMonth, { largestUnit: 'month' });
const isEarlier = Temporal.PlainYearMonth.compare(fromString, nextMonth) < 0;

// Business use cases
function getQuarterStart(yearMonth: Temporal.PlainYearMonth): Temporal.PlainYearMonth {
  const quarterMonth = Math.floor((yearMonth.month - 1) / 3) * 3 + 1;
  return yearMonth.with({ month: quarterMonth });
}

function getMonthlyReport(yearMonth: Temporal.PlainYearMonth) {
  const startOfMonth = yearMonth.toPlainDate({ day: 1 });
  const endOfMonth = yearMonth.toPlainDate({ day: yearMonth.daysInMonth });
  return { startOfMonth, endOfMonth, daysInMonth: yearMonth.daysInMonth };
}

PlainMonthDay

Represents a month and day combination in a calendar system. Perfect for recurring events like birthdays, anniversaries, or holidays.

/**
 * Represents a month and day combination in a calendar
 */
class PlainMonthDay {
  /** Creates a PlainMonthDay from various inputs */
  static from(item: PlainMonthDayLike | string, options?: AssignmentOptions): PlainMonthDay;

  // Month-day component properties
  readonly calendarId: string;
  readonly monthCode: string;
  readonly day: number;

  // Month-day manipulation methods
  /** Returns a new PlainMonthDay with the specified fields changed */
  with(monthDayLike: PlainMonthDayLike, options?: AssignmentOptions): PlainMonthDay;

  // Comparison methods
  /** Tests if this month-day equals another month-day */
  equals(other: PlainMonthDayLike): boolean;

  // Conversion methods
  /** Combines this month-day with a year to create a PlainDate */
  toPlainDate(year: PlainYearMonthLike): PlainDate;

  // String representation methods
  /** Returns a locale-formatted string representation */
  toLocaleString(locales?: string | string[], options?: Intl.DateTimeFormatOptions): string;
  /** Returns an ISO 8601 string representation */
  toString(options?: ShowCalendarOption): string;
  /** Returns a JSON representation (same as toString) */
  toJSON(): string;
  /** Throws a TypeError when used in comparison operators */
  valueOf(): never;
}

Usage Examples:

import { Temporal } from "temporal-polyfill";

// Creating month-days
const birthday = Temporal.PlainMonthDay.from('06-15'); // June 15
const fromObject = Temporal.PlainMonthDay.from({ month: 12, day: 25 }); // Christmas
const fromDate = Temporal.PlainDate.from('2024-03-15').toPlainMonthDay();

// Creating specific year dates from month-day
const thisYear = birthday.toPlainDate({ year: 2024 });
const nextYear = birthday.toPlainDate({ year: 2025 });

// Holiday and recurring event handling
const holidays = [
  Temporal.PlainMonthDay.from('01-01'), // New Year's Day
  Temporal.PlainMonthDay.from('07-04'), // Independence Day
  Temporal.PlainMonthDay.from('12-25'), // Christmas
];

function getHolidaysForYear(year: number): Temporal.PlainDate[] {
  return holidays.map(holiday => holiday.toPlainDate({ year }));
}

function isHoliday(date: Temporal.PlainDate): boolean {
  const monthDay = date.toPlainMonthDay();
  return holidays.some(holiday => holiday.equals(monthDay));
}

// Age calculation on birthday
function getNextBirthday(
  birthday: Temporal.PlainMonthDay,
  referenceDate: Temporal.PlainDate = Temporal.Now.plainDateISO()
): Temporal.PlainDate {
  let birthdayThisYear = birthday.toPlainDate({ year: referenceDate.year });

  if (Temporal.PlainDate.compare(birthdayThisYear, referenceDate) < 0) {
    // Birthday already passed this year, get next year's
    birthdayThisYear = birthday.toPlainDate({ year: referenceDate.year + 1 });
  }

  return birthdayThisYear;
}

// Leap year handling
const feb29 = Temporal.PlainMonthDay.from('02-29');

function getLeapDayForYear(year: number): Temporal.PlainDate | null {
  try {
    return feb29.toPlainDate({ year });
  } catch {
    return null; // Not a leap year
  }
}

// Subscription management
class RecurringEvent {
  constructor(
    public readonly monthDay: Temporal.PlainMonthDay,
    public readonly description: string
  ) {}

  getNextOccurrence(after: Temporal.PlainDate = Temporal.Now.plainDateISO()): Temporal.PlainDate {
    let occurrence = this.monthDay.toPlainDate({ year: after.year });

    if (Temporal.PlainDate.compare(occurrence, after) <= 0) {
      occurrence = this.monthDay.toPlainDate({ year: after.year + 1 });
    }

    return occurrence;
  }

  getOccurrenceInYear(year: number): Temporal.PlainDate {
    return this.monthDay.toPlainDate({ year });
  }
}

const anniversary = new RecurringEvent(
  Temporal.PlainMonthDay.from('06-01'),
  'Company Anniversary'
);

Calendar-Aware Operations

Working with different calendar systems and handling calendar-specific behavior.

// Calendar interface (for reference)
interface Calendar {
  readonly id: string;
  dateFromFields(fields: DateLike, options?: AssignmentOptions): PlainDate;
  yearMonthFromFields(fields: YearMonthLike, options?: AssignmentOptions): PlainYearMonth;
  monthDayFromFields(fields: MonthDayLike, options?: AssignmentOptions): PlainMonthDay;
  dateAdd(date: PlainDate, duration: Duration, options?: ArithmeticOptions): PlainDate;
  dateUntil(one: PlainDate, two: PlainDate, options?: DifferenceOptions): Duration;
  year(date: PlainDate): number;
  month(date: PlainDate): number;
  monthCode(date: PlainDate): string;
  day(date: PlainDate): number;
  era(date: PlainDate): string | undefined;
  eraYear(date: PlainDate): number | undefined;
  dayOfWeek(date: PlainDate): number;
  dayOfYear(date: PlainDate): number;
  weekOfYear(date: PlainDate): number;
  daysInWeek(date: PlainDate): number;
  daysInMonth(date: PlainDate): number;
  daysInYear(date: PlainDate): number;
  monthsInYear(date: PlainDate): number;
  inLeapYear(date: PlainDate): boolean;
  toString(): string;
  toJSON(): string;
}

Usage Examples:

import { Temporal } from "temporal-polyfill";

// Working with different calendars (theoretical - actual calendar support varies)
const gregorianYM = Temporal.PlainYearMonth.from('2024-03');
const isoYearMonth = gregorianYM.withCalendar('iso8601');

// Month code handling (important for non-Gregorian calendars)
const marchMD = Temporal.PlainMonthDay.from({ monthCode: 'M03', day: 15 });
console.log(marchMD.monthCode); // 'M03'

// Era-aware operations (for calendars with eras)
const historicalYM = Temporal.PlainYearMonth.from({
  era: 'ce',
  eraYear: 2024,
  month: 3
});

// Calendar property access
function getCalendarInfo(yearMonth: Temporal.PlainYearMonth): object {
  return {
    calendar: yearMonth.calendarId,
    year: yearMonth.year,
    month: yearMonth.month,
    monthCode: yearMonth.monthCode,
    era: yearMonth.era,
    eraYear: yearMonth.eraYear,
    daysInMonth: yearMonth.daysInMonth,
    daysInYear: yearMonth.daysInYear,
    monthsInYear: yearMonth.monthsInYear,
    inLeapYear: yearMonth.inLeapYear
  };
}

Types

// Core type definitions for calendar system classes
type PlainYearMonthLike = PlainYearMonth | PlainYearMonthFields | string;
type PlainMonthDayLike = PlainMonthDay | PlainMonthDayFields | string;
type CalendarLike = string | Calendar;

interface PlainYearMonthFields {
  era?: string;
  eraYear?: number;
  year: number;
  month?: number;
  monthCode?: string;
  calendar?: CalendarLike;
}

interface PlainMonthDayFields {
  monthCode?: string;
  month?: number;
  day: number;
  calendar?: CalendarLike;
}

// Unit types for year-month operations
type YearMonthUnit = 'year' | 'month';

// Options for calendar system operations
interface ShowCalendarOption {
  calendarName?: 'auto' | 'always' | 'never' | 'critical';
}

interface AssignmentOptions {
  overflow?: OverflowMode;
}

interface ArithmeticOptions {
  overflow?: OverflowMode;
}

interface DifferenceOptions<T extends string> {
  largestUnit?: T;
  smallestUnit?: T;
  roundingIncrement?: number;
  roundingMode?: RoundingMode;
}

// Common types
type OverflowMode = 'constrain' | 'reject';
type RoundingMode = 'ceil' | 'floor' | 'expand' | 'trunc' | 'halfCeil' | 'halfFloor' | 'halfExpand' | 'halfTrunc' | 'halfEven';
type ComparisonResult = -1 | 0 | 1;

// Date-like interface for toPlainDate operations
interface DateLike {
  era?: string;
  eraYear?: number;
  year?: number;
  month?: number;
  monthCode?: string;
  day?: number;
  calendar?: CalendarLike;
}

interface YearMonthLike {
  era?: string;
  eraYear?: number;
  year?: number;
  month?: number;
  monthCode?: string;
  calendar?: CalendarLike;
}

interface MonthDayLike {
  monthCode?: string;
  month?: number;
  day?: number;
  calendar?: CalendarLike;
}

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