Получить ISO 8601, используя Intl.DateTimeFormat
Я хочу использовать Intl.DateTimeFormat
отформатировать дату, и в примерах это говорит
// when requesting a language that may not be supported, such as
// Balinese, include a fallback language, in this case Indonesian
Отлично, поэтому я хочу, чтобы мой запасной вариант был ISO 8601 в случае, если язык не существует
// i.e. the same as/similar to
new Date().toISOString(); // "2014-07-31T02:42:06.702Z"
тем не мение
// Intl.DateTimeFormat([locales [, options]])
var o = {};
o.year = o.month = o.day = o.hour = o.minute = o.second = 'numeric';
new Intl.DateTimeFormat(['foo', 'iso8601'], o);
// RangeError: Invalid language tag: iso8601
Это, кажется, потому что iso8601
не является частью
locales
Строка с языковым тегом BCP 47 или массив таких строк.
Я также пытался использовать один, я знаю, работает, например, en-GB
с u-ca-iso8601
суффикс, но это не приводит к какому-либо другому результату без суффикса
var f = new Intl.DateTimeFormat(['foo', 'en-GB-u-ca-iso8601'], o);
f.format(new Date());
// 31/7/2014 03:35:26
Почему это не работает? Есть ли даже locale
что даст мне результат, который я ищу?
Я бы предпочел не писать какую-то сложную оболочку, например
if (Intl.DateTimeFormat.supportedLocalesOf(['foo']).length === 0)
5 ответов
Поскольку, похоже, нет способа настроить определения локалей в Intl
, вам нужно будет найти локаль, которая использует формат ISO 8601. Проверяя определения CLDR для формата yMd в By-Type Chart: Date & Time: Gregorian, я обнаружил, что они похожи на ISO 8601. Однако поддержка определенных локалей в браузерах или других реализациях JavaScript не гарантируется.
На практике, среди таких языков в CLDR, fo
(Faroese), похоже, наиболее близок к ISO 8601 и поддерживается браузерами. Тестирование с Intl.DateTimeFormat(['foo', 'iso8601'], o)
дает следующие результаты:
2014-7-31 10:26:50 in Chrome
2014-07-31 10:26:50 in Firefox and IE
Таким образом, Chrome не совсем применяет правильный (согласно CLDR) формат, и все эти браузеры используют пробел, а не T
в качестве разделителя. Тем не менее, пространство делает презентацию более читабельной, и теперь она принята как альтернатива в соответствии с действующим стандартом ISO 8601, а именно ISO 8601:2004, который гласит:
4.3.2 ПРИМЕЧАНИЕ. По взаимному согласию партнеров по обмену информацией символ [T] может быть опущен в приложениях, где нет риска спутать представление даты и времени дня с другими, определенными в этом международном стандарте.
Тем не менее, кажется более безопасным использовать обертку, как в вопросе; это не слишком сложно, по сравнению с рисками и грязной природой использования некоторой выбранной локали. (Даже если fo
поддерживается всеми реализациями, нет никаких гарантий, что власти Фарерских островов не примут решение об изменении определения локали.)
Спустя годы я обнаружил, что принятый ответ, основанный на локали 'fo' или 'foo' (фарерский язык), больше не работает. Возможно, это так, как предположил Юкка: власти Фарерских островов решили перейти (на д / м / год) или их информация о местоположении была исправлена.
И, к сожалению, явная предлагаемая локаль "ISO8601" до сих пор не добавлена, так что это очень далеко, если это когда-либо произойдет.
Что касается комментария @felixbuenemann, я немного озадачен, поскольку Node 10 не поддерживает данные локали и просто дает результаты "en-us" для всех (возможно, это была специальная сборка Node).
Но я согласен с тем, что канадские языковые стандарты ('fr-CA' и 'en-CA') являются хорошими заменами - суть в том, чтобы использовать языковой стандарт, такой как Канада или Швеция, где был принят ISO 8601, поэтому формат локали вряд ли будет менять. Швеция (sv-SE) на самом деле лучше, чем Канада, потому что в ней не добавляются нестандартные запятые и используются более совместимые идентификаторы часовых поясов.
Итак, я предлагаю
new Date().toLocaleString( 'sv-SE', o );
// where the options 'o' is defined as in the question or however you want
который производит "2019-09-03 05:29:54". Обратите внимание, что разделитель пробелов вместо "T" также соответствует стандарту. Не добавляйте букву "Z" в конце (как в вопросе), если вы не переопределяете местное время, устанавливая для параметра timeZone значение GMT.
Один лайнер:
new Intl.DateTimeFormat('sv-SE', {timeZone: 'Asia/Jakarta', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, timeZoneName: 'short'}).format(new Date())
// "2021-09-02 21:19:35 GMT+7"
Чуть более короткий вариант, чем опубликованные:
> new Intl.DateTimeFormat('en-CA', { dateStyle: 'short' }).format(new Date())
'2021-12-01'
Проблема с использованием Intl.DateTimeFormat заключается в том, что настройки локали могут быть изменены в будущем, и любая разработанная методология может выйти из строя из-за таких изменений.
В этом ответе будет дан независимый от локали подход .
Однако, если вам необходимо использовать Intl.DateTimeFormat, ниже будет описан способ наиболее точного соответствия строке ISO в 2023 году:
const localTZ = Intl.DateTimeFormat().resolvedOptions().timeZone;
const desiredLocale = "foo";
const fallbackRequired = !Intl.DateTimeFormat.supportedLocalesOf([desiredLocale]).length;
// In 2023, apparently Swedish (sv-SE) is the closest locale to ISO format amongst all locales.
// Even "ISO" and "UTC" locales return widely different results.
const isoLocaleMatch = fallbackRequired ? "sv-SE" : desiredLocale;
const localeBackupFormatterOptions = {
timeZone: localTZ,
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
fractionalSecondDigits: 3,
hour12: false,
};
const utcBackupFormatterOptions = { ...localeBackupFormatterOptions};
utcBackupFormatterOptions.timeZone = "UTC";
const nowDate = new Date();
// The replace() at the end changes the value from i.e. '2023-06-01 15:30:11,960' to '2023-06-01T15:30:11.960'
const localNow = fallbackRequired ? Intl.DateTimeFormat(isoLocaleMatch, localeFormatterOptions)
.format(nowDate)
.replace(",", ".")
.replace(" ", "T") : Intl.DateTimeFormat(isoLocaleMatch);
const utcNow = fallbackRequired ? `${Intl.DateTimeFormat(isoLocaleMatch, utcFormatterOptions)
.format(nowDate)
.replace(",", ".")
.replace(" ", "T")}Z` : Intl.DateTimeFormat(isoLocaleMatch);
nowDate.toISOString()
> 2023-06-01T20:30:51.880Z // Trailing "Z" indicates "Zero" timezone (UTC)
localNow
> 2023-06-01T15:30:51.880 // Lack of trailing "Z" indicates local timezone (from this machine, UTC-5)
utcNow
> 2023-06-01T20:30:51.880Z
nowDate.toISOString() === utcNow
> true
Если вы желаете найти совпадение для