Работа с датами и временем: альтернативы 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 более высокого уровня (например, для относительного времени) и дополнительные функции, такие как часовые пояса или вспомогательные методы для добавления или удаления единиц времени, вы можете взглянуть на одну из других библиотек, рассмотренных в этом руководстве. статья.