Работа с датами и временем: альтернативы Moment.js

Одним из наиболее важных элементов при настройке приложения для использования на разных языках является форматирование даты. Никто не хочет работать с объектом Date и в конечном итоге сломать их; вместо этого они ищут готовые библиотеки, позволяющие легко управлять датами.

Moment.Js — одна из самых популярных сред JavaScript для форматирования и изменения дат, библиотека дат для JavaScript, которая может анализировать, проверять, манипулировать и форматировать даты. Однако некоторые аспекты этой библиотеки, такие как ее размер или организационный стиль, могут заставить вас задаться вопросом, доступны ли какие-либо лучшие варианты.

В этой статье мы рассмотрим пять заменителей Moment.js для интернационализации даты:

API интернационализации JavaScript

Пространство имен ECMAScript Internationalization API’s представлено глобальным объектом intl. Этот объект имеет следующие конструкторы для дат:

  • Форматирование даты и времени определяется Intl.DateTimeFormat().
  • Даты и временные метки могут быть выражены с помощью чувствительных к языку, удобочитаемых слов, определенных Intl.RelativeTimeFormat().
  • Объекты с конструктором Intl.Locale() представляют собой идентификатор локали Unicode.
  • Конструктор Intl.NumberFormat() для объектов делает возможным форматирование чисел с учетом языка.

Эти конструкторы принимают два необязательных ввода: языковой стандарт и объект, содержащий параметры для изменения вывода. Например:

const timeFormat = new Intl.RelativeTimeFormat("de-DE");
const dateFormat = new Intl.DateTimeFormat("da");

Согласно BCP 47, параметр локали представляет собой строку, предоставляющую тег языка, который состоит из следующих компонентов:

  • Код языка (ISO 639–1/639–2).
  • Код скрипта (ISO 15924).
  • Код страны (ISO 3166).
  • Вариант (от iana.org), ищите «Тип: вариант»).
  • Расширения (из Юникода, подробнее здесь).

Среди вариантов, предоставляемых Intl.DateTimeFormat, есть формат даты ( full, long, medium и short), использование 12- или 24-часового времени, а также способ форматирования представления различных аспектов дня, таких как год, месяц, день недели и т.

Все параметры, которые можно использовать для настройки этого объекта, более подробно описаны на странице документации Intl.DateTimeFormat.

Объект имеет только следующие поля для Intl.RelativeTimeFormat:

  • Рекомендуемый алгоритм сопоставления языковых стандартов — localeMatcher. Lookup (от наиболее конкретного к наименее специфичному; если en-us недоступен, выбирается en) и best fit — два возможных значения (значение по умолчанию, если en-us недоступно, можно выбрать что-то вроде en-uk).
  • форматировать выходное сообщение как числовое. Единственными двумя вариантами являются always (например, 2 hours ago) или auto, которые иногда запрещают вывод числовых чисел (например, yesterday).
  • style для форматирования длины выходного сообщения. Long, short и narrow являются потенциальными значениями.

Методы format() и formatToParts() могут форматировать дату после того, как у вас есть объект типа Intl.DateTimeFormat или Intl.RelativeTimeFormat. Последняя функция возвращает массив, содержащий части вывода.

При использовании Intl.DateTimeFormat методы форматирования используют объект Date в качестве входных данных:

const date = new Date(Date.UTC(2022, 7, 30, 3, 5, 0));
const options = {
  hour12: true,
  day: "numeric",
  month: "long",
  year: "2-digit",
  minute: "2-digit",
  second: "2-digit",
};
// Sample output: 30 Αυγούστου 22, 05:00
console.log(new Intl.DateTimeFormat("el", options).format(date));
// Sample output: 30. August 22, 05:00
console.log(new Intl.DateTimeFormat("de-AT", options).format(date));

Если вы введете только несколько элементов даты и времени в объект параметров, вы увидите следующее на выходе:

const date = new Date(Date.UTC(2022, 7, 30, 3, 5, 0));
const options = {
  day: "numeric",
  year: "2-digit",
};
// Sample output: 22 30
console.log(new Intl.DateTimeFormat("el", options).format(date));

Функция format() для Intl.RelativeTimeFormat требует два аргумента: число, которое должно быть включено в сообщение, и его единица измерения (например, year или second в единственном или множественном числе):

const options = {
  localeMatcher: "best fit",
  numeric: "auto",
  style: "short",
};
// Output: sem. ant.
console.log(new Intl.RelativeTimeFormat("es-ES", options).format(-1, "week"));
// Output: 上個月
console.log(new Intl.RelativeTimeFormat("zh-TW", options).format(-1, "month"));

Еще одно различие между использованием значений always и auto для числового свойства заключается в следующем:

// Output: in 1 day
console.log(
  new Intl.RelativeTimeFormat("en", { numeric: "always" }).format(1, "day")
);
// Output: tomorrow
console.log(
  new Intl.RelativeTimeFormat("en", { numeric: "auto" }).format(1, "day")
);

Вы можете попробовать изменить все приведенные выше примеры; однако у вас могут возникнуть проблемы с браузером.
Современные браузеры обеспечивают хорошую поддержку большинства Intl.DateTimeFormat, но только Chrome 71 и Firefox 70 полностью поддерживают Intl.RelativeTimeFormat (в настоящее время ни Safari, ни Edge его не поддерживают).

Day.js

Day.Js — это облегченная библиотека, заменяющая Moment.js. Day.js по умолчанию использует локаль американского английского. Другие местоположения должны быть импортированы следующим образом:

<script src= "path/to/dayjs/locale/de"></script>
    <script>
      dayjs.locale('de') // use locale globally
      dayjs().locale('de').format() // use locale in a specific instance
    </script>

Метод format() в приведенном выше примере возвращает строку, содержащую отформатированную дату. Чтобы отформатировать дату определенным образом, можно использовать строку, включающую токены:

// Sample output: September 2022, Freitag
console.log(dayjs().locale(localeDe).format("MMMM YYYY, dddd"));

Дополнительный функционал Day.js в основном обеспечивается плагинами, которые вы можете загрузить в соответствии с вашими требованиями. Плагин UTC, например, включает следующие методы для получения даты в формате UTC и по местному времени:

let utc = require("dayjs/plugin/utc");
dayjs.extend(utc);

dayjs().format(); //2022-09-03T17:11:55+08:00

dayjs.utc().format(); // 2022-09-03T09:11:55Z

Плагины advancedFormat, localizedFormat, relativeTime и Calendar можно использовать для интернационализации.

Метод format() теперь имеет больше вариантов форматирования благодаря плагинам advancedFormat и localizedFormat (вы можете просмотреть все параметры на странице документации плагинов):

let advancedFormat = require("dayjs/plugin/advancedFormat");
dayjs.extend(advancedFormat);

dayjs().format("Q Do k kk X x");

let localizedFormat = require("dayjs/plugin/localizedFormat");
dayjs.extend(localizedFormat);

dayjs().format("L LT");

let relativeTime = require("dayjs/plugin/relativeTime");
dayjs.extend(relativeTime);

dayjs().from(dayjs("1999-01-01")); // in 23 years

Для отображения календарного времени плагин Calendar вводит метод календаря (с интервалом в семь дней). Вывод не локализован:

let calendar = require("dayjs/plugin/calendar");
dayjs.extend(calendar);

dayjs().calendar(dayjs("2022-09-03"));
dayjs().calendar(null, {
  sameDay: "[Today at] h:mm A", // The same day ( Today at 2:30 AM )
  nextDay: "[Tomorrow at] h:mm A", // The next day ( Tomorrow at 2:30 AM )
  nextWeek: "dddd [at] h:mm A", // The next week ( Sunday at 2:30 AM )
  lastDay: "[Yesterday at] h:mm A", // The day before ( Yesterday at 2:30 AM )
  lastWeek: "[Last] dddd [at] h:mm A", // Last week ( Last Monday at 2:30 AM )
  sameElse: "DD/MM/YYYY", // Everything else ( 17/10/2025 )
});

Люксон

Luxon был разработан одним из сопровождающих Moment и включает в себя многие из его принципов с внесением конкретных улучшений. Luxon можно считать оболочкой интернационализации для Intl.DateTimeFormat и Intl.RelativeTimeFormat.

Например, для форматирования дат при соблюдении локали используйте метод toFormat(fmt:string, opts: Object) и токены даты и времени из этой таблицы после предварительной установки локали:

// Sample output: 2022 августа
console.log(luxon.DateTime.local().setLocale("ru").toFormat("yyyy MMMM"));

Объект параметров, который метод принимает в качестве аргумента, также может содержать локаль:

// Output: 2021 октября
console.log(
  luxon.DateTime.local(2021, 10).toFormat("yyyy MMMM", { locale: "ru" })
);

Кроме того, вы можете указать локаль во время создания, если вы используете такие методы, как Object, ISO, Format, HTTP или RFC2822:

const date = luxon.DateTime.fromISO("2021-10-19", { locale: "it" });
// Output: 2021 ottobre 19
console.log(date.toFormat("yyyy MMMM dd"));

Однако рекомендуется использовать методы toLocaleString() и toLocaleParts(), которые создают массив, содержащий различные компоненты строки и локализованную строку, представляющую дату соответственно.

Эти методы используют одни и те же параметры (вместе с некоторыми пресетами, такими как DateTime.DATE_SHORT), что делает их похожими на методы Intl.DateTimeFormat format() и formatToParts() среди прочих).

const date = luxon.DateTime.utc(2021, 10, 1, 9, 3, 1);
const options = {
  hour12: true,
  day: "numeric",
  month: "long",
  year: "2-digit",
  minute: "2-digit",
  second: "2-digit",
};
// Output: 1 octobre 21, 03:01
console.log(date.setLocale("fr").toLocaleString(options));
// Output: 1. Oktober 21, 03:01
console.log(date.setLocale("de-AT").toLocaleString(options));
/* Output: [{"type": "day", "value": "1"},{"type": "literal", "value":" "},{"type": "month", "value": "ottobre"},{"type": "literal", "value":" "},{"type": "year", "value": "21"},{"type": "literal", "value": ","},{"type": "minute", "value": "03"},{"type": "literal", "value": ":"},{"type": "second", "value": "01"}] */
console.log(
  JSON.stringify(date.setLocale("it").toLocaleParts(options), null, 3)
);
// Output: 9:03 AM
console.log(date.toLocaleString(luxon.DateTime.TIME_SIMPLE));
// Output: 10/01/2021
console.log(date.toLocaleString({ locale: "pt" }));

Единственными методами, которые обеспечивают функциональность, аналогичную Intl.RelativeTimeFormat, являются toRelative (который по умолчанию возвращает строковое представление заданного времени относительно текущего момента) и toRelativeCalendar (который по умолчанию возвращает строковое представление заданной даты относительно сегодняшнего дня).

// Sample output: 0 seconds ago
console.log(luxon.DateTime.local().plus({ days: 0 }).toRelative());
// Sample output: today
console.log(luxon.DateTime.local().plus({ days: 0 }).toRelativeCalendar());

Единственная проблема с указанными выше способами заключается в том, что они не будут локализованы, в отличие от Intl.RelativeTimeFormat, если ваш браузер не поддерживает заявленный API.

Повтор сеанса для разработчиков

Раскройте разочарования, выявите ошибки и устраните замедления работы, как никогда раньше, с помощью OpenReplay — набора для воспроизведения сеансов с открытым исходным кодом для разработчиков. Его можно разместить самостоятельно за несколько минут, что дает вам полный контроль над данными клиентов.

Удачной отладки! Попробуйте использовать OpenReplay сегодня.

Дата-fns

Date-fns — еще один популярный пакет JavaScript для обработки и форматирования даты. Чтобы использовать самую последнюю версию v2 непосредственно в браузере, вам потребуется использовать сборщик, например Browserify, потому что сейчас он доступен только в виде пакета NPM.

В этой библиотеке около шестидесяти отдельных мест. Чтобы использовать один или несколько из них, вы должны импортировать локали следующим образом:

import { es, enCA, it, ptBR } from "date-fns/locale";

Следующие функции принимают локаль в качестве параметра:

  • format, который принимает дату, строку, определяющую шаблон форматирования (на основе символов поля даты Технического стандарта Unicode № 35), и объект, содержащий параметры, такие как языковой стандарт и индекс первого дня недели, в качестве аргументов и возвращает форматированная дата.
  • Функция formatDistance возвращает расстояние в словах между предоставленными датами после принятия в качестве входных данных дат для сравнения и объекта, содержащего параметры, такие как языковой стандарт или необходимость включения секунд.
  • Подобно formatDistance, formatDistanceStrict не использует никаких вспомогательных средств, таких как почти, больше или меньше. Возможности объектов доступных опций позволяют округлять дробные единицы и принудительно использовать единицу времени.
  • formatRelative — это формат, в котором дата отображается словами по отношению к базовой дате. Он также может принимать объект параметров в качестве входных данных для установки позиции и индекса начального дня недели.

Вот несколько примеров:

import {
  format,
  formatDistance,
  formatDistanceToNow,
  formatDistanceStrict,
  formatRelative,
  addDays,
} from "date-fns";

// Output: febrero / 23
console.log(format(new Date(), "MMMM '/' yy", { locale: es }));

// The Output: za manje od 10 sekundi
console.log(
  formatDistance(
    new Date(2022, 9, 1, 0, 0, 20),
    new Date(2022, 9, 1, 0, 0, 15),
    { locale: srLatn, includeSeconds: true, addSuffix: true }
  )
);


// Output: aproape 2 ani (assuming now is 15/02/2023 12:00)
 console.log(formatDistanceToNow(new Date(2021, 2, 15), { locale: ro }));


// Output: un minuto
console.log(
    formatDistanceStrict(
      new Date(2022, 9, 1, 0, 1, 20),
      new Date(2022, 9, 1, 0, 0, 15),
      { locale: it, unit: "minute"}
    ):

// Output: morgen om 18:38
console.log(
  formatRelative(addDays(new Date(), 1), new Date(), { locale: nlBE })
);

FormatRelative часто используется в сочетании с помощниками для добавления или удаления различных единиц времени, таких как addWeeks, subMonths и addQuarters, среди прочих.

Кроме того, имейте в виду, что formatRelative вернет дату, указанную в качестве первого аргумента, если разрыв между датами превышает шесть дней:

// If today is September 10, 2022 the output will be 17/09/2022
console.log(
  formatRelative(addDays(new Date(), 7), new Date(), { locale: ptBR })
);

временной

Temporal API для JavaScript направлен на то, чтобы полностью исправить даты, добавив новый глобальный объект с именем Temporal. Хотя он относительно новый, говорят, что он предоставляет разные классы ECMAScript для ограниченных вариантов использования, таких как только время, только дата и другие. Неправильное принятие 0, UTC или местного часового пояса для действительно неизвестных значений приведет к проблемам и затруднит чтение кода.

Временный API

Большинство API-интерфейсов Temporal делятся на версии plain и zoned, и это первое существенное различие, которое бросается в глаза. Дата/время plain представляет дату или время без указания часового пояса, что является единственным различием между этими двумя типами. С другой стороны, дата/время zoned представляет определенную дату и время в определенном часовом поясе. Мы рассмотрим несколько API, которые мы обсудим ниже.

PlainDateTime

Учитывая, что он просто предоставляет дату и время без учета часового пояса, объект PlainDateTime является одним из самых простых объектов для понимания. Использование метода Temporal.Now.plainDateTimeISO — самый простой подход к созданию нового файла PlainDateTime.

const today = Temporal.Now.plainDateTimeISO();
console.log(today.toString());
// 2022-09-03T14:17:35.306655305

Часовой пояс

Конкретный часовой пояс представлен типом данных TimeZone. Для этого есть две наиболее применяемые техники, и Temporal.Now.timeZone — одна из них, тогда у вас есть техника from. Теперь вы можете использовать конструктор вместо метода from.

const timeZone = Temporal.TimeZone.from("Africa/Cairo");
console.log(timeZone.toString());
// Africa/Cairo

const localTimeZone = Temporal.Now.timeZone();
console.log(localTimeZone.toString());
// America/Chicago

Календарь

Последний тип данных, который вам нужно понять, и, возможно, наименее важный, это тип данных calendar. Календарь можно создать с помощью конструктора или, если хотите, метода from.

const calendar = Temporal.Calendar.from("iso8601");
console.log(calendar.toString());
// iso8601

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

Хотя в настоящее время нет браузеров, которые каким-либо образом поддерживают этот API, если вы хотите начать использовать его прямо сейчас, вы можете использовать файл polyfill@js-temporal/polyfill. Вы можете использовать временный API сразу после установки этого пакета.

Заключение

Moment.js — мощная и зарекомендовавшая себя библиотека для обработки дат. Однако, помимо того, что он больше не находится в разработке, он может быть избыточным для некоторых проектов. В этой статье я проанализировал подходы четырех известных библиотек к форматированию даты в контексте интернационализации в этой статье.

Возможностей API интернационализации JavaScript может быть достаточно для простых случаев использования. Тем не менее, если вам требуется API более высокого уровня (например, для относительного времени) и дополнительные функции, такие как часовые пояса или вспомогательные методы для добавления или удаления единиц времени, вы можете взглянуть на одну из других библиотек, рассмотренных в этом руководстве. статья.

Ресурсы