|
|
@@ -0,0 +1,748 @@
|
|
|
+// HumanizeDuration.js - https://git.io/j0HgmQ
|
|
|
+
|
|
|
+// @ts-check
|
|
|
+
|
|
|
+/**
|
|
|
+ * @typedef {string | ((unitCount: number) => string)} Unit
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * @typedef {("y" | "mo" | "w" | "d" | "h" | "m" | "s" | "ms")} UnitName
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * @typedef {Object} UnitMeasures
|
|
|
+ * @prop {number} y
|
|
|
+ * @prop {number} mo
|
|
|
+ * @prop {number} w
|
|
|
+ * @prop {number} d
|
|
|
+ * @prop {number} h
|
|
|
+ * @prop {number} m
|
|
|
+ * @prop {number} s
|
|
|
+ * @prop {number} ms
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * @internal
|
|
|
+ * @typedef {[string, string, string, string, string, string, string, string, string, string]} DigitReplacements
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * @typedef {Object} Language
|
|
|
+ * @prop {Unit} y
|
|
|
+ * @prop {Unit} mo
|
|
|
+ * @prop {Unit} w
|
|
|
+ * @prop {Unit} d
|
|
|
+ * @prop {Unit} h
|
|
|
+ * @prop {Unit} m
|
|
|
+ * @prop {Unit} s
|
|
|
+ * @prop {Unit} ms
|
|
|
+ * @prop {string} [decimal]
|
|
|
+ * @prop {string} [delimiter]
|
|
|
+ * @prop {DigitReplacements} [_digitReplacements]
|
|
|
+ * @prop {boolean} [_numberFirst]
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * @typedef {Object} Options
|
|
|
+ * @prop {string} [language]
|
|
|
+ * @prop {Record<string, Language>} [languages]
|
|
|
+ * @prop {string[]} [fallbacks]
|
|
|
+ * @prop {string} [delimiter]
|
|
|
+ * @prop {string} [spacer]
|
|
|
+ * @prop {boolean} [round]
|
|
|
+ * @prop {number} [largest]
|
|
|
+ * @prop {UnitName[]} [units]
|
|
|
+ * @prop {string} [decimal]
|
|
|
+ * @prop {string} [conjunction]
|
|
|
+ * @prop {number} [maxDecimalPoints]
|
|
|
+ * @prop {UnitMeasures} [unitMeasures]
|
|
|
+ * @prop {boolean} [serialComma]
|
|
|
+ * @prop {DigitReplacements} [digitReplacements]
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * @internal
|
|
|
+ * @typedef {Required<Options>} NormalizedOptions
|
|
|
+ */
|
|
|
+
|
|
|
+(function () {
|
|
|
+ // Fallback for `Object.assign` if relevant.
|
|
|
+ var assign =
|
|
|
+ Object.assign ||
|
|
|
+ /** @param {...any} destination */
|
|
|
+ function (destination) {
|
|
|
+ var source;
|
|
|
+ for (var i = 1; i < arguments.length; i++) {
|
|
|
+ source = arguments[i];
|
|
|
+ for (var prop in source) {
|
|
|
+ if (has(source, prop)) {
|
|
|
+ destination[prop] = source[prop];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return destination;
|
|
|
+ };
|
|
|
+
|
|
|
+ // Fallback for `Array.isArray` if relevant.
|
|
|
+ var isArray =
|
|
|
+ Array.isArray ||
|
|
|
+ function (arg) {
|
|
|
+ return Object.prototype.toString.call(arg) === "[object Array]";
|
|
|
+ };
|
|
|
+
|
|
|
+ // This has to be defined separately because of a bug: we want to alias
|
|
|
+ // `gr` and `el` for backwards-compatiblity. In a breaking change, we can
|
|
|
+ // remove `gr` entirely.
|
|
|
+ // See https://github.com/EvanHahn/HumanizeDuration.js/issues/143 for more.
|
|
|
+ var GREEK = language("έ", "μ", "ε", "η", "ώ", "λ", "δ", "χδ", ","); //
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @internal
|
|
|
+ * @type {Record<string, Language>}
|
|
|
+ */
|
|
|
+ var LANGUAGES = {
|
|
|
+ // Afrikaans (Afrikaans)
|
|
|
+ af: language("j", "mnd", "w", "d", "u", "m", "s", "ms", ","),
|
|
|
+ // አማርኛ (Amharic)
|
|
|
+ am: language("ዓ", "ወ", "ሳ", "ቀ", "ሰ", "ደ", "ሰከ", "ሳ", "ሚሊ"),
|
|
|
+ //العربية (Arabic) (RTL)
|
|
|
+ // https://github.com/EvanHahn/HumanizeDuration.js/issues/221#issuecomment-2119762498
|
|
|
+ // year -> ع stands for "عام" or س stands for "سنة"
|
|
|
+ // month -> ش stands for "شهر"
|
|
|
+ // week -> أ stands for "أسبوع"
|
|
|
+ // day -> ي stands for "يوم"
|
|
|
+ // hour -> س stands for "ساعة"
|
|
|
+ // minute -> د stands for "دقيقة"
|
|
|
+ // second -> ث stands for "ثانية"
|
|
|
+ ar: assign(language("س", "ش", "أ", "ي", "س", "د", "ث", "م ث", ","), {
|
|
|
+ _digitReplacements: ["۰", "١", "٢", "٣", "٤", "٥", "٦", "٧", "٨", "٩"]
|
|
|
+ }),
|
|
|
+ // български (Bulgarian)
|
|
|
+ bg: language("г", "мес", "с", "д", "ч", "м", "сек", "мс", ","),
|
|
|
+ // বাংলা (Bengali)
|
|
|
+ bn: language("ব", "ম", "সপ্তা", "দ", "ঘ", "মি", "স", "মি.স"),
|
|
|
+ // català (Catalan)
|
|
|
+ ca: language("a", "mes", "set", "d", "h", "m", "s", "ms", ","),
|
|
|
+ //کوردیی ناوەڕاست (Central Kurdish) (RTL)
|
|
|
+ ckb: language("م چ", "چ", "خ", "ک", "ڕ", "ه", "م", "س", "."),
|
|
|
+ // čeština (Czech)
|
|
|
+ cs: language("r", "měs", "t", "d", "h", "m", "s", "ms", ","),
|
|
|
+ // Cymraeg (Welsh)
|
|
|
+ cy: language("b", "mis", "wth", "d", "awr", "mun", "eil", "ms"),
|
|
|
+ // dansk (Danish)
|
|
|
+ da: language("å", "md", "u", "d", "t", "m", "s", "ms", ","),
|
|
|
+ // Deutsch (German)
|
|
|
+ de: language("J", "mo", "w", "t", "std", "m", "s", "ms", ","),
|
|
|
+ // Ελληνικά (Greek)
|
|
|
+ el: GREEK,
|
|
|
+ // English (English)
|
|
|
+ en: language("y", "mo", "w", "d", "h", "m", "s", "ms"),
|
|
|
+ // Esperanto (Esperanto)
|
|
|
+ eo: language("j", "mo", "se", "t", "h", "m", "s", "ms", ","),
|
|
|
+ // español (Spanish)
|
|
|
+ es: language("a", "me", "se", "d", "h", "m", "s", "ms", ","),
|
|
|
+ // eesti keel (Estonian)
|
|
|
+ et: language("a", "k", "n", "p", "t", "m", "s", "ms", ","),
|
|
|
+ // euskara (Basque)
|
|
|
+ eu: language("u", "h", "a", "e", "o", "m", "s", "ms", ","),
|
|
|
+ //فارسی (Farsi/Persian) (RTL)
|
|
|
+ fa: language("س", "ما", "ه", "ر", "سا", "دقی", "ثانی", "میلیثانیه"),
|
|
|
+ // suomi (Finnish)
|
|
|
+ fi: language("v", "kk", "vk", "pv", "t", "m", "s", "ms", ","),
|
|
|
+ // føroyskt (Faroese)
|
|
|
+ fo: language("á", "má", "v", "d", "t", "m", "s", "ms", ","),
|
|
|
+ // français (French)
|
|
|
+ fr: language("a", "m", "sem", "j", "h", "m", "s", "ms", ","),
|
|
|
+ // Ελληνικά (Greek) (el)
|
|
|
+ gr: GREEK,
|
|
|
+ //עברית (Hebrew) (RTL)
|
|
|
+ he: language("ש׳", "ח׳", "שב׳", "י׳", "שע׳", "ד׳", "שנ׳", "מל׳"),
|
|
|
+ // hrvatski (Croatian)
|
|
|
+ hr: language("g", "mj", "t", "d", "h", "m", "s", "ms", ","),
|
|
|
+ // हिंदी (Hindi)
|
|
|
+ hi: language("व", "म", "स", "द", "घ", "मि", "से", "मि.से"),
|
|
|
+ // magyar (Hungarian)
|
|
|
+ hu: language("é", "h", "hét", "n", "ó", "p", "mp", "ms", ","),
|
|
|
+ // Indonesia (Indonesian)
|
|
|
+ id: language("t", "b", "mgg", "h", "j", "m", "d", "md"),
|
|
|
+ // íslenska (Icelandic)
|
|
|
+ is: language("ár", "mán", "v", "d", "k", "m", "s", "ms"),
|
|
|
+ // italiano (Italian)
|
|
|
+ it: language("a", "me", "se", "g", "h", "m", "s", "ms", ","),
|
|
|
+ // 日本語 (Japanese)
|
|
|
+ ja: language("年", "月", "週", "日", "時", "分", "秒", "ミリ秒"),
|
|
|
+ // ភាសាខ្មែរ (Khmer)
|
|
|
+ km: language("ឆ", "ខ", "សប្តា", "ថ", "ម", "ន", "វ", "មវ"),
|
|
|
+ // ಕನ್ನಡ (Kannada)
|
|
|
+ kn: language("ವ", "ತ", "ವ", "ದ", "ಗಂ", "ನಿ", "ಸೆ", "ಮಿಸೆ"),
|
|
|
+ // 한국어 (Korean)
|
|
|
+ ko: language("년", "월", "주", "일", "시", "분", "초", "밀리초"),
|
|
|
+ // Kurdî (Kurdish)
|
|
|
+ ku: language("sal", "m", "h", "r", "s", "d", "ç", "ms", ","),
|
|
|
+ // ລາວ (Lao)
|
|
|
+ lo: language("ປ", "ເດ", "ອ", "ວ", "ຊ", "ນທ", "ວິນ", "ມິລິວິນາທີ", ","),
|
|
|
+ // lietuvių (Lithuanian)
|
|
|
+ lt: language("met", "mėn", "sav", "d", "v", "m", "s", "ms", ","),
|
|
|
+ // latviešu (Latvian)
|
|
|
+ lv: language("g", "mēn", "n", "d", "st", "m", "s", "ms", ","),
|
|
|
+ // македонски (Macedonian)
|
|
|
+ mk: language("г", "мес", "н", "д", "ч", "м", "с", "мс", ","),
|
|
|
+ // монгол (Mongolian)
|
|
|
+ mn: language("ж", "с", "дх", "ө", "ц", "м", "с", "мс"),
|
|
|
+ // मराठी (Marathi)
|
|
|
+ mr: language("व", "म", "आ", "दि", "त", "मि", "से", "मि.से"),
|
|
|
+ // Melayu (Malay)
|
|
|
+ ms: language("thn", "bln", "mgg", "hr", "j", "m", "s", "ms"),
|
|
|
+ // Nederlands (Dutch)
|
|
|
+ nl: language("j", "mnd", "w", "d", "u", "m", "s", "ms", ","),
|
|
|
+ // norsk (Norwegian)
|
|
|
+ no: language("år", "mnd", "u", "d", "t", "m", "s", "ms", ","),
|
|
|
+ // polski (Polish)
|
|
|
+ pl: language("r", "mi", "t", "d", "g", "m", "s", "ms", ","),
|
|
|
+ // português (Portuguese)
|
|
|
+ pt: language("a", "mês", "sem", "d", "h", "m", "s", "ms", ","),
|
|
|
+ // română (Romanian) săpt?
|
|
|
+ ro: language("a", "l", "să", "z", "h", "m", "s", "ms", ","),
|
|
|
+ // русский (Russian)
|
|
|
+ ru: language("г", "мес", "н", "д", "ч", "м", "с", "мс", ","),
|
|
|
+ // shqip (Albanian) orë? muaj?
|
|
|
+ sq: language("v", "mu", "j", "d", "o", "m", "s", "ms", ","),
|
|
|
+ // српски (Serbian)
|
|
|
+ sr: language("г", "мес", "н", "д", "ч", "м", "с", "мс", ","),
|
|
|
+ // தமிழ் (Tamil)
|
|
|
+ ta: language("ஆ", "மா", "வ", "நா", "ம", "நி", "வி", "மி.வி"),
|
|
|
+ // తెలుగు (Telugu)
|
|
|
+ te: language("సం", "నె", "వ", "రో", "గం", "ని", "సె", "మి.సె"), //
|
|
|
+ // українська (Ukrainian)
|
|
|
+ uk: language("р", "м", "т", "д", "г", "хв", "с", "мс", ","),
|
|
|
+ //اردو (Urdu) (RTL)
|
|
|
+ ur: language("س", "م", "ہ", "د", "گ", "م", "س", "م س"),
|
|
|
+ // slovenčina (Slovak)
|
|
|
+ sk: language("r", "mes", "t", "d", "h", "m", "s", "ms", ","),
|
|
|
+ // slovenščina (Slovenian)
|
|
|
+ sl: language("l", "mes", "t", "d", "ur", "m", "s", "ms", ","),
|
|
|
+ // svenska (Swedish)
|
|
|
+ sv: language("å", "mån", "v", "d", "h", "m", "s", "ms", ","),
|
|
|
+ // Kiswahili (Swahili)
|
|
|
+ sw: assign(language("mw", "m", "w", "s", "h", "dk", "s", "ms"), {
|
|
|
+ _numberFirst: true
|
|
|
+ }),
|
|
|
+ // Türkçe (Turkish)
|
|
|
+ tr: language("y", "a", "h", "g", "sa", "d", "s", "ms", ","),
|
|
|
+ // ไทย (Thai)
|
|
|
+ th: language("ปี", "ด", "ส", "ว", "ชม", "น", "วิ", "มิลลิวินาที"),
|
|
|
+ // o'zbek (Uzbek)
|
|
|
+ uz: language("y", "o", "h", "k", "soa", "m", "s", "ms"),
|
|
|
+ // Ўзбек (Кирилл) (Uzbek (Cyrillic))
|
|
|
+ uz_CYR: language("й", "о", "х", "к", "соа", "д", "с", "мс"),
|
|
|
+ // Tiếng Việt (Vietnamese)
|
|
|
+ vi: language("n", "th", "t", "ng", "gi", "p", "g", "ms", ","),
|
|
|
+ // 中文 (简体) (Chinese, simplified)
|
|
|
+ zh_CN: language("年", "月", "周", "天", "时", "分", "秒", "毫秒"),
|
|
|
+ // 中文 (繁體) (Chinese, traditional)
|
|
|
+ zh_TW: language("年", "月", "週", "天", "時", "分", "秒", "毫秒")
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Helper function for creating language definitions.
|
|
|
+ *
|
|
|
+ * @internal
|
|
|
+ * @param {Unit} y
|
|
|
+ * @param {Unit} mo
|
|
|
+ * @param {Unit} w
|
|
|
+ * @param {Unit} d
|
|
|
+ * @param {Unit} h
|
|
|
+ * @param {Unit} m
|
|
|
+ * @param {Unit} s
|
|
|
+ * @param {Unit} ms
|
|
|
+ * @param {string} [decimal]
|
|
|
+ * @returns {Language}
|
|
|
+ */
|
|
|
+ function language(y, mo, w, d, h, m, s, ms, decimal) {
|
|
|
+ /** @type {Language} */
|
|
|
+ var result = { y: y, mo: mo, w: w, d: d, h: h, m: m, s: s, ms: ms };
|
|
|
+ if (typeof decimal !== "undefined") {
|
|
|
+ result.decimal = decimal;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Helper function for Arabic.
|
|
|
+ *
|
|
|
+ * @internal
|
|
|
+ * @param {number} c
|
|
|
+ * @returns {0 | 1 | 2}
|
|
|
+ */
|
|
|
+ // function getArabicForm(c) {
|
|
|
+ // if (c === 2) {
|
|
|
+ // return 1;
|
|
|
+ // }
|
|
|
+ // if (c > 2 && c < 11) {
|
|
|
+ // return 2;
|
|
|
+ // }
|
|
|
+ // return 0;
|
|
|
+ // }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Helper function for Polish.
|
|
|
+ *
|
|
|
+ * @internal
|
|
|
+ * @param {number} c
|
|
|
+ * @returns {0 | 1 | 2 | 3}
|
|
|
+ */
|
|
|
+ // function getPolishForm(c) {
|
|
|
+ // if (c === 1) {
|
|
|
+ // return 0;
|
|
|
+ // }
|
|
|
+ // if (Math.floor(c) !== c) {
|
|
|
+ // return 1;
|
|
|
+ // }
|
|
|
+ // if (c % 10 >= 2 && c % 10 <= 4 && !(c % 100 > 10 && c % 100 < 20)) {
|
|
|
+ // return 2;
|
|
|
+ // }
|
|
|
+ // return 3;
|
|
|
+ // }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Helper function for Slavic languages.
|
|
|
+ *
|
|
|
+ * @internal
|
|
|
+ * @param {number} c
|
|
|
+ * @returns {0 | 1 | 2 | 3}
|
|
|
+ */
|
|
|
+ // function getSlavicForm(c) {
|
|
|
+ // if (Math.floor(c) !== c) {
|
|
|
+ // return 2;
|
|
|
+ // }
|
|
|
+ // if (
|
|
|
+ // (c % 100 >= 5 && c % 100 <= 20) ||
|
|
|
+ // (c % 10 >= 5 && c % 10 <= 9) ||
|
|
|
+ // c % 10 === 0
|
|
|
+ // ) {
|
|
|
+ // return 0;
|
|
|
+ // }
|
|
|
+ // if (c % 10 === 1) {
|
|
|
+ // return 1;
|
|
|
+ // }
|
|
|
+ // if (c > 1) {
|
|
|
+ // return 2;
|
|
|
+ // }
|
|
|
+ // return 0;
|
|
|
+ // }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Helper function for Czech or Slovak.
|
|
|
+ *
|
|
|
+ * @internal
|
|
|
+ * @param {number} c
|
|
|
+ * @returns {0 | 1 | 2 | 3}
|
|
|
+ */
|
|
|
+ // function getCzechOrSlovakForm(c) {
|
|
|
+ // if (c === 1) {
|
|
|
+ // return 0;
|
|
|
+ // }
|
|
|
+ // if (Math.floor(c) !== c) {
|
|
|
+ // return 1;
|
|
|
+ // }
|
|
|
+ // if (c % 10 >= 2 && c % 10 <= 4 && c % 100 < 10) {
|
|
|
+ // return 2;
|
|
|
+ // }
|
|
|
+ // return 3;
|
|
|
+ // }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Helper function for Lithuanian.
|
|
|
+ *
|
|
|
+ * @internal
|
|
|
+ * @param {number} c
|
|
|
+ * @returns {0 | 1 | 2}
|
|
|
+ */
|
|
|
+ // function getLithuanianForm(c) {
|
|
|
+ // if (c === 1 || (c % 10 === 1 && c % 100 > 20)) {
|
|
|
+ // return 0;
|
|
|
+ // }
|
|
|
+ // if (
|
|
|
+ // Math.floor(c) !== c ||
|
|
|
+ // (c % 10 >= 2 && c % 100 > 20) ||
|
|
|
+ // (c % 10 >= 2 && c % 100 < 10)
|
|
|
+ // ) {
|
|
|
+ // return 1;
|
|
|
+ // }
|
|
|
+ // return 2;
|
|
|
+ // }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Helper function for Latvian.
|
|
|
+ *
|
|
|
+ * @internal
|
|
|
+ * @param {number} c
|
|
|
+ * @returns {boolean}
|
|
|
+ */
|
|
|
+ // function getLatvianForm(c) {
|
|
|
+ // return c % 10 === 1 && c % 100 !== 11;
|
|
|
+ // }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @internal
|
|
|
+ * @template T
|
|
|
+ * @param {T} obj
|
|
|
+ * @param {keyof T} key
|
|
|
+ * @returns {boolean}
|
|
|
+ */
|
|
|
+ function has(obj, key) {
|
|
|
+ return Object.prototype.hasOwnProperty.call(obj, key);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @internal
|
|
|
+ * @param {Pick<Required<Options>, "language" | "fallbacks" | "languages">} options
|
|
|
+ * @throws {Error} Throws an error if language is not found.
|
|
|
+ * @returns {Language}
|
|
|
+ */
|
|
|
+ function getLanguage(options) {
|
|
|
+ var possibleLanguages = [options.language];
|
|
|
+
|
|
|
+ if (has(options, "fallbacks")) {
|
|
|
+ if (isArray(options.fallbacks) && options.fallbacks.length) {
|
|
|
+ possibleLanguages = possibleLanguages.concat(options.fallbacks);
|
|
|
+ } else {
|
|
|
+ throw new Error("fallbacks must be an array with at least one element");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (var i = 0; i < possibleLanguages.length; i++) {
|
|
|
+ var languageToTry = possibleLanguages[i];
|
|
|
+ if (has(options.languages, languageToTry)) {
|
|
|
+ return options.languages[languageToTry];
|
|
|
+ }
|
|
|
+ if (has(LANGUAGES, languageToTry)) {
|
|
|
+ return LANGUAGES[languageToTry];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new Error("No language found.");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @internal
|
|
|
+ * @param {Piece} piece
|
|
|
+ * @param {Language} language
|
|
|
+ * @param {Pick<Required<Options>, "decimal" | "spacer" | "maxDecimalPoints" | "digitReplacements">} options
|
|
|
+ */
|
|
|
+ function renderPiece(piece, language, options) {
|
|
|
+ var unitName = piece.unitName;
|
|
|
+ var unitCount = piece.unitCount;
|
|
|
+
|
|
|
+ var spacer = options.spacer;
|
|
|
+ var maxDecimalPoints = options.maxDecimalPoints;
|
|
|
+
|
|
|
+ /** @type {string} */
|
|
|
+ var decimal;
|
|
|
+ if (has(options, "decimal")) {
|
|
|
+ decimal = options.decimal;
|
|
|
+ } else if (has(language, "decimal")) {
|
|
|
+ decimal = language.decimal;
|
|
|
+ } else {
|
|
|
+ decimal = ".";
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @type {undefined | DigitReplacements} */
|
|
|
+ var digitReplacements;
|
|
|
+ if ("digitReplacements" in options) {
|
|
|
+ digitReplacements = options.digitReplacements;
|
|
|
+ } else if ("_digitReplacements" in language) {
|
|
|
+ digitReplacements = language._digitReplacements;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @type {string} */
|
|
|
+ var formattedCount;
|
|
|
+ var normalizedUnitCount =
|
|
|
+ maxDecimalPoints === void 0
|
|
|
+ ? unitCount
|
|
|
+ : Math.floor(unitCount * Math.pow(10, maxDecimalPoints)) /
|
|
|
+ Math.pow(10, maxDecimalPoints);
|
|
|
+ var countStr = normalizedUnitCount.toString();
|
|
|
+ if (digitReplacements) {
|
|
|
+ formattedCount = "";
|
|
|
+ for (var i = 0; i < countStr.length; i++) {
|
|
|
+ var char = countStr[i];
|
|
|
+ if (char === ".") {
|
|
|
+ formattedCount += decimal;
|
|
|
+ } else {
|
|
|
+ // @ts-ignore because `char` should always be 0-9 at this point.
|
|
|
+ formattedCount += digitReplacements[char];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ formattedCount = countStr.replace(".", decimal);
|
|
|
+ }
|
|
|
+
|
|
|
+ var languageWord = language[unitName];
|
|
|
+ var word;
|
|
|
+ if (typeof languageWord === "function") {
|
|
|
+ word = languageWord(unitCount);
|
|
|
+ } else {
|
|
|
+ word = languageWord;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (language._numberFirst) {
|
|
|
+ return word + spacer + formattedCount;
|
|
|
+ }
|
|
|
+ return formattedCount + spacer + word;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @internal
|
|
|
+ * @typedef {Object} Piece
|
|
|
+ * @prop {UnitName} unitName
|
|
|
+ * @prop {number} unitCount
|
|
|
+ */
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @internal
|
|
|
+ * @param {number} ms
|
|
|
+ * @param {Pick<Required<Options>, "units" | "unitMeasures" | "largest" | "round">} options
|
|
|
+ * @returns {Piece[]}
|
|
|
+ */
|
|
|
+ function getPieces(ms, options) {
|
|
|
+ /** @type {UnitName} */
|
|
|
+ var unitName;
|
|
|
+
|
|
|
+ /** @type {number} */
|
|
|
+ var i;
|
|
|
+
|
|
|
+ /** @type {number} */
|
|
|
+ var unitCount;
|
|
|
+
|
|
|
+ /** @type {number} */
|
|
|
+ var msRemaining;
|
|
|
+
|
|
|
+ var units = options.units;
|
|
|
+ var unitMeasures = options.unitMeasures;
|
|
|
+ var largest = "largest" in options ? options.largest : Infinity;
|
|
|
+
|
|
|
+ if (!units.length) return [];
|
|
|
+
|
|
|
+ // Get the counts for each unit. Doesn't round or truncate anything.
|
|
|
+ // For example, might create an object like `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`.
|
|
|
+ /** @type {Partial<Record<UnitName, number>>} */
|
|
|
+ var unitCounts = {};
|
|
|
+ msRemaining = ms;
|
|
|
+ for (i = 0; i < units.length; i++) {
|
|
|
+ unitName = units[i];
|
|
|
+ var unitMs = unitMeasures[unitName];
|
|
|
+
|
|
|
+ var isLast = i === units.length - 1;
|
|
|
+ unitCount = isLast
|
|
|
+ ? msRemaining / unitMs
|
|
|
+ : Math.floor(msRemaining / unitMs);
|
|
|
+ unitCounts[unitName] = unitCount;
|
|
|
+
|
|
|
+ msRemaining -= unitCount * unitMs;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (options.round) {
|
|
|
+ // Update counts based on the `largest` option.
|
|
|
+ // For example, if `largest === 2` and `unitCount` is `{ y: 7, m: 6, w: 0, d: 5, h: 23.99 }`,
|
|
|
+ // updates to something like `{ y: 7, m: 6.2 }`.
|
|
|
+ var unitsRemainingBeforeRound = largest;
|
|
|
+ for (i = 0; i < units.length; i++) {
|
|
|
+ unitName = units[i];
|
|
|
+ unitCount = unitCounts[unitName];
|
|
|
+
|
|
|
+ if (unitCount === 0) continue;
|
|
|
+
|
|
|
+ unitsRemainingBeforeRound--;
|
|
|
+
|
|
|
+ // "Take" the rest of the units into this one.
|
|
|
+ if (unitsRemainingBeforeRound === 0) {
|
|
|
+ for (var j = i + 1; j < units.length; j++) {
|
|
|
+ var smallerUnitName = units[j];
|
|
|
+ var smallerUnitCount = unitCounts[smallerUnitName];
|
|
|
+ unitCounts[unitName] +=
|
|
|
+ (smallerUnitCount * unitMeasures[smallerUnitName]) /
|
|
|
+ unitMeasures[unitName];
|
|
|
+ unitCounts[smallerUnitName] = 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Round the last piece (which should be the only non-integer).
|
|
|
+ //
|
|
|
+ // This can be a little tricky if the last piece "bubbles up" to a larger
|
|
|
+ // unit. For example, "3 days, 23.99 hours" should be rounded to "4 days".
|
|
|
+ // It can also require multiple passes. For example, "6 days, 23.99 hours"
|
|
|
+ // should become "1 week".
|
|
|
+ for (i = units.length - 1; i >= 0; i--) {
|
|
|
+ unitName = units[i];
|
|
|
+ unitCount = unitCounts[unitName];
|
|
|
+
|
|
|
+ if (unitCount === 0) continue;
|
|
|
+
|
|
|
+ var rounded = Math.round(unitCount);
|
|
|
+ unitCounts[unitName] = rounded;
|
|
|
+
|
|
|
+ if (i === 0) break;
|
|
|
+
|
|
|
+ var previousUnitName = units[i - 1];
|
|
|
+ var previousUnitMs = unitMeasures[previousUnitName];
|
|
|
+ var amountOfPreviousUnit = Math.floor(
|
|
|
+ (rounded * unitMeasures[unitName]) / previousUnitMs
|
|
|
+ );
|
|
|
+ if (amountOfPreviousUnit) {
|
|
|
+ unitCounts[previousUnitName] += amountOfPreviousUnit;
|
|
|
+ unitCounts[unitName] = 0;
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @type {Piece[]} */
|
|
|
+ var result = [];
|
|
|
+ for (i = 0; i < units.length && result.length < largest; i++) {
|
|
|
+ unitName = units[i];
|
|
|
+ unitCount = unitCounts[unitName];
|
|
|
+ if (unitCount) {
|
|
|
+ result.push({ unitName: unitName, unitCount: unitCount });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @internal
|
|
|
+ * @param {Piece[]} pieces
|
|
|
+ * @param {Pick<Required<Options>, "units" | "language" | "languages" | "fallbacks" | "delimiter" | "spacer" | "decimal" | "conjunction" | "maxDecimalPoints" | "serialComma" | "digitReplacements">} options
|
|
|
+ * @returns {string}
|
|
|
+ */
|
|
|
+ function formatPieces(pieces, options) {
|
|
|
+ var language = getLanguage(options);
|
|
|
+
|
|
|
+ if (!pieces.length) {
|
|
|
+ var units = options.units;
|
|
|
+ var smallestUnitName = units[units.length - 1];
|
|
|
+ return renderPiece(
|
|
|
+ { unitName: smallestUnitName, unitCount: 0 },
|
|
|
+ language,
|
|
|
+ options
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ var conjunction = options.conjunction;
|
|
|
+ var serialComma = options.serialComma;
|
|
|
+
|
|
|
+ var delimiter;
|
|
|
+ if (has(options, "delimiter")) {
|
|
|
+ delimiter = options.delimiter;
|
|
|
+ } else if (has(language, "delimiter")) {
|
|
|
+ delimiter = language.delimiter;
|
|
|
+ } else {
|
|
|
+ delimiter = " ";
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @type {string[]} */
|
|
|
+ var renderedPieces = [];
|
|
|
+ for (var i = 0; i < pieces.length; i++) {
|
|
|
+ renderedPieces.push(renderPiece(pieces[i], language, options));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!conjunction || pieces.length === 1) {
|
|
|
+ return renderedPieces.join(delimiter);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pieces.length === 2) {
|
|
|
+ return renderedPieces.join(conjunction);
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ renderedPieces.slice(0, -1).join(delimiter) +
|
|
|
+ (serialComma ? "," : "") +
|
|
|
+ conjunction +
|
|
|
+ renderedPieces.slice(-1)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Create a humanizer, which lets you change the default options.
|
|
|
+ *
|
|
|
+ * @param {Options} [passedOptions]
|
|
|
+ */
|
|
|
+ function humanizer(passedOptions) {
|
|
|
+ /**
|
|
|
+ * @param {number} ms
|
|
|
+ * @param {Options} [humanizerOptions]
|
|
|
+ * @returns {string}
|
|
|
+ */
|
|
|
+ var result = function humanizer(ms, humanizerOptions) {
|
|
|
+ // Make sure we have a positive number.
|
|
|
+ //
|
|
|
+ // Has the nice side-effect of converting things to numbers. For example,
|
|
|
+ // converts `"123"` and `Number(123)` to `123`.
|
|
|
+ ms = Math.abs(ms);
|
|
|
+
|
|
|
+ var options = assign({}, result, humanizerOptions || {});
|
|
|
+
|
|
|
+ var pieces = getPieces(ms, options);
|
|
|
+
|
|
|
+ return formatPieces(pieces, options);
|
|
|
+ };
|
|
|
+
|
|
|
+ return assign(
|
|
|
+ result,
|
|
|
+ {
|
|
|
+ language: "en",
|
|
|
+ spacer: "",
|
|
|
+ conjunction: "",
|
|
|
+ serialComma: true,
|
|
|
+ units: ["y", "mo", "w", "d", "h", "m", "s"],
|
|
|
+ languages: {},
|
|
|
+ round: false,
|
|
|
+ unitMeasures: {
|
|
|
+ y: 31557600000,
|
|
|
+ mo: 2629800000,
|
|
|
+ w: 604800000,
|
|
|
+ d: 86400000,
|
|
|
+ h: 3600000,
|
|
|
+ m: 60000,
|
|
|
+ s: 1000,
|
|
|
+ ms: 1
|
|
|
+ }
|
|
|
+ },
|
|
|
+ passedOptions
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Humanize a duration.
|
|
|
+ *
|
|
|
+ * This is a wrapper around the default humanizer.
|
|
|
+ */
|
|
|
+ var humanizeDuration = assign(humanizer({}), {
|
|
|
+ getSupportedLanguages: function getSupportedLanguages() {
|
|
|
+ var result = [];
|
|
|
+ for (var language in LANGUAGES) {
|
|
|
+ if (has(LANGUAGES, language) && language !== "gr") {
|
|
|
+ result.push(language);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ },
|
|
|
+ humanizer: humanizer
|
|
|
+ });
|
|
|
+
|
|
|
+ // @ts-ignore
|
|
|
+ if (typeof define === "function" && define.amd) {
|
|
|
+ // @ts-ignore
|
|
|
+ define(function () {
|
|
|
+ return humanizeDuration;
|
|
|
+ });
|
|
|
+ } else if (typeof module !== "undefined" && module.exports) {
|
|
|
+ module.exports = humanizeDuration;
|
|
|
+ } else {
|
|
|
+ this.humanizeDuration = humanizeDuration;
|
|
|
+ }
|
|
|
+})();
|