사용 가능한 달력 시스템 목록 가져오기

JavaScript 환경에서 지원하는 달력 시스템 확인하기

소개

전 세계 사용자를 대상으로 애플리케이션을 구축할 때, 사용자는 선호하는 달력 시스템으로 날짜를 보고 싶어 합니다. 대부분의 서구 국가에서 사용되는 그레고리력에 익숙할 수 있지만, 많은 문화권에서는 이슬람 히즈라력, 히브리력 또는 불교력과 같이 완전히 다른 달력 시스템을 사용합니다.

사용자가 선호하는 달력을 선택할 수 있도록 하려면 JavaScript 환경에서 지원하는 달력 시스템을 알아야 합니다. 오래되거나 지원되지 않는 값을 포함하는 하드코딩된 목록을 유지하는 대신, JavaScript는 런타임에 사용 가능한 달력 시스템을 검색하는 메서드를 제공합니다.

"calendar" 매개변수를 사용하는 Intl.supportedValuesOf() 메서드는 현재 환경에서 지원되는 모든 달력 시스템 식별자의 배열을 반환합니다. 이를 통해 애플리케이션이 올바르게 작동하는 달력 옵션만 제공할 수 있습니다.

달력 시스템이란

달력 시스템은 시간을 구성하고 계산하는 다양한 방법입니다. 모든 달력은 일, 월, 년을 추적하지만, 년이 시작되는 시기, 월의 길이, 천문학적 주기를 설명하는 방법에 대해 서로 다른 규칙을 사용합니다.

1582년에 도입된 그레고리력은 가장 널리 사용되는 민간 달력입니다. 약 365.25일의 태양년을 사용하며 12개월로 나뉘고, 4년마다 윤년이 추가됩니다(세기년에 대한 예외 포함).

다른 달력 시스템은 다른 규칙을 따릅니다. 이슬람력은 순수한 음력으로, 월이 달의 위상을 따릅니다. 이는 이슬람 년이 태양년보다 약 11일 짧다는 것을 의미하며, 시간이 지남에 따라 이슬람 날짜가 계절을 통해 이동하게 됩니다. 히브리력과 중국력은 태음태양력으로, 음력 월과 태양년에 맞춰 달력을 동기화하는 조정을 결합합니다.

JavaScript에서 날짜 형식을 지정할 때, 달력 시스템은 어떤 연도, 월, 일 숫자가 표시될지 결정합니다. 동일한 시점이 서로 다른 달력 시스템에서는 다른 날짜 표현을 갖습니다.

Intl.supportedValuesOf를 사용하여 달력 시스템 가져오기

Intl.supportedValuesOf() 메서드는 반환할 값의 유형을 지정하는 문자열 매개변수를 받습니다. 달력 시스템을 가져오려면 "calendar"를 전달하세요:

const calendars = Intl.supportedValuesOf("calendar");
console.log(calendars);
// Output: ["buddhist", "chinese", "coptic", "dangi", "ethioaa",
//          "ethiopic", "gregory", "hebrew", "indian", "islamic",
//          "islamic-civil", "islamic-rgsa", "islamic-tbla",
//          "islamic-umalqura", "iso8601", "japanese", "persian", "roc"]

이 메서드는 달력 식별자를 나타내는 문자열 배열을 반환합니다. 이러한 식별자는 유니코드 CLDR(Common Locale Data Repository) 표준을 따르며, 이는 다양한 플랫폼과 프로그래밍 언어에서 달력 시스템을 참조하는 일관된 방법을 제공합니다.

반환된 배열은 다음과 같은 특성을 갖습니다:

  • 값은 오름차순 알파벳 순서로 정렬됩니다
  • 중복 값은 제거됩니다
  • 각 식별자는 소문자와 하이픈을 사용합니다
  • 목록에는 JavaScript 구현에서 지원하는 모든 달력 시스템이 포함됩니다

브라우저와 JavaScript 환경마다 지원하는 달력 세트가 다르지만, 모든 최신 브라우저는 공통 달력의 핵심 세트를 지원합니다.

달력 식별자 이해하기

각 달력 식별자는 하나 이상의 문화권에서 사용되는 특정 달력 시스템을 나타냅니다. 다음은 가장 일반적인 식별자입니다:

"gregory" 식별자는 대부분의 국가에서 사용되는 표준 민간 달력인 그레고리력을 나타냅니다. 미국, 유럽 또는 세계 대부분의 지역에 거주하는 경우 일상생활에서 사용하는 달력입니다.

"buddhist" 식별자는 태국 불교력을 나타내며, 그레고리력과 동일한 월 및 일 구조를 사용하지만 부처의 탄생(그레고리력 기원전 543년)부터 연도를 계산합니다. 그레고리력의 2025년은 불교력의 2568년입니다.

"chinese" 식별자는 전통 중국 달력을 나타내며, 월은 달의 위상을 따르고 연도는 목성의 공전 주기와 일치하는 태음태양력입니다. 중국 달력은 춘절과 같은 전통 명절을 결정하는 데 사용됩니다.

"islamic" 식별자는 이슬람 히즈라력을 나타내며, 29일 또는 30일로 구성된 12개월의 순수 태음력입니다. 연도는 서기 622년 무함마드가 메카에서 메디나로 이주한 히즈라부터 계산됩니다.

"hebrew" 식별자는 유대교 종교 의식에 사용되는 태음태양력인 히브리력을 나타냅니다. 전통적인 창조 날짜(그레고리력 기원전 3761년)부터 연도를 계산합니다.

"japanese" 식별자는 일본력을 나타내며, 그레고리력과 동일한 월 및 일 구조를 사용하지만 재위 중인 천황을 기준으로 시대를 구분합니다. 현재 시대는 2019년에 시작된 레이와입니다.

"persian" 식별자는 이란과 아프가니스탄에서 사용되는 태양 히즈라력을 나타냅니다. 히즈라부터 연도를 계산하는 태양력으로, 음력 이슬람력과는 다릅니다.

추가 식별자로는 "coptic"(콥트 정교회력), "dangi"(전통 한국력), "ethiopic"(에티오피아력), "indian"(인도 국가력), "roc"(대만에서 사용되는 중화민국력)가 있습니다.

일부 식별자에는 "islamic-civil", "islamic-rgsa", "islamic-tbla", "islamic-umalqura"와 같은 변형이 있으며, 이는 이슬람력의 서로 다른 계산 방법을 나타냅니다.

"iso8601" 식별자는 ISO 8601 달력을 나타내며, 본질적으로 그레고리력이지만 항상 선제적 그레고리력을 사용합니다(1582년 도입 이전으로 그레고리력을 소급 적용).

달력 시스템 실제 사용 예시

달력 시스템이 날짜 형식에 어떻게 영향을 미치는지 이해하려면 다른 달력을 사용하여 동일한 날짜를 형식화하세요:

const date = new Date("2025-10-15");

const gregorian = new Intl.DateTimeFormat("en-US", {
  calendar: "gregory",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const islamic = new Intl.DateTimeFormat("en-US", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const hebrew = new Intl.DateTimeFormat("en-US", {
  calendar: "hebrew",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const buddhist = new Intl.DateTimeFormat("en-US", {
  calendar: "buddhist",
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(gregorian.format(date));
// Output: "October 15, 2025"

console.log(islamic.format(date));
// Output: "Rabi' II 16, 1447 AH"

console.log(hebrew.format(date));
// Output: "Tishrei 23, 5786"

console.log(buddhist.format(date));
// Output: "October 15, 2568 BE"

동일한 JavaScript Date 객체는 동일한 시점을 나타내지만, 각 달력 시스템은 해당 시점을 서로 다른 연도, 월, 일 값으로 표현합니다. 그레고리력 날짜 2025년 10월 15일은 이슬람력으로 1447년 라비 알 아키르 16일, 히브리력으로 5786년 티슈레이 23일, 불교력으로 2568년 10월 15일에 해당합니다.

이는 다른 달력을 따르는 사용자를 위해 날짜를 형식화할 때 어떤 달력 시스템을 사용할지 지정해야 하는 이유를 보여줍니다.

달력 선택기 구축

사용자가 선호하는 달력을 선택할 수 있는 사용자 인터페이스를 만들 때는 사용 가능한 달력을 쿼리하고 선택기를 동적으로 구축하세요:

function buildCalendarSelector() {
  const calendars = Intl.supportedValuesOf("calendar");
  const select = document.createElement("select");
  select.id = "calendar-selector";

  calendars.forEach(calendar => {
    const option = document.createElement("option");
    option.value = calendar;
    option.textContent = calendar;
    select.appendChild(option);
  });

  return select;
}

const selector = buildCalendarSelector();
document.body.appendChild(selector);

이렇게 하면 지원되는 모든 달력이 포함된 드롭다운이 생성됩니다. 그러나 "gregory""islamic"와 같은 기술적 식별자는 사용자 친화적이지 않습니다. 사용자는 자신의 언어로 된 설명적인 이름을 볼 필요가 있습니다.

사람이 읽을 수 있는 달력 이름 표시

Intl.DisplayNames API는 달력 식별자를 사람이 읽을 수 있는 이름으로 변환합니다. 이를 사용하여 더 나은 달력 선택기를 만드세요:

function buildCalendarSelector(locale = "en-US") {
  const calendars = Intl.supportedValuesOf("calendar");
  const displayNames = new Intl.DisplayNames([locale], { type: "calendar" });

  const select = document.createElement("select");
  select.id = "calendar-selector";

  calendars.forEach(calendar => {
    const option = document.createElement("option");
    option.value = calendar;
    option.textContent = displayNames.of(calendar);
    select.appendChild(option);
  });

  return select;
}

const selector = buildCalendarSelector("en-US");
document.body.appendChild(selector);

이제 드롭다운에는 기술적 식별자 대신 "그레고리력", "이슬람력", "히브리력"과 같은 이름이 표시됩니다.

로케일을 변경하여 다른 언어로 달력 이름을 표시할 수 있습니다:

const calendars = ["gregory", "islamic", "hebrew", "buddhist", "chinese"];

const englishNames = new Intl.DisplayNames(["en-US"], { type: "calendar" });
const frenchNames = new Intl.DisplayNames(["fr-FR"], { type: "calendar" });
const arabicNames = new Intl.DisplayNames(["ar-SA"], { type: "calendar" });

calendars.forEach(calendar => {
  console.log(`${calendar}:`);
  console.log(`  English: ${englishNames.of(calendar)}`);
  console.log(`  French: ${frenchNames.of(calendar)}`);
  console.log(`  Arabic: ${arabicNames.of(calendar)}`);
});

// Output:
// gregory:
//   English: Gregorian Calendar
//   French: calendrier grégorien
//   Arabic: التقويم الميلادي
// islamic:
//   English: Islamic Calendar
//   French: calendrier musulman
//   Arabic: التقويم الهجري
// ...

이를 통해 사용자는 선호하는 언어로 달력 이름을 볼 수 있습니다.

특정 달력이 지원되는지 확인

애플리케이션에서 달력을 사용하기 전에 JavaScript 환경이 이를 지원하는지 확인하세요. 이렇게 하면 런타임 오류를 방지하고 대체 동작을 구현할 수 있습니다:

function isCalendarSupported(calendar) {
  const supported = Intl.supportedValuesOf("calendar");
  return supported.includes(calendar);
}

if (isCalendarSupported("islamic")) {
  const formatter = new Intl.DateTimeFormat("en-US", {
    calendar: "islamic",
    year: "numeric",
    month: "long",
    day: "numeric"
  });
  console.log(formatter.format(new Date()));
} else {
  console.log("Islamic calendar is not supported");
}

이 패턴은 모든 달력 식별자에 적용됩니다. 사용자 인터페이스에서 달력 옵션을 표시하거나 숨기거나, 선호하는 달력을 사용할 수 없을 때 기본 달력으로 대체하는 데 사용할 수 있습니다.

재사용 가능한 기능 감지 함수 만들기

달력 지원을 확인하고 누락된 달력을 우아하게 처리하는 범용 함수를 구축하세요:

function getCalendarOrFallback(preferredCalendar, fallbackCalendar = "gregory") {
  const supported = Intl.supportedValuesOf("calendar");

  if (supported.includes(preferredCalendar)) {
    return preferredCalendar;
  }

  if (supported.includes(fallbackCalendar)) {
    return fallbackCalendar;
  }

  return supported[0];
}

const calendar = getCalendarOrFallback("islamic", "gregory");
const formatter = new Intl.DateTimeFormat("en-US", {
  calendar,
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(formatter.format(new Date()));

이 함수는 선호하는 달력을 사용하려고 시도하고, 선호하는 달력을 사용할 수 없는 경우 지정된 대체 달력으로 폴백하며, 궁극적으로 둘 다 지원되지 않는 경우 사용 가능한 첫 번째 달력을 반환합니다.

애플리케이션에 맞는 달력 필터링

대부분의 애플리케이션은 모든 달력 시스템을 지원할 필요가 없습니다. 사용자와 관련된 달력만 포함하도록 목록을 필터링하세요:

function getRelevantCalendars(allowedCalendars) {
  const supported = Intl.supportedValuesOf("calendar");
  return allowedCalendars.filter(calendar => supported.includes(calendar));
}

const allowedCalendars = ["gregory", "islamic", "hebrew", "buddhist"];
const availableCalendars = getRelevantCalendars(allowedCalendars);

console.log(availableCalendars);
// Output: ["gregory", "islamic", "hebrew", "buddhist"]
// (assuming all are supported in the current environment)

이렇게 하면 요구 사항을 충족하고 사용자의 브라우저에서 작동하는 달력만 제공할 수 있습니다.

이를 달력 선택과 결합하여 집중된 사용자 인터페이스를 만들 수 있습니다:

function buildFilteredCalendarSelector(allowedCalendars, locale = "en-US") {
  const supported = Intl.supportedValuesOf("calendar");
  const available = allowedCalendars.filter(cal => supported.includes(cal));
  const displayNames = new Intl.DisplayNames([locale], { type: "calendar" });

  const select = document.createElement("select");

  available.forEach(calendar => {
    const option = document.createElement("option");
    option.value = calendar;
    option.textContent = displayNames.of(calendar);
    select.appendChild(option);
  });

  return select;
}

const selector = buildFilteredCalendarSelector(
  ["gregory", "islamic", "hebrew", "buddhist"],
  "en-US"
);
document.body.appendChild(selector);

이렇게 하면 지원하려는 달력만 표시하는 선택기가 생성되며, 사람이 읽을 수 있는 이름으로 현재 환경에서 작동이 보장됩니다.

카테고리별로 달력 그룹화

많은 달력을 지원하는 애플리케이션의 경우 유형이나 지역별로 그룹화하여 사용성을 개선하세요:

function groupCalendars() {
  const calendars = Intl.supportedValuesOf("calendar");

  const groups = {
    solar: ["gregory", "iso8601", "persian", "ethiopic", "coptic"],
    lunar: ["islamic", "islamic-civil", "islamic-rgsa", "islamic-tbla", "islamic-umalqura"],
    lunisolar: ["hebrew", "chinese", "dangi"],
    erasBased: ["japanese", "roc", "buddhist"],
    other: []
  };

  const grouped = {
    solar: [],
    lunar: [],
    lunisolar: [],
    erasBased: [],
    other: []
  };

  calendars.forEach(calendar => {
    let placed = false;

    for (const [group, members] of Object.entries(groups)) {
      if (members.includes(calendar)) {
        grouped[group].push(calendar);
        placed = true;
        break;
      }
    }

    if (!placed) {
      grouped.other.push(calendar);
    }
  });

  return grouped;
}

const grouped = groupCalendars();
console.log(grouped);
// Output:
// {
//   solar: ["gregory", "iso8601", "persian", "ethiopic", "coptic"],
//   lunar: ["islamic", "islamic-civil", "islamic-rgsa", ...],
//   lunisolar: ["hebrew", "chinese", "dangi"],
//   erasBased: ["japanese", "roc", "buddhist"],
//   other: ["ethioaa", "indian"]
// }

이러한 구성은 사용자가 다양한 달력 시스템의 특성을 이해하고 필요한 달력을 찾는 데 도움이 됩니다.

지원되지 않는 환경 처리

Intl.supportedValuesOf() 메서드는 2022년에 JavaScript에 추가되었습니다. 이전 브라우저는 이를 지원하지 않습니다. 사용하기 전에 메서드가 존재하는지 확인하세요:

function getCalendars() {
  if (typeof Intl.supportedValuesOf === "function") {
    return Intl.supportedValuesOf("calendar");
  }

  return ["gregory"];
}

const calendars = getCalendars();
console.log(calendars);

이렇게 하면 최신 브라우저에서는 지원되는 달력의 전체 목록을 반환하고, 이전 환경에서는 그레고리력만 폴백합니다.

더 나은 하위 호환성을 위해 더 완전한 폴백 목록을 제공하세요:

function getCalendars() {
  if (typeof Intl.supportedValuesOf === "function") {
    return Intl.supportedValuesOf("calendar");
  }

  return [
    "buddhist",
    "chinese",
    "coptic",
    "ethiopic",
    "gregory",
    "hebrew",
    "indian",
    "islamic",
    "japanese",
    "persian",
    "roc"
  ];
}

이렇게 하면 이전 브라우저에 대해 합리적인 달력 세트를 제공하지만, 모든 환경에서 모든 달력이 작동한다고 보장할 수는 없습니다.

달력과 로케일의 차이점 이해하기

달력과 로케일은 관련이 있지만 별개의 개념입니다. 로케일은 언어와 지역별 형식 규칙을 결정하고, 달력은 사용할 달력 체계를 결정합니다.

하나의 로케일에서 여러 달력을 사용할 수 있습니다. 예를 들어, 사우디아라비아의 아랍어 사용자는 일반적으로 종교적 목적으로는 이슬람력을, 민간 목적으로는 그레고리력을 사용합니다. 아랍어로 두 달력 중 하나를 사용하여 날짜를 형식화할 수 있습니다:

const date = new Date("2025-10-15");

const arabicGregorian = new Intl.DateTimeFormat("ar-SA", {
  calendar: "gregory",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const arabicIslamic = new Intl.DateTimeFormat("ar-SA", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(arabicGregorian.format(date));
// Output: "١٥ أكتوبر ٢٠٢٥"

console.log(arabicIslamic.format(date));
// Output: "١٦ ربيع الآخر ١٤٤٧ هـ"

둘 다 아랍어 텍스트와 숫자를 사용하지만, 각 달력에 따라 다른 날짜를 표시합니다.

반대로, 동일한 달력을 다른 언어로 형식화할 수 있습니다:

const date = new Date("2025-10-15");

const englishIslamic = new Intl.DateTimeFormat("en-US", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

const arabicIslamic = new Intl.DateTimeFormat("ar-SA", {
  calendar: "islamic",
  year: "numeric",
  month: "long",
  day: "numeric"
});

console.log(englishIslamic.format(date));
// Output: "Rabi' II 16, 1447 AH"

console.log(arabicIslamic.format(date));
// Output: "١٦ ربيع الآخر ١٤٤٧ هـ"

둘 다 이슬람력 날짜를 표시하지만, 다른 언어와 숫자 체계를 사용합니다.

사용 가능한 달력을 조회해야 하는 경우

다음과 같은 상황에서 사용 가능한 달력을 조회하세요:

달력 선택 인터페이스를 구축할 때 Intl.supportedValuesOf("calendar")를 호출하여 옵션을 채우세요. 이렇게 하면 현재 환경에서 작동하는 달력만 표시할 수 있습니다.

달력 기반 기능을 구현할 때 필요한 달력이 지원되는지 확인한 후 사용하세요. 이렇게 하면 오류를 방지하고 대체 달력으로 원활하게 전환할 수 있습니다.

사용자 기본 설정을 저장할 때 지원되는 목록에 대해 달력 선택을 검증하세요. 이렇게 하면 저장된 기본 설정이 다양한 기기와 브라우저에서 유효하게 유지됩니다.

환경 간 마이그레이션 시 소스 환경과 대상 환경 모두에서 달력 지원을 확인하세요. 달력 지원은 브라우저 버전, Node.js 버전 및 다양한 JavaScript 런타임에 따라 달라질 수 있습니다.

달력 종속 데이터를 로드할 때 날짜를 파싱하거나 형식화하기 전에 달력을 사용할 수 있는지 확인하세요. 이렇게 하면 특정 달력 체계에서 날짜를 처리할 때 런타임 오류를 방지할 수 있습니다.