Back

Форматирование дат и чисел с помощью Intl API

Форматирование дат и чисел с помощью Intl API

Вы уже использовали Intl.DateTimeFormat и Intl.NumberFormat раньше. Но если ваше представление об этих API сформировалось несколько лет назад, вы, вероятно, упускаете значительные возможности — особенно в части управления округлением в Intl.NumberFormat и того, как Temporal и Intl работают вместе в рантаймах конца 2025 года.

Эта статья предоставляет обновлённый технический обзор интернационализации в JavaScript через Intl API, фокусируясь на том, что изменилось и что опытные разработчики часто понимают неправильно.

Ключевые выводы

  • Intl API форматирует значения в строки с учётом локали, но не парсит, не вычисляет и не хранит данные — держите представление отдельно от бизнес-логики приложения.
  • Intl.NumberFormat теперь поддерживает расширенные возможности управления округлением, включая roundingMode, roundingIncrement и trailingZeroDisplay.
  • Типы Temporal обрабатывают собственное форматирование через toLocaleString(), в то время как Intl.DateTimeFormat продолжает форматировать объекты Date.
  • Всегда переиспользуйте экземпляры форматтеров для производительности и избегайте жёстко заданных строк ожидаемого вывода в тестах.

Что на самом деле делает Intl

Пространство имён Intl обрабатывает форматирование значений с учётом локали. Оно не парсит, не вычисляет и не манипулирует данными — оно преобразует значения в читаемые человеком строки в соответствии с культурными конвенциями.

Два критически важных момента:

  1. Intl форматирует; он не хранит и не вычисляет. Бизнес-логика вашего приложения остаётся отдельной от представления.
  2. Вывод варьируется в зависимости от рантайма. Базовые данные локалей поступают из библиотек ICU, встроенных в браузеры и Node.js. Точные строки могут немного отличаться между Chrome и Safari или между версиями Node.

Никогда не задавайте жёстко ожидаемые строки вывода в тестах. Вместо этого используйте formatToParts() или структурные утверждения.

Intl.DateTimeFormat: за пределами базовых опций

Форматирование объектов Date

Intl.DateTimeFormat остаётся стандартным способом форматирования JavaScript-объектов Date:

const formatter = new Intl.DateTimeFormat('de-DE', {
  dateStyle: 'long',
  timeStyle: 'short',
  timeZone: 'Europe/Berlin'
});

formatter.format(new Date()); // "27. Juni 2025 um 14:30"

Опции dateStyle и timeStyle предоставляют предустановленные конфигурации. Когда вам нужен детальный контроль, используйте индивидуальные опции, такие как weekday, month, hour и timeZoneName.

Типы Temporal и форматирование с учётом локали

Temporal активно внедряется в современные движки JavaScript, но пока ещё не имеет широкой поддержки во всех браузерах и окружениях. Там, где он доступен, Temporal.PlainDate, Temporal.ZonedDateTime и другие типы Temporal форматируют сами себя через toLocaleString(), а не передаются напрямую в Intl.DateTimeFormat.prototype.format().

const date = Temporal.PlainDate.from('2025-06-27');
date.toLocaleString('ja-JP', { dateStyle: 'full' });
// "2025年6月27日金曜日"

Intl.DateTimeFormat принимает объекты Date. Типы Temporal обрабатывают собственную логику форматирования и могут внутренне делегировать поведение, чувствительное к локали, но они не форматируются самим Intl.DateTimeFormat. Это различие важно при проектировании API, принимающих входные данные с датами.

Диапазоны дат с formatRange

Для отображения диапазонов дат используйте formatRange():

const start = new Date(2025, 5, 27);
const end = new Date(2025, 6, 3);

new Intl.DateTimeFormat('en-US', { dateStyle: 'medium' })
  .formatRange(start, end);
// "Jun 27 – Jul 3, 2025"

Форматтер интеллектуально сворачивает избыточные части на основе конвенций локали.

Управление округлением и отображением в Intl.NumberFormat

Современные опции округления

Поведение округления в Intl.NumberFormat значительно расширилось в последних спецификациях, при этом поддержка варьируется в зависимости от рантайма. Помимо minimumFractionDigits и maximumFractionDigits, теперь у вас есть:

  • roundingMode: Управляет способом округления значений (ceil, floor, halfExpand, halfEven и т.д.)
  • roundingIncrement: Округляет до конкретных приращений (5, 10, 50 и т.д.)
  • trailingZeroDisplay: Управляет отображением конечных нулей (auto или stripIfInteger)
const formatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD',
  roundingMode: 'halfEven',
  maximumFractionDigits: 2
});

formatter.format(2.225); // "$2.22" (банковское округление)
formatter.format(2.235); // "$2.24"

Паттерны форматирования чисел с учётом локали

Для компактной нотации и форматирования единиц измерения:

// Компактная нотация
new Intl.NumberFormat('en-US', {
  notation: 'compact',
  compactDisplay: 'short'
}).format(1234567); // "1.2M"

// Форматирование единиц измерения
new Intl.NumberFormat('de-DE', {
  style: 'unit',
  unit: 'kilometer-per-hour',
  unitDisplay: 'short'
}).format(120); // "120 km/h"

Практические соображения

Переиспользуйте экземпляры форматтеров

Создание форматтеров имеет накладные расходы. Кэшируйте их при форматировании множества значений:

// Делайте так
const priceFormatter = new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
});
prices.map(p => priceFormatter.format(p));

// Не так
prices.map(p => new Intl.NumberFormat('en-US', {
  style: 'currency',
  currency: 'USD'
}).format(p));

Определение поддержки возможностей

Проверяйте наличие новых опций программно:

try {
  new Intl.NumberFormat('en', { roundingMode: 'halfEven' });
  // Возможность поддерживается
} catch (e) {
  // Откат к округлению по умолчанию
}

Локаль vs. Часовой пояс

Это независимые концепции. Локаль (en-GB) определяет конвенции форматирования. Часовой пояс (Europe/London) определяет отображаемое время на часах. Вы можете форматировать дату в немецких конвенциях, показывая при этом токийское время.

Заключение

Intl API для форматирования дат и чисел с учётом локали значительно повзрослел. Спецификация теперь включает режимы округления, элементы управления отображением и форматирование диапазонов, которые устраняют большинство причин для использования внешних библиотек.

Типы Temporal существуют наряду с Intl — они обрабатывают собственные вызовы toLocaleString(), в то время как Intl.DateTimeFormat продолжает форматировать объекты Date. Постройте своё представление вокруг этого разделения, тестируйте без жёстко заданных строковых ожиданий и переиспользуйте экземпляры форматтеров для производительности.

Часто задаваемые вопросы

Нет. Intl.DateTimeFormat.prototype.format() принимает только объекты Date. Типы Temporal, такие как PlainDate и ZonedDateTime, имеют собственные методы toLocaleString(). Они не форматируются самим Intl.DateTimeFormat, даже если могут использовать аналогичные данные локалей внутренне.

Intl полагается на данные локалей ICU, встроенные в каждый рантайм. Chrome, Safari, Firefox и Node.js могут поставляться с разными версиями ICU с небольшими вариациями в выводе. Избегайте жёсткого задания ожидаемых строк в тестах. Используйте formatToParts() или структурные утверждения для надёжной проверки поведения форматирования.

Банковское округление (halfEven) округляет промежуточные значения к ближайшей чётной цифре, уменьшая накопительное смещение округления. Оно обычно используется в финансовых и бухгалтерских контекстах, но точные результаты всё ещё могут быть затронуты представлением чисел с плавающей точкой в двоичной системе.

Попытайтесь создать форматтер с включённой опцией. Если рантайм не поддерживает её, он может выбросить RangeError или молча проигнорировать опцию, в зависимости от движка. Всегда включайте резервную стратегию.

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay