# Calendar

Calendars display a grid of days in one or more months and allow users to select a single date.

## Introduction

While the Gregorian calendar is the most common, many other calendar systems are used throughout the world. The `Calendar` interface represents calendar systems in the `@internationalized/date` library. It encapsulates information such as the number of days in a month, the number of months in a year, and the list of eras in a calendar system, as well as methods that handle correct arithmetic of dates in that calendar system, as well as converting dates between calendar systems. Many implementations of this interface are provided in `@internationalized/date` to handle the most commonly used calendar systems.

As described in the docs for [CalendarDate](CalendarDate.html#calendar-systems) and other date objects, you can pass a `Calendar` instance to a date to represent a date in that calendar. Date manipulation follows the rules defined by that calendar system. You can also convert between calendar systems using the `toCalendar` function.

```tsx
import {HebrewCalendar, GregorianCalendar, toCalendar} from '@internationalized/date';

let hebrewDate = new CalendarDate(new HebrewCalendar(), 5781, 1, 1);
toCalendar(hebrewDate, new GregorianCalendar());
// => new CalendarDate(new GregorianCalendar(), 2020, 9, 19);
```

### Calendar identifiers

While it is possible to construct `Calendar` objects manually, a common usecase is to get a calendar object for a certain locale. Each calendar has an associated string identifier that can be used to retrieve an instance of that calendar using the `createCalendar` function. A list of supported calendar identifiers is available [below](#implementations).

```tsx
import {createCalendar} from '@internationalized/date';

createCalendar('gregory');
createCalendar('hebrew');
createCalendar('japanese');
```

Locales are typically represented as strings such as `en-US`, and represent information about a user's preferences, such as language, script, number format, and calendar. Most of this is automatically determined based on data, but it can also be provided in the locale string itself via a [locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string). For example, the locale `"hi-IN-u-ca-indian"` represents the Hindi language, in the country of India, using the `indian` calendar.

The [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) object can be used to get the calendar identifier from a locale string, either provided explicitly or implicitly. This can then be passed to the `createCalendar` function to retrieve a `Calendar` instance.

```tsx
// Get the calendar identifier for the current user.
let calendarIdentifier = new Intl.DateTimeFormat().resolvedOptions().calendar; // e.g. 'gregory'
createCalendar(calendarIdentifier); // new GregorianCalendar()

// Language and region provided, calendar inferred.
let calendarIdentifier = new Intl.DateTimeFormat('th-TH').resolvedOptions().calendar; // 'buddhist'
createCalendar(calendarIdentifier); // new BuddhistCalendar()

// Calendar system set explicitly.
let calendarIdentifier = new Intl.DateTimeFormat('hi-IN-u-ca-indian').resolvedOptions().calendar; // 'indian'
createCalendar(calendarIdentifier); // new IndianCalendar()
```

**Note**: importing `createCalendar` into your project will result in all available calendars being included in your bundle. If you wish to limit the supported calendars to reduce bundle sizes, you can create your own implementation that only imports the desired classes. This way, your bundler can tree-shake the unused calendar implementations.

```tsx
import {GregorianCalendar, JapaneseCalendar} from '@internationalized/date';

function createCalendar(identifier) {
  switch (identifier) {
    case 'gregory':
      return new GregorianCalendar();
    case 'japanese':
      return new JapaneseCalendar();
    default:
      throw new Error(`Unsupported calendar ${identifier}`);
  }
}
```

## Implementations

| Class | Identifier | Description |
| ------ | ------ | ------ |
| GregorianCalendar | `'gregory'` | The Gregorian calendar is the most commonly used calendar system in the world. It supports two eras: BC, and AD.
Years always contain 12 months, and 365 or 366 days depending on whether it is a leap year. |
| BuddhistCalendar | `'buddhist'` | The Buddhist calendar is the same as the Gregorian calendar, but counts years
starting from the birth of Buddha in 543 BC (Gregorian). It supports only one
era, identified as 'BE'. |
| EthiopicCalendar | `'ethiopic'` | The Ethiopic calendar system is the official calendar used in Ethiopia.
It includes 12 months of 30 days each, plus 5 or 6 intercalary days depending
on whether it is a leap year. Two eras are supported: 'AA' and 'AM'. |
| EthiopicAmeteAlemCalendar | `'ethioaa'` | \[description] |
| CopticCalendar | `'coptic'` | \[description] |
| HebrewCalendar | `'hebrew'` | The Hebrew calendar is used in Israel and around the world by the Jewish faith.
Years include either 12 or 13 months depending on whether it is a leap year.
In leap years, an extra month is inserted at month 6. |
| IndianCalendar | `'indian'` | The Indian National Calendar is similar to the Gregorian calendar, but with
years numbered since the Saka era in 78 AD (Gregorian). There are 12 months
in each year, with either 30 or 31 days. Only one era identifier is supported: 'saka'. |
| IslamicCivilCalendar | `'islamic-civil'` | \[description] |
| IslamicTabularCalendar | `'islamic-tbla'` | \[description] |
| IslamicUmalquraCalendar | `'islamic-umalqura'` | \[description] |
| JapaneseCalendar | `'japanese'` | The Japanese calendar is based on the Gregorian calendar, but with eras for the reign of each Japanese emperor.
Whenever a new emperor ascends to the throne, a new era begins and the year starts again from 1.
Note that eras before 1868 (Gregorian) are not currently supported by this implementation. |
| PersianCalendar | `'persian'` | The Persian calendar is the main calendar used in Iran and Afghanistan. It has 12 months
in each year, the first 6 of which have 31 days, and the next 5 have 30 days. The 12th month
has either 29 or 30 days depending on whether it is a leap year. The Persian year starts
around the March equinox. |
| TaiwanCalendar | `'roc'` | The Taiwanese calendar is the same as the Gregorian calendar, but years
are numbered starting from 1912 (Gregorian). Two eras are supported:
'before\_minguo' and 'minguo'. |

## Interface

## Custom calendars

You can create your own custom calendar system by implementing the `Calendar` interface shown above. This enables calendars that follow custom business rules. An example would be a fiscal year calendar that follows a [4-5-4 format](https://nrf.com/resources/4-5-4-calendar), where month ranges don't follow the usual Gregorian calendar.

To implement a calendar, either extend an existing implementation (e.g. `GregorianCalendar`) or implement the `Calendar` interface from scratch. The most important methods are `fromJulianDay` and `toJulianDay`, which convert between the calendar's year/month/day numbering system and a [Julian Day Number](https://en.wikipedia.org/wiki/Julian_day). This allows converting dates between calendar systems. Other methods such as `getDaysInMonth` and `getMonthsInYear` can be implemented to define how dates are organized in your calendar system.

The following code is an example of how you might implement a custom 4-5-4 calendar (though implementing a true 4-5-4 calendar would be more nuanced than this).

```tsx
import type {AnyCalendarDate, Calendar} from '@internationalized/date';
import {CalendarDate, GregorianCalendar, startOfWeek} from '@internationalized/date';

const weekPattern = [4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4];

class Custom454 extends GregorianCalendar {
  // Months always have either 4 or 5 full weeks.
  getDaysInMonth(date) {
    return weekPattern[date.month - 1] * 7;
  }

  // Enable conversion between calendar systems.
  fromJulianDay(jd: number): CalendarDate {
    let gregorian = super.fromJulianDay(jd);

    // Start from the beginning of the first week of the gregorian year
    // and add weeks until we find the month.
    let monthStart = startOfWeek(new CalendarDate(gregorian.year, 1, 1), 'en');
    for (let months = 0; months < weekPattern.length; months++) {
      let weeksInMonth = weekPattern[months];
      let monthEnd = monthStart.add({weeks: weeksInMonth});
      if (monthEnd.compare(gregorian) > 0) {
        let days = gregorian.compare(monthStart);
        return new CalendarDate(this, monthStart.year, months + 1, days + 1);
      }
      monthStart = monthEnd;
    }

    throw Error('Date is not in any month somehow!');
  }

  toJulianDay(date: AnyCalendarDate): number {
    let monthStart = startOfWeek(new CalendarDate(date.year, 1, 1), 'en');
    for (let month = 1; month < date.month; month++) {
      monthStart = monthStart.add({weeks: weekPattern[month - 1]});
    }

    let gregorian = monthStart.add({days: date.day - 1});
    return super.toJulianDay(gregorian);
  }

  isEqual(other: Calendar) {
    return other instanceof Custom454;
  }
}
```

This enables dates to be converted between calendar systems.

```tsx
import {GregorianCalendar, toCalendar} from '@internationalized/date';

let date = new CalendarDate(new Custom454(), 2024, 2, 1);
let gregorianDate = toCalendar(date, new GregorianCalendar());
// => new CalendarDate(new GregorianCalendar(), 2024, 1, 29);
```

## Related Types

### toCalendar

### createCalendar

Creates a \`Calendar\` instance from a Unicode calendar identifier string.
