moment-duration-format
Advanced tools
Comparing version 1.3.0 to 2.0.0
{ | ||
"name": "moment-duration-format", | ||
"version": "1.3.0", | ||
"version": "2.0.0", | ||
"description": "A moment.js plugin for formatting durations.", | ||
@@ -11,3 +11,3 @@ "main": "lib/moment-duration-format.js", | ||
"authors": [ | ||
"John Madhavan-Reese <jsmreese@pureshare.com> (http://pureshare.com/)" | ||
"John Madhavan-Reese <jsmreese@gmail.com>" | ||
], | ||
@@ -14,0 +14,0 @@ "keywords": [ |
@@ -1,4 +0,4 @@ | ||
/*! Moment Duration Format v1.3.0 | ||
* https://github.com/jsmreese/moment-duration-format | ||
* Date: 2014-07-15 | ||
/*! Moment Duration Format v2.0.0 | ||
* https://github.com/jsmreese/moment-duration-format | ||
* Date: 2017-12-13 | ||
* | ||
@@ -8,476 +8,1072 @@ * Duration format plugin function for the Moment.js library | ||
* | ||
* Copyright 2014 John Madhavan-Reese | ||
* Copyright 2017 John Madhavan-Reese | ||
* Released under the MIT license | ||
*/ | ||
(function (root, undefined) { | ||
(function (root, factory) { | ||
if (typeof define === 'function' && define.amd) { | ||
// AMD. Register as an anonymous module. | ||
define(['moment'], factory); | ||
} else if (typeof exports === 'object') { | ||
// Node. Does not work with strict CommonJS, but only CommonJS-like | ||
// enviroments that support module.exports, like Node. | ||
try { | ||
module.exports = factory(require('moment')); | ||
} catch (e) { | ||
// If moment is not available, leave the setup up to the user. | ||
// Like when using moment-timezone or similar moment-based package. | ||
module.exports = factory; | ||
} | ||
} | ||
// repeatZero(qty) | ||
// returns "0" repeated qty times | ||
function repeatZero(qty) { | ||
var result = ""; | ||
// exit early | ||
// if qty is 0 or a negative number | ||
// or doesn't coerce to an integer | ||
qty = parseInt(qty, 10); | ||
if (!qty || qty < 1) { return result; } | ||
while (qty) { | ||
result += "0"; | ||
qty -= 1; | ||
} | ||
return result; | ||
} | ||
// padZero(str, len [, isRight]) | ||
// pads a string with zeros up to a specified length | ||
// will not pad a string if its length is aready | ||
// greater than or equal to the specified length | ||
// default output pads with zeros on the left | ||
// set isRight to `true` to pad with zeros on the right | ||
function padZero(str, len, isRight) { | ||
if (str == null) { str = ""; } | ||
str = "" + str; | ||
return (isRight ? str : "") + repeatZero(len - str.length) + (isRight ? "" : str); | ||
} | ||
// isArray | ||
function isArray(array) { | ||
return Object.prototype.toString.call(array) === "[object Array]"; | ||
} | ||
// isObject | ||
function isObject(obj) { | ||
return Object.prototype.toString.call(obj) === "[object Object]"; | ||
} | ||
// findLast | ||
function findLast(array, callback) { | ||
var index = array.length; | ||
if (root) { | ||
// Globals. | ||
root.momentDurationFormatSetup = root.moment ? factory(root.moment) : factory; | ||
} | ||
})(this, function (moment) { | ||
// durationLabelCompare | ||
function durationLabelCompare(a, b) { | ||
if (a.label.length > b.label.length) { | ||
return -1; | ||
} | ||
while (index -= 1) { | ||
if (callback(array[index])) { return array[index]; } | ||
} | ||
} | ||
if (a.label.length < b.label.length) { | ||
return 1; | ||
} | ||
// find | ||
function find(array, callback) { | ||
var index = 0, | ||
max = array.length, | ||
match; | ||
if (typeof callback !== "function") { | ||
match = callback; | ||
callback = function (item) { | ||
return item === match; | ||
}; | ||
} | ||
// a must be equal to b | ||
return 0; | ||
} | ||
while (index < max) { | ||
if (callback(array[index])) { return array[index]; } | ||
index += 1; | ||
} | ||
} | ||
// each | ||
function each(array, callback) { | ||
var index = 0, | ||
max = array.length; | ||
if (!array || !max) { return; } | ||
// durationGetLabels | ||
function durationGetLabels(token, localeData) { | ||
var labels = []; | ||
while (index < max) { | ||
if (callback(array[index], index) === false) { return; } | ||
index += 1; | ||
} | ||
} | ||
// map | ||
function map(array, callback) { | ||
var index = 0, | ||
max = array.length, | ||
ret = []; | ||
each(keys(localeData), function (localeDataKey) { | ||
if (localeDataKey.slice(0, 15) !== "_durationLabels") { | ||
return; | ||
} | ||
if (!array || !max) { return ret; } | ||
while (index < max) { | ||
ret[index] = callback(array[index], index); | ||
index += 1; | ||
} | ||
return ret; | ||
} | ||
// pluck | ||
function pluck(array, prop) { | ||
return map(array, function (item) { | ||
return item[prop]; | ||
}); | ||
} | ||
// compact | ||
function compact(array) { | ||
var ret = []; | ||
each(array, function (item) { | ||
if (item) { ret.push(item); } | ||
}); | ||
return ret; | ||
} | ||
// unique | ||
function unique(array) { | ||
var ret = []; | ||
each(array, function (_a) { | ||
if (!find(ret, _a)) { ret.push(_a); } | ||
}); | ||
return ret; | ||
} | ||
// intersection | ||
function intersection(a, b) { | ||
var ret = []; | ||
each(a, function (_a) { | ||
each(b, function (_b) { | ||
if (_a === _b) { ret.push(_a); } | ||
}); | ||
}); | ||
return unique(ret); | ||
} | ||
// rest | ||
function rest(array, callback) { | ||
var ret = []; | ||
each(array, function (item, index) { | ||
if (!callback(item)) { | ||
ret = array.slice(index); | ||
return false; | ||
} | ||
}); | ||
return ret; | ||
} | ||
var labelType = localeDataKey.slice(15).toLowerCase(); | ||
// initial | ||
function initial(array, callback) { | ||
var reversed = array.slice().reverse(); | ||
return rest(reversed, callback).reverse(); | ||
} | ||
// extend | ||
function extend(a, b) { | ||
for (var key in b) { | ||
if (b.hasOwnProperty(key)) { a[key] = b[key]; } | ||
} | ||
return a; | ||
} | ||
// define internal moment reference | ||
var moment; | ||
each(keys(localeData[localeDataKey]), function (labelKey) { | ||
if (labelKey.slice(0, 1) === token) { | ||
labels.push({ | ||
type: labelType, | ||
key: labelKey, | ||
label: localeData[localeDataKey][labelKey] | ||
}); | ||
} | ||
}); | ||
}); | ||
if (typeof require === "function") { | ||
try { moment = require('moment'); } | ||
catch (e) {} | ||
} | ||
if (!moment && root.moment) { | ||
moment = root.moment; | ||
} | ||
if (!moment) { | ||
throw "Moment Duration Format cannot find Moment.js"; | ||
} | ||
// moment.duration.format([template] [, precision] [, settings]) | ||
moment.duration.fn.format = function () { | ||
return labels; | ||
} | ||
var tokenizer, tokens, types, typeMap, momentTypes, foundFirst, trimIndex, | ||
args = [].slice.call(arguments), | ||
settings = extend({}, this.format.defaults), | ||
// keep a shadow copy of this moment for calculating remainders | ||
remainder = moment.duration(this); | ||
// durationPluralKey | ||
function durationPluralKey(token, integerValue, decimalValue) { | ||
// Singular for a value of `1`, but not for `1.0`. | ||
if (integerValue === 1 && decimalValue === null) { | ||
return token; | ||
} | ||
// add a reference to this duration object to the settings for use | ||
// in a template function | ||
settings.duration = this; | ||
return token + token; | ||
} | ||
// parse arguments | ||
each(args, function (arg) { | ||
if (typeof arg === "string" || typeof arg === "function") { | ||
settings.template = arg; | ||
return; | ||
} | ||
var engLocale = { | ||
durationLabelsStandard: { | ||
S: 'millisecond', | ||
SS: 'milliseconds', | ||
s: 'second', | ||
ss: 'seconds', | ||
m: 'minute', | ||
mm: 'minutes', | ||
h: 'hour', | ||
hh: 'hours', | ||
d: 'day', | ||
dd: 'days', | ||
w: 'week', | ||
ww: 'weeks', | ||
M: 'month', | ||
MM: 'months', | ||
y: 'year', | ||
yy: 'years' | ||
}, | ||
durationLabelsShort: { | ||
S: 'msec', | ||
SS: 'msecs', | ||
s: 'sec', | ||
ss: 'secs', | ||
m: 'min', | ||
mm: 'mins', | ||
h: 'hr', | ||
hh: 'hrs', | ||
d: 'dy', | ||
dd: 'dys', | ||
w: 'wk', | ||
ww: 'wks', | ||
M: 'mo', | ||
MM: 'mos', | ||
y: 'yr', | ||
yy: 'yrs' | ||
}, | ||
durationTimeTemplates: { | ||
HMS: 'h:mm:ss', | ||
HM: 'h:mm', | ||
MS: 'm:ss' | ||
}, | ||
durationLabelTypes: [ | ||
{ type: "standard", string: "__" }, | ||
{ type: "short", string: "_" } | ||
], | ||
durationPluralKey: durationPluralKey | ||
}; | ||
if (typeof arg === "number") { | ||
settings.precision = arg; | ||
return; | ||
} | ||
// isArray | ||
function isArray(array) { | ||
return Object.prototype.toString.call(array) === "[object Array]"; | ||
} | ||
if (isObject(arg)) { | ||
extend(settings, arg); | ||
} | ||
}); | ||
// isObject | ||
function isObject(obj) { | ||
return Object.prototype.toString.call(obj) === "[object Object]"; | ||
} | ||
// types | ||
types = settings.types = (isArray(settings.types) ? settings.types : settings.types.split(" ")); | ||
// findLast | ||
function findLast(array, callback) { | ||
var index = array.length; | ||
// template | ||
if (typeof settings.template === "function") { | ||
settings.template = settings.template.apply(settings); | ||
} | ||
while (index -= 1) { | ||
if (callback(array[index])) { return array[index]; } | ||
} | ||
} | ||
// tokenizer regexp | ||
tokenizer = new RegExp(map(types, function (type) { | ||
return settings[type].source; | ||
}).join("|"), "g"); | ||
// find | ||
function find(array, callback) { | ||
var index = 0, | ||
max = array.length, | ||
match; | ||
// token type map function | ||
typeMap = function (token) { | ||
return find(types, function (type) { | ||
return settings[type].test(token); | ||
}); | ||
}; | ||
if (typeof callback !== "function") { | ||
match = callback; | ||
callback = function (item) { | ||
return item === match; | ||
}; | ||
} | ||
// tokens array | ||
tokens = map(settings.template.match(tokenizer), function (token, index) { | ||
var type = typeMap(token), | ||
length = token.length; | ||
while (index < max) { | ||
if (callback(array[index])) { return array[index]; } | ||
index += 1; | ||
} | ||
} | ||
return { | ||
index: index, | ||
length: length, | ||
// each | ||
function each(array, callback) { | ||
var index = 0, | ||
max = array.length; | ||
// replace escaped tokens with the non-escaped token text | ||
token: (type === "escape" ? token.replace(settings.escape, "$1") : token), | ||
if (!array || !max) { return; } | ||
// ignore type on non-moment tokens | ||
type: ((type === "escape" || type === "general") ? null : type) | ||
while (index < max) { | ||
if (callback(array[index], index) === false) { return; } | ||
index += 1; | ||
} | ||
} | ||
// calculate base value for all moment tokens | ||
//baseValue: ((type === "escape" || type === "general") ? null : this.as(type)) | ||
}; | ||
}, this); | ||
// map | ||
function map(array, callback) { | ||
var index = 0, | ||
max = array.length, | ||
ret = []; | ||
// unique moment token types in the template (in order of descending magnitude) | ||
momentTypes = intersection(types, unique(compact(pluck(tokens, "type")))); | ||
if (!array || !max) { return ret; } | ||
// exit early if there are no momentTypes | ||
if (!momentTypes.length) { | ||
return pluck(tokens, "token").join(""); | ||
} | ||
while (index < max) { | ||
ret[index] = callback(array[index], index); | ||
index += 1; | ||
} | ||
// calculate values for each token type in the template | ||
each(momentTypes, function (momentType, index) { | ||
var value, wholeValue, decimalValue, isLeast, isMost; | ||
return ret; | ||
} | ||
// calculate integer and decimal value portions | ||
value = remainder.as(momentType); | ||
wholeValue = (value > 0 ? Math.floor(value) : Math.ceil(value)); | ||
decimalValue = value - wholeValue; | ||
// pluck | ||
function pluck(array, prop) { | ||
return map(array, function (item) { | ||
return item[prop]; | ||
}); | ||
} | ||
// is this the least-significant moment token found? | ||
isLeast = ((index + 1) === momentTypes.length); | ||
// compact | ||
function compact(array) { | ||
var ret = []; | ||
// is this the most-significant moment token found? | ||
isMost = (!index); | ||
each(array, function (item) { | ||
if (item) { ret.push(item); } | ||
}); | ||
// update tokens array | ||
// using this algorithm to not assume anything about | ||
// the order or frequency of any tokens | ||
each(tokens, function (token) { | ||
if (token.type === momentType) { | ||
extend(token, { | ||
value: value, | ||
wholeValue: wholeValue, | ||
decimalValue: decimalValue, | ||
isLeast: isLeast, | ||
isMost: isMost | ||
}); | ||
return ret; | ||
} | ||
if (isMost) { | ||
// note the length of the most-significant moment token: | ||
// if it is greater than one and forceLength is not set, default forceLength to `true` | ||
if (settings.forceLength == null && token.length > 1) { | ||
settings.forceLength = true; | ||
} | ||
// unique | ||
function unique(array) { | ||
var ret = []; | ||
// rationale is this: | ||
// if the template is "h:mm:ss" and the moment value is 5 minutes, the user-friendly output is "5:00", not "05:00" | ||
// shouldn't pad the `minutes` token even though it has length of two | ||
// if the template is "hh:mm:ss", the user clearly wanted everything padded so we should output "05:00" | ||
// if the user wanted the full padded output, they can set `{ trim: false }` to get "00:05:00" | ||
} | ||
} | ||
}); | ||
each(array, function (_a) { | ||
if (!find(ret, _a)) { ret.push(_a); } | ||
}); | ||
// update remainder | ||
remainder.subtract(wholeValue, momentType); | ||
}); | ||
// trim tokens array | ||
if (settings.trim) { | ||
tokens = (settings.trim === "left" ? rest : initial)(tokens, function (token) { | ||
// return `true` if: | ||
// the token is not the least moment token (don't trim the least moment token) | ||
// the token is a moment token that does not have a value (don't trim moment tokens that have a whole value) | ||
return !(token.isLeast || (token.type != null && token.wholeValue)); | ||
}); | ||
} | ||
// build output | ||
return ret; | ||
} | ||
// the first moment token can have special handling | ||
foundFirst = false; | ||
// intersection | ||
function intersection(a, b) { | ||
var ret = []; | ||
// run the map in reverse order if trimming from the right | ||
if (settings.trim === "right") { | ||
tokens.reverse(); | ||
} | ||
each(a, function (_a) { | ||
each(b, function (_b) { | ||
if (_a === _b) { ret.push(_a); } | ||
}); | ||
}); | ||
tokens = map(tokens, function (token) { | ||
var val, | ||
decVal; | ||
return unique(ret); | ||
} | ||
if (!token.type) { | ||
// if it is not a moment token, use the token as its own value | ||
return token.token; | ||
} | ||
// rest | ||
function rest(array, callback) { | ||
var ret = []; | ||
// apply negative precision formatting to the least-significant moment token | ||
if (token.isLeast && (settings.precision < 0)) { | ||
val = (Math.floor(token.wholeValue * Math.pow(10, settings.precision)) * Math.pow(10, -settings.precision)).toString(); | ||
} else { | ||
val = token.wholeValue.toString(); | ||
} | ||
// remove negative sign from the beginning | ||
val = val.replace(/^\-/, ""); | ||
each(array, function (item, index) { | ||
if (!callback(item)) { | ||
ret = array.slice(index); | ||
return false; | ||
} | ||
}); | ||
// apply token length formatting | ||
// special handling for the first moment token that is not the most significant in a trimmed template | ||
if (token.length > 1 && (foundFirst || token.isMost || settings.forceLength)) { | ||
val = padZero(val, token.length); | ||
} | ||
return ret; | ||
} | ||
// add decimal value if precision > 0 | ||
if (token.isLeast && (settings.precision > 0)) { | ||
decVal = token.decimalValue.toString().replace(/^\-/, "").split(/\.|e\-/); | ||
switch (decVal.length) { | ||
case 1: | ||
val += "." + padZero(decVal[0], settings.precision, true).slice(0, settings.precision); | ||
break; | ||
case 2: | ||
val += "." + padZero(decVal[1], settings.precision, true).slice(0, settings.precision); | ||
break; | ||
case 3: | ||
val += "." + padZero(repeatZero((+decVal[2]) - 1) + (decVal[0] || "0") + decVal[1], settings.precision, true).slice(0, settings.precision); | ||
break; | ||
default: | ||
throw "Moment Duration Format: unable to parse token decimal value."; | ||
} | ||
} | ||
// add a negative sign if the value is negative and token is most significant | ||
if (token.isMost && token.value < 0) { | ||
val = "-" + val; | ||
} | ||
// initial | ||
function initial(array, callback) { | ||
var reversed = array.slice().reverse(); | ||
foundFirst = true; | ||
return rest(reversed, callback).reverse(); | ||
} | ||
return val; | ||
}); | ||
// extend | ||
function extend(a, b) { | ||
for (var key in b) { | ||
if (b.hasOwnProperty(key)) { a[key] = b[key]; } | ||
} | ||
// undo the reverse if trimming from the right | ||
if (settings.trim === "right") { | ||
tokens.reverse(); | ||
} | ||
return a; | ||
} | ||
return tokens.join(""); | ||
}; | ||
// keys | ||
function keys(a) { | ||
var ret = []; | ||
moment.duration.fn.format.defaults = { | ||
// token definitions | ||
escape: /\[(.+?)\]/, | ||
years: /[Yy]+/, | ||
months: /M+/, | ||
weeks: /[Ww]+/, | ||
days: /[Dd]+/, | ||
hours: /[Hh]+/, | ||
minutes: /m+/, | ||
seconds: /s+/, | ||
milliseconds: /S+/, | ||
general: /.+?/, | ||
for (var key in a) { | ||
if (a.hasOwnProperty(key)) { ret.push(key); } | ||
} | ||
// token type names | ||
// in order of descending magnitude | ||
// can be a space-separated token name list or an array of token names | ||
types: "escape years months weeks days hours minutes seconds milliseconds general", | ||
return ret; | ||
} | ||
// format options | ||
// any | ||
function any(array, callback) { | ||
var index = 0, | ||
max = array.length; | ||
// trim | ||
// "left" - template tokens are trimmed from the left until the first moment token that has a value >= 1 | ||
// "right" - template tokens are trimmed from the right until the first moment token that has a value >= 1 | ||
// (the final moment token is not trimmed, regardless of value) | ||
// `false` - template tokens are not trimmed | ||
trim: "left", | ||
if (!array || !max) { return false; } | ||
// precision | ||
// number of decimal digits to include after (to the right of) the decimal point (positive integer) | ||
// or the number of digits to truncate to 0 before (to the left of) the decimal point (negative integer) | ||
precision: 0, | ||
while (index < max) { | ||
if (callback(array[index], index) === true) { return true; } | ||
index += 1; | ||
} | ||
// force first moment token with a value to render at full length even when template is trimmed and first moment token has length of 1 | ||
forceLength: null, | ||
return false; | ||
} | ||
// template used to format duration | ||
// may be a function or a string | ||
// template functions are executed with the `this` binding of the settings object | ||
// so that template strings may be dynamically generated based on the duration object | ||
// (accessible via `this.duration`) | ||
// or any of the other settings | ||
template: function () { | ||
var types = this.types, | ||
dur = this.duration, | ||
lastType = findLast(types, function (type) { | ||
return dur._data[type]; | ||
}); | ||
// truncate | ||
function truncate(method, value, places) { | ||
return method(value * Math.pow(10, places)) * Math.pow(10, -places); | ||
} | ||
// default template strings for each duration dimension type | ||
switch (lastType) { | ||
case "seconds": | ||
return "h:mm:ss"; | ||
case "minutes": | ||
return "d[d] h:mm"; | ||
case "hours": | ||
return "d[d] h[h]"; | ||
case "days": | ||
return "M[m] d[d]"; | ||
case "weeks": | ||
return "y[y] w[w]"; | ||
case "months": | ||
return "y[y] M[m]"; | ||
case "years": | ||
return "y[y]"; | ||
default: | ||
return "y[y] M[m] d[d] h:mm:ss"; | ||
} | ||
} | ||
}; | ||
// durationFormat([template] [, precision] [, settings]) | ||
function durationFormat() { | ||
})(this); | ||
var args = [].slice.call(arguments); | ||
var settings = extend({}, this.format.defaults); | ||
// Keep a shadow copy of this moment for calculating remainders. | ||
// Perform all calculations on positive duration value, handle negative | ||
// sign at the very end. | ||
var asMilliseconds = this.asMilliseconds(); | ||
// Treat invalid durations as having a value of 0 milliseconds. | ||
if (this.isValid() === false) { | ||
asMilliseconds = 0; | ||
} | ||
var isNegative = asMilliseconds < 0; | ||
var remainder = moment.duration(Math.abs(asMilliseconds), "milliseconds"); | ||
// Parse arguments. | ||
each(args, function (arg) { | ||
if (typeof arg === "string" || typeof arg === "function") { | ||
settings.template = arg; | ||
return; | ||
} | ||
if (typeof arg === "number") { | ||
settings.precision = arg; | ||
return; | ||
} | ||
if (isObject(arg)) { | ||
extend(settings, arg); | ||
} | ||
}); | ||
var momentTokens = { | ||
years: "y", | ||
months: "M", | ||
weeks: "w", | ||
days: "d", | ||
hours: "h", | ||
minutes: "m", | ||
seconds: "s", | ||
milliseconds: "S" | ||
}; | ||
var tokenDefs = { | ||
escape: /\[(.+?)\]/, | ||
years: /\*?[Yy]+/, | ||
months: /\*?M+/, | ||
weeks: /\*?[Ww]+/, | ||
days: /\*?[Dd]+/, | ||
hours: /\*?[Hh]+/, | ||
minutes: /\*?m+/, | ||
seconds: /\*?s+/, | ||
milliseconds: /\*?S+/, | ||
general: /.+?/ | ||
}; | ||
// Token type names in order of descending magnitude. | ||
var types = "escape years months weeks days hours minutes seconds milliseconds general".split(" "); | ||
// Types array is available in the template function. | ||
settings.types = types; | ||
var typeMap = function (token) { | ||
return find(types, function (type) { | ||
return tokenDefs[type].test(token); | ||
}); | ||
}; | ||
var tokenizer = new RegExp(map(types, function (type) { | ||
return tokenDefs[type].source; | ||
}).join("|"), "g"); | ||
// Current duration object is available in the template function. | ||
settings.duration = this; | ||
// Eval template function and cache template string. | ||
var template = typeof settings.template === "function" ? settings.template.apply(settings) : settings.template; | ||
var largest = settings.largest; | ||
// Setup stopTrim array of token types. | ||
var stopTrim = []; | ||
if (!largest) { | ||
if (isArray(settings.stopTrim)) { | ||
settings.stopTrim = settings.stopTrim.join(""); | ||
} | ||
// Parse stopTrim string to create token types array. | ||
if (settings.stopTrim) { | ||
each(settings.stopTrim.match(tokenizer), function (token) { | ||
var type = typeMap(token); | ||
if (type === "escape" || type === "general") { | ||
return; | ||
} | ||
stopTrim.push(type); | ||
}); | ||
} | ||
} | ||
// Cache moment's locale data. | ||
var localeData = moment.localeData(); | ||
if (!localeData) { | ||
localeData = {}; | ||
} | ||
// Fall back to this plugin's `eng` extension. | ||
each(keys(engLocale), function (key) { | ||
if (typeof engLocale[key] === "function") { | ||
if (!localeData[key]) { | ||
localeData[key] = engLocale[key]; | ||
} | ||
return; | ||
} | ||
if (!localeData["_" + key]) { | ||
localeData["_" + key] = engLocale[key]; | ||
} | ||
}); | ||
// Replace Duration Time Template strings. | ||
// For locale `eng`: `_HMS_`, `_HM_`, and `_MS_`. | ||
each(keys(localeData._durationTimeTemplates), function (item) { | ||
template = template.replace("_" + item + "_", localeData._durationTimeTemplates[item]); | ||
}); | ||
// Determine user's locale. | ||
var userLocale = settings.userLocale || window.navigator.userLanguage || window.navigator.language; | ||
var useLeftUnits = settings.useLeftUnits; | ||
var usePlural = settings.usePlural; | ||
var precision = settings.precision; | ||
var forceLength = settings.forceLength; | ||
var minValue = settings.minValue; | ||
var maxValue = settings.maxValue; | ||
var useGrouping = settings.useGrouping; | ||
var trunc = settings.trunc; | ||
// Use significant digits only when precision is greater than 0. | ||
var useSignificantDigits = settings.useSignificantDigits && precision > 0; | ||
var significantDigits = useSignificantDigits ? settings.precision : 0; | ||
// Trim options. | ||
var trim = settings.trim; | ||
if (isArray(trim)) { | ||
trim = trim.join(" "); | ||
} | ||
if (trim === null || trim === true || trim === "left" || trim === "right") { | ||
trim = "large"; | ||
} | ||
if (trim === false) { | ||
trim = ""; | ||
} | ||
if (largest) { | ||
trim = "all"; | ||
} | ||
var trimIncludes = function (item) { | ||
return item.test(trim); | ||
} | ||
var rLarge = /large/; | ||
var rSmall = /small/; | ||
var rBoth = /both/; | ||
var rMid = /mid/; | ||
var rAll = /^all|[^sm]all/; | ||
var rFinal = /final/; | ||
var trimLarge = any([rLarge, rBoth, rAll], trimIncludes); | ||
var trimSmall = any([rSmall, rBoth, rAll], trimIncludes); | ||
var trimMid = any([rMid, rAll], trimIncludes); | ||
var trimFinal = any([rFinal, rAll], trimIncludes); | ||
// Parse format string to create raw tokens array. | ||
var rawTokens = map(template.match(tokenizer), function (token, index) { | ||
var type = typeMap(token); | ||
if (token.slice(0, 1) === "*") { | ||
token = token.slice(1); | ||
if (!largest && type !== "escape" && type !== "general") { | ||
stopTrim.push(type); | ||
} | ||
} | ||
return { | ||
index: index, | ||
length: token.length, | ||
text: "", | ||
// Replace escaped tokens with the non-escaped token text. | ||
token: (type === "escape" ? token.replace(tokenDefs.escape, "$1") : token), | ||
// Ignore type on non-moment tokens. | ||
type: ((type === "escape" || type === "general") ? null : type) | ||
}; | ||
}); | ||
// Associate text tokens with moment tokens. | ||
var currentToken = { | ||
index: 0, | ||
length: 0, | ||
token: "", | ||
text: "", | ||
type: null | ||
}; | ||
var tokens = []; | ||
if (useLeftUnits) { | ||
rawTokens.reverse(); | ||
} | ||
each(rawTokens, function (token) { | ||
if (token.type) { | ||
if (currentToken.type || currentToken.text) { | ||
tokens.push(currentToken); | ||
} | ||
currentToken = token; | ||
return; | ||
} | ||
if (useLeftUnits) { | ||
currentToken.text = token.token + currentToken.text; | ||
} else { | ||
currentToken.text += token.token; | ||
} | ||
}); | ||
if (currentToken.type || currentToken.text) { | ||
tokens.push(currentToken); | ||
} | ||
if (useLeftUnits) { | ||
tokens.reverse(); | ||
} | ||
// Find unique moment token types in the template in order of | ||
// descending magnitude. | ||
var momentTypes = intersection(types, unique(compact(pluck(tokens, "type")))); | ||
// Exit early if there are no moment token types. | ||
if (!momentTypes.length) { | ||
return pluck(tokens, "text").join(""); | ||
} | ||
// Calculate values for each moment type in the template. | ||
// For processing the settings, values are associated with moment types. | ||
// Values will be assigned to tokens at the last step in order to | ||
// assume nothing about frequency or order of tokens in the template. | ||
momentTypes = map(momentTypes, function (momentType, index) { | ||
// Is this the least-magnitude moment token found? | ||
var isSmallest = ((index + 1) === momentTypes.length); | ||
// Is this the greatest-magnitude moment token found? | ||
var isLargest = (!index); | ||
// Get the raw value in the current units. | ||
var rawValue = remainder.as(momentType); | ||
var wholeValue = Math.floor(rawValue); | ||
var decimalValue = rawValue - wholeValue; | ||
var token = find(tokens, function (token) { | ||
return momentType === token.type; | ||
}); | ||
// Note the length of the largest-magnitude moment token: | ||
// if it is greater than one and forceLength is not set, | ||
// then default forceLength to `true`. | ||
// | ||
// Rationale is this: If the template is "h:mm:ss" and the | ||
// moment value is 5 minutes, the user-friendly output is | ||
// "5:00", not "05:00". We shouldn't pad the `minutes` token | ||
// even though it has length of two if the template is "h:mm:ss"; | ||
// | ||
// If the minutes output should always include the leading zero | ||
// even when the hour is trimmed then set `{ forceLength: true }` | ||
// to output "05:00". If the template is "hh:mm:ss", the user | ||
// clearly wanted everything padded so we should output "05:00"; | ||
// | ||
// If the user wants the full padded output, they can use | ||
// template "hh:mm:ss" and set `{ trim: false }` to output | ||
// "00:05:00". | ||
if (isLargest && forceLength == null && token.length > 1) { | ||
forceLength = true; | ||
} | ||
// Update remainder. | ||
remainder.subtract(wholeValue, momentType); | ||
return { | ||
rawValue: rawValue, | ||
wholeValue: wholeValue, | ||
// Decimal value is only retained for the least-magnitude | ||
// moment type in the format template. | ||
decimalValue: isSmallest ? decimalValue : 0, | ||
isSmallest: isSmallest, | ||
isLargest: isLargest, | ||
type: momentType, | ||
// Tokens can appear multiple times in a template string, | ||
// but all instances must share the same length. | ||
tokenLength: token.length | ||
}; | ||
}); | ||
// Trim Large. | ||
if (trimLarge) { | ||
momentTypes = rest(momentTypes, function (momentType) { | ||
// Stop trimming on: | ||
// - the smallest moment type | ||
// - a type marked for stopTrim | ||
// - a type that has a whole value | ||
return !momentType.isSmallest && !momentType.wholeValue && !find(stopTrim, momentType.type); | ||
}); | ||
} | ||
// Trim Small. | ||
if (trimSmall && momentTypes.length > 1) { | ||
momentTypes = initial(momentTypes, function (momentType) { | ||
// Stop trimming on: | ||
// - a type marked for stopTrim | ||
// - a type that has a whole value | ||
// - the largest momentType | ||
return !momentType.wholeValue && !find(stopTrim, momentType.type) && !momentType.isLargest; | ||
}); | ||
} | ||
// Trim Mid. | ||
if (trimMid) { | ||
momentTypes = map(momentTypes, function (momentType, index) { | ||
if (index > 0 && index < momentTypes.length - 1 && !momentType.wholeValue) { | ||
return null; | ||
} | ||
return momentType; | ||
}); | ||
momentTypes = compact(momentTypes); | ||
} | ||
// Trim Final. | ||
if (trimFinal && momentTypes.length === 1 && !momentTypes[0].wholeValue && !(!trunc && momentTypes[0].isSmallest && momentTypes[0].rawValue < minValue)) { | ||
momentTypes = []; | ||
} | ||
// Max Value. | ||
if (maxValue && momentTypes.length && momentTypes[0].isLargest && momentTypes[0].rawValue > maxValue) { | ||
momentTypes = momentTypes.slice(0, 1); | ||
momentTypes[0].isMaxValue = true; | ||
} | ||
// Min Value. | ||
if (minValue && momentTypes.length === 1 && momentTypes[0].isSmallest && momentTypes[0].rawValue < minValue) { | ||
momentTypes[0].isMinValue = true; | ||
} | ||
// Largest. | ||
if (largest && momentTypes.length) { | ||
momentTypes = momentTypes.slice(0, largest); | ||
} | ||
// Calculate formatted values. | ||
var truncMethod = trunc ? Math.floor : Math.round; | ||
momentTypes = map(momentTypes, function (momentType, index) { | ||
var localeStringOptions = { | ||
useGrouping: useGrouping | ||
}; | ||
if (useSignificantDigits) { | ||
if (significantDigits <= 0) { | ||
return null; | ||
} | ||
localeStringOptions.maximumSignificantDigits = significantDigits; | ||
} | ||
if (momentType.isMaxValue) { | ||
momentType.value = maxValue; | ||
} else if (momentType.isMinValue) { | ||
momentType.value = minValue; | ||
} else if (momentType.isSmallest) { | ||
// Apply precision to least significant token value. | ||
if (precision < 0) { | ||
momentType.value = truncate(truncMethod, momentType.wholeValue, precision); | ||
} else if (precision === 0) { | ||
momentType.value = truncMethod(momentType.wholeValue + momentType.decimalValue); | ||
} else { // precision > 0 | ||
if (useSignificantDigits) { | ||
if (trunc) { | ||
momentType.value = truncate(truncMethod, momentType.wholeValue + momentType.decimalValue, significantDigits - momentType.wholeValue.toString().length); | ||
} else { | ||
momentType.value = momentType.wholeValue + momentType.decimalValue; | ||
} | ||
} else { | ||
localeStringOptions.minimumFractionDigits = precision; | ||
localeStringOptions.maximumFractionDigits = precision; | ||
if (trunc) { | ||
momentType.value = momentType.wholeValue + truncate(truncMethod, momentType.decimalValue, precision); | ||
} else { | ||
momentType.value = momentType.wholeValue + momentType.decimalValue; | ||
} | ||
} | ||
} | ||
} else { | ||
if (useSignificantDigits) { | ||
// Outer Math.round required here to handle floating point errors. | ||
momentType.value = Math.round(truncate(truncMethod, momentType.wholeValue, significantDigits - momentType.wholeValue.toString().length)); | ||
significantDigits -= momentType.wholeValue.toString().length; | ||
} else { | ||
momentType.value = momentType.wholeValue; | ||
} | ||
} | ||
if (momentType.tokenLength > 1 && (index || momentType.isLargest || forceLength)) { | ||
localeStringOptions.minimumIntegerDigits = momentType.tokenLength; | ||
} | ||
momentType.formattedValue = momentType.value.toLocaleString(userLocale, localeStringOptions); | ||
localeStringOptions.useGrouping = false; | ||
momentType.formattedValueEnUS = momentType.value.toLocaleString("en-US", localeStringOptions); | ||
if (momentType.tokenLength === 2 && momentType.type === "milliseconds") { | ||
momentType.formattedValueMS = momentType.value.toLocaleString("en-US", { | ||
minimumIntegerDigits: 3, | ||
useGrouping: false | ||
}).slice(0, 2); | ||
} | ||
return momentType; | ||
}); | ||
momentTypes = compact(momentTypes); | ||
// Localize and pluralize unit labels. | ||
each(tokens, function (token) { | ||
var key = momentTokens[token.type]; | ||
var momentType = find(momentTypes, function (momentType) { | ||
return momentType.type === token.type; | ||
}); | ||
if (!key || !momentType) { | ||
return; | ||
} | ||
var values = momentType.formattedValueEnUS.split("."); | ||
values[0] = parseInt(values[0], 10); | ||
if (values[1]) { | ||
values[1] = parseFloat("0." + values[1], 10); | ||
} else { | ||
values[1] = null; | ||
} | ||
var pluralKey = localeData.durationPluralKey(key, values[0], values[1]); | ||
var labels = durationGetLabels(key, localeData); | ||
var autoLocalized = false; | ||
var pluralizedLabels = {}; | ||
// Auto-Localized unit labels. | ||
each(localeData._durationLabelTypes, function (labelType) { | ||
var label = find(labels, function (label) { | ||
return label.type === labelType.type && label.key === pluralKey; | ||
}); | ||
if (label) { | ||
pluralizedLabels[label.type] = label.label; | ||
if (token.text.includes(labelType.string)) { | ||
token.text = token.text.replace(labelType.string, label.label); | ||
autoLocalized = true; | ||
} | ||
} | ||
}); | ||
// Auto-pluralized unit labels. | ||
if (usePlural && !autoLocalized) { | ||
labels.sort(durationLabelCompare); | ||
each(labels, function (label) { | ||
if (pluralizedLabels[label.type] === label.label) { | ||
if (token.text.includes(label.label)) { | ||
// Stop checking this token if its label is already | ||
// correctly pluralized. | ||
return false; | ||
} | ||
// Skip this label if it is correct, but not present in | ||
// the token's text. | ||
return; | ||
} | ||
if (token.text.includes(label.label)) { | ||
// Replece this token's label and stop checking. | ||
token.text = token.text.replace(label.label, pluralizedLabels[label.type]); | ||
return false; | ||
} | ||
}); | ||
} | ||
}); | ||
// Build ouptut. | ||
tokens = map(tokens, function (token) { | ||
if (!token.type) { | ||
return token.text; | ||
} | ||
var momentType = find(momentTypes, function (momentType) { | ||
return momentType.type === token.type; | ||
}); | ||
if (!momentType) { | ||
return ""; | ||
} | ||
var out = ""; | ||
if (useLeftUnits) { | ||
out += token.text; | ||
} | ||
if (isNegative && momentType.isMaxValue || !isNegative && momentType.isMinValue) { | ||
out += "< "; | ||
} | ||
if (isNegative && momentType.isMinValue || !isNegative && momentType.isMaxValue) { | ||
out += "> " | ||
} | ||
if (isNegative && momentType.value > 0) { | ||
out += "-"; | ||
} | ||
isNegative = false; | ||
if (token.type === "milliseconds" && momentType.formattedValueMS) { | ||
out += momentType.formattedValueMS; | ||
} else { | ||
out += momentType.formattedValue; | ||
} | ||
if (!useLeftUnits) { | ||
out += token.text; | ||
} | ||
return out; | ||
}); | ||
// Trim leading and trailing comma, space, colon, and dot. | ||
return tokens.join("").replace(/(,| |:|\.)*$/, "").replace(/^(,| |:|\.)*/, ""); | ||
} | ||
// defaultFormatTemplate | ||
function defaultFormatTemplate() { | ||
var dur = this.duration; | ||
var findType = function findType(type) { | ||
return dur._data[type]; | ||
} | ||
var firstType = find(this.types, findType); | ||
var lastType = findLast(this.types, findType); | ||
// Default template strings for each duration dimension type. | ||
switch (firstType) { | ||
case "milliseconds": | ||
return "S __" | ||
case "seconds": // Fallthrough. | ||
case "minutes": | ||
return "*_MS_"; | ||
case "hours": | ||
return "_HMS_"; | ||
case "days": // Possible Fallthrough. | ||
if (firstType === lastType) { | ||
return "d __"; | ||
} | ||
case "weeks": // Possible Fallthrough. | ||
if (firstType === lastType) { | ||
return "w __"; | ||
} | ||
if (this.trim === null) { | ||
this.trim = "both"; | ||
} | ||
return "w __, d __, h __"; | ||
case "months": | ||
if (firstType === lastType) { | ||
return "M __"; | ||
} | ||
case "years": // Possible Fallthrough. | ||
if (firstType === lastType) { | ||
return "y __"; | ||
} | ||
if (this.trim === null) { | ||
this.trim = "both"; | ||
} | ||
return "y __, M __, d __" | ||
default: | ||
if (this.trim === null) { | ||
this.trim = "both"; | ||
} | ||
return "y __, d __, h __, m __, s __"; | ||
} | ||
} | ||
// init | ||
function init(context) { | ||
if (!context) { | ||
throw "Moment Duration Format init cannot find moment instance."; | ||
} | ||
context.duration.fn.format = durationFormat; | ||
context.duration.fn.format.defaults = { | ||
// Many options are defaulted to `null` to distinguish between | ||
// 'not set' and 'set to `false`' | ||
// trim | ||
// Can be a string, a delimited list of strings, an array of strings, | ||
// or a boolean. | ||
// "large" - will trim largest-magnitude zero-value tokens until | ||
// finding a token with a value, a token identified as 'stopTrim', or | ||
// the final token of the format string. | ||
// "small" - will trim smallest-magnitude zero-value tokens until | ||
// finding a token with a value, a token identified as 'stopTrim', or | ||
// the final token of the format string. | ||
// "both" - will execute "large" trim then "small" trim. | ||
// "mid" - will trim any zero-value tokens that are not the first or | ||
// last tokens. Usually used in conjunction with "large" or "both". | ||
// e.g. "large mid" or "both mid". | ||
// "final" - will trim the final token if it is zero-value. Use this | ||
// option with "large" or "both" to output an empty string when | ||
// formatting a zero-value duration. e.g. "large final" or "both final". | ||
// "all" - Will trim all zero-value tokens. Shorthand for "both mid final". | ||
// "left" - maps to "large" to support plugin's version 1 API. | ||
// "right" - maps to "large" to support plugin's version 1 API. | ||
// `false` - template tokens are not trimmed. | ||
// `true` - treated as "large". | ||
// `null` - treated as "large". | ||
trim: null, | ||
// stopTrim | ||
// A moment token string, a delimited set of moment token strings, | ||
// or an array of moment token strings. Trimming will stop when a token | ||
// listed in this option is reached. A "*" character in the format | ||
// template string will also mark a moment token as stopTrim. | ||
// e.g. "d [days] *h:mm:ss" will always stop trimming at the 'hours' token. | ||
stopTrim: null, | ||
// largest | ||
// Set to a positive integer to output only the "n" largest-magnitude | ||
// moment tokens that have a value. All lesser-magnitude moment tokens | ||
// will be ignored. This option takes effect even if `trim` is set | ||
// to `false`. | ||
largest: null, | ||
// maxValue | ||
// Use `maxValue` to render generalized output for large duration values, | ||
// e.g. `"> 60 days"`. `maxValue` must be a positive integer and is | ||
/// applied to the greatest-magnitude moment token in the format template. | ||
maxValue: null, | ||
// minValue | ||
// Use `minValue` to render generalized output for small duration values, | ||
// e.g. `"< 5 minutes"`. `minValue` must be a positive integer and is | ||
// applied to the least-magnitude moment token in the format template. | ||
minValue: null, | ||
// precision | ||
// If a positive integer, number of decimal fraction digits to render. | ||
// If a negative integer, number of integer place digits to truncate to 0. | ||
// If `useSignificantDigits` is set to `true` and `precision` is a positive | ||
// integer, sets the maximum number of significant digits used in the | ||
// formatted output. | ||
precision: 0, | ||
// trunc | ||
// Default behavior rounds final token value. Set to `true` to | ||
// truncate final token value, which was the default behavior in | ||
// version 1 of this plugin. | ||
trunc: false, | ||
// forceLength | ||
// Force first moment token with a value to render at full length | ||
// even when template is trimmed and first moment token has length of 1. | ||
forceLength: null, | ||
// userLocale | ||
// Formatted numerical output is rendered using `toLocaleString` | ||
// and the locale of the user's environment. Set this option to render | ||
// numerical output using a different locale. Unit names are rendered | ||
// and detected using the locale set in moment.js, which can be different | ||
// from the locale of user's environment. | ||
userLocale: null, | ||
// usePlural | ||
// Will automatically singularize or pluralize unit names when they | ||
// appear in the text associated with each moment token. Standard and | ||
// short unit labels are singularized and pluralized, based on locale. | ||
// e.g. in english, "1 second" or "1 sec" would be rendered instead | ||
// of "1 seconds" or "1 secs". The default pluralization function | ||
// renders a plural label for a value with decimal precision. | ||
// e.g. "1.0 seconds" is never rendered as "1.0 second". | ||
// Label types and pluralization function are configurable in the | ||
// localeData extensions. | ||
usePlural: true, | ||
// useLeftUnits | ||
// The text to the right of each moment token in a format string | ||
// is treated as that token's units for the purposes of trimming, | ||
// singularizing, and auto-localizing. | ||
// e.g. "h [hours], m [minutes], s [seconds]". | ||
// To properly singularize or localize a format string such as | ||
// "[hours] h, [minutes] m, [seconds] s", where the units appear | ||
// to the left of each moment token, set useLeftUnits to `true`. | ||
// This plugin is not tested in the context of rtl text. | ||
useLeftUnits: false, | ||
// useGrouping | ||
// Enables locale-based digit grouping in the formatted output. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString | ||
useGrouping: true, | ||
// useSignificantDigits | ||
// Treat the `precision` option as the maximum significant digits | ||
// to be rendered. Precision must be a positive integer. Significant | ||
// digits extend across unit types, | ||
// e.g. "6 hours 37.5 minutes" represents 4 significant digits. | ||
// Enabling this option causes token length to be ignored. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString | ||
useSignificantDigits: false, | ||
// template | ||
// The template string used to format the duration. May be a function | ||
// or a string. Template functions are executed with the `this` binding | ||
// of the settings object so that template strings may be dynamically | ||
// generated based on the duration object (accessible via `this.duration`) | ||
// or any of the other settings. Leading and trailing space, comma, | ||
// period, and colon characters are trimmed from the resulting string. | ||
template: defaultFormatTemplate | ||
}; | ||
context.updateLocale('en', engLocale); | ||
} | ||
// Initialize duration format on the global moment instance. | ||
init(moment); | ||
// Return the init function so that duration format can be | ||
// initialized on other moment instances. | ||
return init; | ||
}); |
{ | ||
"name": "moment-duration-format", | ||
"version": "1.3.0", | ||
"version": "2.0.0", | ||
"description": "A moment.js plugin for formatting durations.", | ||
@@ -11,3 +11,3 @@ "main": "lib/moment-duration-format.js", | ||
"authors": [ | ||
"John Madhavan-Reese <jsmreese@pureshare.com> (http://pureshare.com/)" | ||
"John Madhavan-Reese <jsmreese@gmail.com>" | ||
], | ||
@@ -14,0 +14,0 @@ "keywords": [ |
888
README.md
@@ -15,2 +15,4 @@ # Moment Duration Format | ||
The plugin depends on moment.js, which is not specified as a package dependency in the currently published version. | ||
**Node.js** | ||
@@ -28,4 +30,2 @@ | ||
When using this plugin in the browser, be sure to include moment.js on your page first. | ||
--- | ||
@@ -35,13 +35,32 @@ | ||
This plugin will always try to install itself on the `root.moment` instance, if it exists. | ||
This plugin will install its setup function to `root.momentDurationFormatSetup` so that it may be later called on any moment instance. | ||
### Browser | ||
When using this plugin in the browser, if you do not include moment.js on your page first, you need to manually call `window.momentDurationFormatSetup` on your moment instance once it is created. | ||
### Module | ||
To use this plugin as a module, use the `require` function: | ||
To use this plugin as a module, use the `require` function. | ||
```javascript | ||
var moment = require("moment"); | ||
var momentDurationFormatSetup = require("moment-duration-format"); | ||
``` | ||
require("moment-duration-format"); | ||
``` | ||
The plugin does not export anything, so there is no need to assign the require output to a variable. | ||
The plugin exports the init function so that duration format can be initialized on other moment instances. | ||
The plugin depends on moment.js, which is not specified as a package dependency in the currently published version. | ||
To use this plugin with any other moment.js package, for example `moment-timezone`, manually call the exported setup function to install the plugin into the desired package. | ||
```javascript | ||
var moment = require("moment-timezone"); | ||
var momentDurationFormatSetup = require("moment-duration-format"); | ||
momentDurationFormatSetup(moment); | ||
typeof moment.duration.fn.format === "function" | ||
// true | ||
``` | ||
### Basics | ||
@@ -51,3 +70,3 @@ | ||
``` | ||
```javascript | ||
moment.duration(123, "minutes").format(); | ||
@@ -57,10 +76,24 @@ // "2:03:00" | ||
moment.duration(123, "months").format(); | ||
// "10y 3m" | ||
// "10 years, 3 months" | ||
``` | ||
The duration format method may be called with three optional arguments: | ||
``` | ||
```javascript | ||
moment.duration.format([template] [, precision] [, settings]) | ||
``` | ||
#### Invalid Durations | ||
Invalid durations are treated as having a value of `0` for formatting. | ||
```javascript | ||
var invalidDuration = moment.duration(NaN, "second"); | ||
invalidDuration.isValid(); | ||
// false | ||
invalidDuration.format(); | ||
// "0 seconds" | ||
``` | ||
### Template | ||
@@ -70,3 +103,5 @@ | ||
``` | ||
#### Template String | ||
```javascript | ||
moment.duration(123, "minutes").format("h:mm"); | ||
@@ -76,3 +111,4 @@ // "2:03" | ||
The template string is parsed for moment-token characters, which are replaced with the duration's value for each unit type. The default tokens are: | ||
The template string is parsed for moment token characters, which are replaced with the duration's value for each unit type. The moment tokens are: | ||
``` | ||
@@ -89,20 +125,122 @@ years: Y or y | ||
Token characters may be customized ([see below for an example](\#tokens)). | ||
Escape token characters within the template string using square brackets. | ||
``` | ||
```javascript | ||
moment.duration(123, "minutes").format("h [hrs], m [min]"); | ||
// "2 hrs, 3 min" | ||
// "2 hrs, 3 mins" | ||
``` | ||
Escape characters may also be customized ([see below for an example](\#escape)). | ||
#### Token Length | ||
For some time duration formats, a zero-padded value is required. Use multiple token characters together to create the correct amount of padding. | ||
```javascript | ||
moment.duration(3661, "seconds").format("h:mm:ss"); | ||
// "1:01:01" | ||
moment.duration(15, "seconds").format("sss [s]"); | ||
// "015 s" | ||
``` | ||
When the format template is trimmed, token length on the largest-magnitude rendered token can be trimmed as well. See sections *trim* and *forceLength* below for more details. | ||
```javascript | ||
moment.duration(123, "seconds").format("h:mm:ss"); | ||
// "2:03" | ||
``` | ||
##### Milliseconds Token Length | ||
Token length of `2` for milliseconds is a special case, most likely used to render milliseconds as part of a timer output, such as `mm:ss:SS`. In this case, the milliseconds value is padded to three digits then truncated from the left to render a two digit output. | ||
```javascript | ||
moment.duration(9, "milliseconds").format("mm:ss:SS", { | ||
trim: false | ||
}); | ||
// "00:00:00" | ||
moment.duration(10, "milliseconds").format("mm:ss:SS", { | ||
trim: false | ||
}); | ||
// "00:00:01" | ||
moment.duration(999, "milliseconds").format("mm:ss:SS", { | ||
trim: false | ||
}); | ||
// "00:00:99" | ||
moment.duration(1011, "milliseconds").format("mm:ss:SS", { | ||
trim: false | ||
}); | ||
// "00:01:01" | ||
``` | ||
#### Multiple Token Instances | ||
Tokens can appear multiple times in the format template, but all instances must share the same length. If they do not, all instances will be rendered at the length of the first token of that type. | ||
```javascript | ||
moment.duration(15, "seconds").format("ssss sss ss s"); | ||
// "0015 0015 0015 0015" | ||
moment.duration(15, "seconds").format("s ss sss ssss"); | ||
// "15 15 15 15" | ||
``` | ||
#### Default Template Function | ||
The default template function attempts to format a duration based on its magnitude. The larger the duration value, the larger the units of the formatted output will be. | ||
For some duration values, the default template function will default `trim` to `"both"` if that option is not set in the settings object (more on that below). | ||
The default template function uses auto-localized unit labels (more on that below, also). | ||
```javascript | ||
moment.duration(100, "milliseconds").format(); | ||
// "100 milliseconds" | ||
moment.duration(100, "seconds").format(); | ||
// "1:40" | ||
moment.duration(100, "days").format(); | ||
// "3 months, 9 days" | ||
moment.duration(100, "weeks").format(); | ||
// "1 year, 10 months, 30 days" | ||
moment.duration(100, "months").format(); | ||
// "8 years, 4 months" | ||
``` | ||
#### Custom Template Function | ||
Use a custom template function if you need runtime control over the template string. Template functions are executed with a `this` binding of the settings object, and have access to the underlying duration object via `this.duration`. Any of the settings may be accessed or modified by the template function. | ||
This custom template function uses a different template based on the value of the duration: | ||
```javascript | ||
function customTemplate() { | ||
return this.duration.asSeconds() >= 86400 ? "w [weeks], d [days]" : "hh:mm:ss"; | ||
} | ||
moment.duration(65, 'seconds').format(customTemplate, { | ||
trim: false | ||
}); | ||
// "00:01:05" | ||
moment.duration(1347840, 'seconds').format(customTemplate, { | ||
trim: false | ||
}); | ||
// "2 weeks, 2 days" | ||
``` | ||
#### Punctuation Trimming | ||
To ensure user-friendly formatted output, punctuation characters are trimmed from the beginning and end of the formatted output. Specifically, leading and trailing period `.`, comma `,`, colon `:`, and space ` ` characters are removed. | ||
### Precision | ||
`precision` (number) defines the number of digits to display for the final value. | ||
`precision` (number) defines the number of decimal fraction or integer digits to display for the final value. | ||
The default precison value is `0`. | ||
``` | ||
```javascript | ||
moment.duration(123, "minutes").format("h [hrs]"); | ||
@@ -112,13 +250,13 @@ // "2 hrs" | ||
Positive precision defines the number of digits to display to the right of the decimal point. | ||
``` | ||
Positive precision defines the number of decimal fraction digits to display. | ||
```javascript | ||
moment.duration(123, "minutes").format("h [hrs]", 2); | ||
// "2.04 hrs" | ||
// "2.05 hrs" | ||
``` | ||
Negative precision will truncate the value to the left of the decimal point. | ||
Negative precision defines the number of integer digits to truncate to zero. | ||
```javascript | ||
moment.duration(223, "minutes").format("m [min]", -2); | ||
// "200 mins" | ||
``` | ||
moment.duration(123, "minutes").format("m [min]", -1); | ||
// "120 min" | ||
``` | ||
@@ -131,32 +269,358 @@ ### Settings | ||
```javascript | ||
moment.duration(123, "minutes").format({ | ||
template: "h [hrs]", | ||
precision: 2 | ||
}); | ||
// "2.05 hrs" | ||
``` | ||
moment.duration(123, "minutes").format({ template: "h [hrs]", precision: 2 }); | ||
// "2.04 hrs" | ||
#### trim | ||
The default `trim` behaviour is `"large"`. | ||
Largest-magnitude tokens are automatically trimmed when they have no value. | ||
```javascript | ||
moment.duration(123, "minutes").format("d[d] h:mm:ss"); | ||
// "2:03:00" | ||
``` | ||
#### Trim | ||
Trimming also functions when the format string is oriented with token magnitude increasing from left to right. | ||
```javascript | ||
moment.duration(123, "minutes").format("s [seconds], m [minutes], h [hours], d [days]"); | ||
// "0 seconds, 3 minutes, 2 hours" | ||
``` | ||
Leading tokens are automatically trimmed when they have no value. | ||
To stop trimming altogether, set `{ trim: false }`. | ||
```javascript | ||
moment.duration(123, "minutes").format("d[d] h:mm:ss", { | ||
trim: false | ||
}); | ||
// "0d 2:03:00" | ||
``` | ||
`trim` can be a string, a delimited list of strings, an array of strings, or a boolean. Accepted values are as follows: | ||
- ##### `"large"` | ||
Trim largest-magnitude zero-value tokens until finding a token with a value, a token identified as `stopTrim`, or the final token of the format string. This is the default `trim` value. | ||
```javascript | ||
moment.duration(123, "minutes").format("d[d] h:mm:ss"); | ||
// "2:03:00" | ||
moment.duration(123, "minutes").format("d[d] h:mm:ss", { | ||
trim: "large" | ||
}); | ||
// "2:03:00" | ||
moment.duration(0, "minutes").format("d[d] h:mm:ss", { | ||
trim: "large" | ||
}); | ||
// "0" | ||
``` | ||
To stop that behavior, set `{ trim: false }`. | ||
- ##### `"small"` | ||
Trim smallest-magnitude zero-value tokens until finding a token with a value, a token identified as `stopTrim`, or the final token of the format string. | ||
```javascript | ||
moment.duration(123, "minutes").format("d[d] h:mm:ss", { | ||
trim: "small" | ||
}); | ||
// "0d 2:03" | ||
moment.duration(0, "minutes").format("d[d] h:mm:ss", { | ||
trim: "small" | ||
}); | ||
// "0d" | ||
``` | ||
moment.duration(123, "minutes").format("d[d] h:mm:ss", { trim: false }); | ||
// "0d 2:03:00" | ||
- ##### `"both"` | ||
Execute `"large"` trim then `"small"` trim. | ||
```javascript | ||
moment.duration(123, "minutes").format("d[d] h[h] m[m] s[s]", { | ||
trim: "both" | ||
}); | ||
// "2h 3m" | ||
moment.duration(0, "minutes").format("d[d] h[h] m[m] s[s]", { | ||
trim: "both" | ||
}); | ||
// "0s" | ||
``` | ||
Use `{ trim: "right" }` to trim from the right. | ||
- ##### `"mid"` | ||
Trim any zero-value tokens that are not the first or last tokens. Usually used in conjunction with `"large"` or `"both"`. e.g. `"large mid"` or `"both mid"`. | ||
```javascript | ||
moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { | ||
trim: "mid" | ||
}); | ||
// "0w 1d 1m 0s" | ||
moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { | ||
trim: "large mid" | ||
}); | ||
// "1d 1m 0s" | ||
moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { | ||
trim: "small mid" | ||
}); | ||
// "0w 1d 1m" | ||
moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { | ||
trim: "both mid" | ||
}); | ||
// "1d 1m" | ||
moment.duration(0, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { | ||
trim: "both mid" | ||
}); | ||
// "0s" | ||
``` | ||
moment.duration(123, "minutes").format("[seconds:] s -- [minutes:] m -- [hours:] h -- [days:] d", { trim: "right" }); | ||
// "seconds: 0 -- minutes: 3 -- hours: 2" | ||
- ##### `"final"` | ||
Trim the final token if it is zero-value. Use this option with `"large"` or `"both"` to output an empty string when formatting a zero-value duration. e.g. `"large final"` or `"both final"`. | ||
```javascript | ||
moment.duration(0, "minutes").format("d[d] h:mm:ss", { | ||
trim: "large final" | ||
}); | ||
// "" | ||
moment.duration(0, "minutes").format("d[d] h:mm:ss", { | ||
trim: "small final" | ||
}); | ||
// "" | ||
moment.duration(0, "minutes").format("d[d] h[h] m[m] s[s]", { | ||
trim: "both final" | ||
}); | ||
// "" | ||
``` | ||
#### Force Length | ||
- ##### `"all"` | ||
Force the first moment token with a value to render at full length, even when the template is trimmed and the first moment token has a length of 1. Sounds more complicated than it is. | ||
Trim all zero-value tokens. Shorthand for `"both mid final"`. | ||
```javascript | ||
moment.duration(0, "minutes").format("d[d] h[h] m[m] s[s]", { | ||
trim: "all" | ||
}); | ||
// "" | ||
``` | ||
- ##### `"left"` | ||
Maps to `"large"` to support this plugin's version 1 API. | ||
- ##### `"right"` | ||
Maps to `"large"` to support this plugin's version 1 API. | ||
- ##### `true` | ||
Maps to `"large"`. | ||
- ##### `null` | ||
Maps to `"large"`. | ||
- ##### `false` | ||
Disables trimming. | ||
#### largest | ||
Set `largest` to a positive integer to output only the `n` largest-magnitude moment tokens that have a value. All lesser-magnitude or zero-value moment tokens will be ignored. Using the `largest` option overrides `trim` to `"all"` and disables `stopTrim`. This option takes effect even when `trim: false` is used. | ||
```javascript | ||
moment.duration(7322, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { | ||
largest: 2 | ||
}); | ||
// "2 hours, 2 minutes" | ||
moment.duration(1216922, "seconds").format("y [years], w [weeks], d [days], h [hours], m [minutes], s [seconds]", { | ||
largest: 2 | ||
}); | ||
// "2 weeks, 2 hours" | ||
``` | ||
#### stopTrim | ||
Trimming will stop when a token listed in this option is reached. | ||
Option value may be a moment token string, a delimited set of moment token strings, or an array of moment token strings. Alternatively, set `stopTrim` on tokens in the format template string directly using a `*` character before the moment token. | ||
```javascript | ||
moment.duration(23, "minutes").format("d[d] h:mm:ss", { | ||
stopTrim: "h" | ||
}); | ||
// "0:23:00" | ||
moment.duration(23, "minutes").format("d[d] *h:mm:ss"); | ||
// "0:23:00" | ||
``` | ||
This option affects all trimming modes: `"large"`, `"small"`, `"mid"`, and `"final"`. | ||
```javascript | ||
moment.duration(2, "hours").format("y [years], d [days], h [hours], m [minutes], s [seconds]", { | ||
trim: "both", | ||
stopTrim: "d m" | ||
}); | ||
// "0 days, 2 hours, 0 minutes" | ||
moment.duration(2, "hours").format("y [years], *d [days], h [hours], *m [minutes], s [seconds]", { | ||
trim: "both" | ||
}); | ||
// "0 days, 2 hours, 0 minutes" | ||
``` | ||
`stopTrim` is disabled when using `largest`. | ||
```javascript | ||
moment.duration(2, "hours").format("y [years], d [days], h [hours], m [minutes], s [seconds]", { | ||
trim: "both", | ||
stopTrim: "d m", | ||
largest: 2 | ||
}); | ||
// "2 hours" | ||
``` | ||
#### trunc | ||
Default behavior rounds the final token value. | ||
```javascript | ||
moment.duration(179, "seconds").format("m [minutes]"); | ||
// "3 minutes" | ||
moment.duration(3780, "seconds").format("h [hours]", 1); | ||
// "1.1 hours" | ||
``` | ||
Set `trunc` to `true` to truncate final token value. This was the default behavior in version 1 of this plugin. | ||
```javascript | ||
moment.duration(179, "seconds").format("m [minutes]", { | ||
trunc: true | ||
}); | ||
// "2 minutes" | ||
moment.duration(3780, "seconds").format("h [hours]", 1, { | ||
trunc: true | ||
}); | ||
// "1.0 hours" | ||
``` | ||
Using `trunc` can affect the operation of `trim` and `largest`. | ||
```javascript | ||
moment.duration(59, "seconds").format("d [days], h [hours], m [minutes]", { | ||
trunc: true, | ||
trim: "both" | ||
}); | ||
// "0 minutes" | ||
moment.duration(59, "seconds").format("d [days], h [hours], m [minutes]", { | ||
trunc: true, | ||
trim: "all" | ||
}); | ||
// "" | ||
moment.duration(59, "seconds").format("d [days], h [hours], m [minutes]", { | ||
trunc: true, | ||
largest: 1 | ||
}); | ||
// "" | ||
``` | ||
#### minValue | ||
Use `minValue` to render generalized output for small duration values, e.g. `"< 5 minutes"`. `minValue` must be a positive integer and is applied to the least-magnitude moment token in the format template. This option affects the operation of `trim`, and is affected by `trunc`. | ||
```javascript | ||
moment.duration(59, "seconds").format("h [hours], m [minutes]", { | ||
minValue: 1 | ||
}); | ||
// "< 1 minute" | ||
moment.duration(59, "seconds").format("h [hours], m [minutes]", { | ||
minValue: 1, | ||
trim: "both" | ||
}); | ||
// "< 1 minute" | ||
moment.duration(7229, "seconds").format("h [hours], m [minutes]", { | ||
minValue: 1, | ||
trim: "both" | ||
}); | ||
// "2 hours" | ||
moment.duration(59, "seconds").format("h [hours], m [minutes]", { | ||
minValue: 1, | ||
trunc: true, | ||
trim: "all" | ||
}); | ||
// "" | ||
``` | ||
`minValue` can be used with negative durations, where it has the same effect on the least-magnitude moment token's absolute value. | ||
```javascript | ||
moment.duration(-59, "seconds").format("h [hours], m [minutes]", { | ||
minValue: 1 | ||
}); | ||
// "> -1 minute" | ||
``` | ||
When `minValue` is reached, only the least-magnitude moment token is output, regardless of `trim` and `largest`. | ||
```javascript | ||
moment.duration(59, "seconds").format("h [hours], m [minutes]", { | ||
minValue: 1, | ||
trim: false, | ||
largest: 2 | ||
}); | ||
// "< 1 minute" | ||
``` | ||
#### maxValue | ||
Use `maxValue` to render generalized output for large duration values, e.g. `"> 60 days"`. `maxValue` must be a positive integer and is applied to the greatest-magnitude moment token in the format template. As with `minValue`, this option affects the operation of `trim`, is affected by `trunc`, and can be used with negative durations. | ||
```javascript | ||
moment.duration(15, "days").format("w [weeks]", { | ||
maxValue: 2 | ||
}); | ||
// "> 2 weeks" | ||
moment.duration(-15, "days").format("w [weeks]]", { | ||
maxValue: 2 | ||
}); | ||
// "< -2 weeks" | ||
``` | ||
When `maxValue` is reached, only the greatest-magnitude moment token is output, regardless of `trim` and `largest`. | ||
```javascript | ||
moment.duration(15, "days").format("w [weeks], d [days]", { | ||
maxValue: 2, | ||
trim: false, | ||
largest: 2 | ||
}); | ||
// "> 2 weeks" | ||
``` | ||
#### forceLength | ||
Force the first moment token with a value to render at full length, even when the template is trimmed and the first moment token has a length of `1`. Sounds more complicated than it is. | ||
```javascript | ||
moment.duration(123, "seconds").format("h:mm:ss"); | ||
@@ -166,5 +630,5 @@ // "2:03" | ||
If you want minutes to always be rendered with two digits, you can set the first token to a length greater than 1 (this stops the automatic length trimming for the first token that has a value). | ||
If you want minutes to always be rendered with two digits, you can use a first token with a length greater than 1 (this stops the automatic token length trimming for the first token that has a value). | ||
``` | ||
```javascript | ||
moment.duration(123, "seconds").format("hh:mm:ss"); | ||
@@ -176,109 +640,341 @@ // "02:03" | ||
``` | ||
moment.duration(123, "seconds").format("h:mm:ss", { forceLength: true }); | ||
```javascript | ||
moment.duration(123, "seconds").format("h:mm:ss", { | ||
forceLength: true | ||
}); | ||
// "02:03" | ||
``` | ||
#### useSignificantDigits | ||
### Tokens | ||
When `useSignificantDigits` is set to `true`, the `precision` option determines the maximum significant digits to be rendered. Precision must be a positive integer. Significant digits extend across unit types, e.g. `"6 hours 37.5 minutes"` represents `4` significant digits. Enabling this option causes token length to be ignored. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString. | ||
Moment tokens are defined as regular expressions, and may be customized. Token definitions may be passed in the settings object, but are more likely set on the defaults object. | ||
Setting `trunc` affects the operation of `useSignificantDigits`. | ||
```javascript | ||
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { | ||
useSignificantDigits: true, | ||
precision: 3 | ||
}); | ||
// "1 day, 3 hours, 50 minutes" | ||
#### Escape | ||
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { | ||
useSignificantDigits: true, | ||
precision: 3, | ||
trunc: true | ||
}); | ||
// "1 day, 3 hours, 40 minutes" | ||
Default escape token regexp: `/\[(.+?)\]/` | ||
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { | ||
useSignificantDigits: true, | ||
precision: 5 | ||
}); | ||
// "1 day, 3 hours, 46 minutes, 40 seconds" | ||
Define something other than square brackets as escape characters. | ||
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { | ||
useSignificantDigits: true, | ||
trunc: true, | ||
precision: 5 | ||
}); | ||
// "1 day, 3 hours, 46 minutes, 30 seconds" | ||
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { | ||
useSignificantDigits: true, | ||
precision: 6 | ||
}); | ||
// "1 day, 3 hours, 46 minutes, 39 seconds" | ||
``` | ||
moment.duration.fn.format.defaults.escape = /\((.+?)\)/; | ||
moment.duration(123, "seconds").format("m (minutes)", 2); | ||
// "2.04 minutes" | ||
### Localization | ||
Formatted numerical output is rendered using [`toLocaleString`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString). | ||
Unit labels are automatically localized and pluralized. Unit labels are detected using the [locale set in moment.js](https://momentjs.com/docs/#/i18n/), which can be different from the locale of user's environment. This plugin uses custom extensions to the moment.js locale object, which can be easily added for any locale (see below). | ||
It's likely that the options below do not address every i18n requirement for duration formatting (the plugin hasn't been tested on languages that are written from right to left, for instance), but they are a significant step in the right direction and support languages with [multiple forms of plural](https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_and_Plurals)). | ||
#### userLocale | ||
Numerical output is rendered using the locale of the user's environment. Set the `userLocale` option to render numerical output using a different locale. | ||
```javascript | ||
moment.duration(1234567, "seconds").format("m [minutes]", 3); | ||
// "20,576.117 minutes" | ||
moment.duration(1234567, "seconds").format("m [minutes]", 3, { | ||
userLocale: "de-DE" | ||
}); | ||
// "20.576,117 minutes" | ||
``` | ||
#### Years | ||
#### Auto-Localized Unit Labels | ||
Default years token regexp: `/[Yy]+/` | ||
The `_` character can be used to generate auto-localized unit labels in the formatted output. | ||
Define years token for spanish language formatting. | ||
A single underscore `_` will be replaced with the short duration unit label for its associated moment token. | ||
A double underscore `__` will be replaced with the standard duration unit label for its associated moment token. | ||
```javascript | ||
moment.duration(2, "minutes").format("m _"); | ||
// "2 mins" | ||
moment.duration(2, "minutes").format("m __"); | ||
// "2 minutes" | ||
``` | ||
moment.duration.fn.format.defaults.years = /[Aa]+/; | ||
moment.duration(123, "weeks").format("a [años]", 2); | ||
// "2.35 años" | ||
These are the default `"en"` locale options for unit labels. Unit label types and even the `"_"` character usage can be customized in the locale object extensions (see below). | ||
#### Auto-Localized Time Notation | ||
Durations can also be formatted with a localized time notation. | ||
The string `_HMS_` is replaced with a localized `hour/minute/second` time notation, e.g. `h:mm:ss`. | ||
The string `_HM_` is replaced with a localized `hour/minute` time notation, e.g. `h:mm`. | ||
The string `_MS_` is replaced with a localized `minute/second` time notation, e.g. `m:ss`. | ||
```javascript | ||
moment.duration(3661, "seconds").format("_HMS_"); | ||
// "1:01:01" | ||
moment.duration(3661, "seconds").format("_HM_"); | ||
// "1:01" | ||
moment.duration(61, "seconds").format("_MS_"); | ||
// "1:01" | ||
``` | ||
#### Months | ||
These are the default `"en"` locale options for duration time notation templates. Additional templates may be created in the locale object extensions (see below). | ||
Default months token regexp: `/M+/` | ||
#### usePlural | ||
Define months token to use only lower-case `m`. | ||
Unit label pluralization is automatically corrected when unit labels appear in the text associated with each moment token. The default `"en"` locale extension includes long and short unit labels, and a basic pluralization function. Unit labels, unit label types, and the pluralization function can be customized for a locale (see below). | ||
```javascript | ||
moment.duration(1, "minutes").format("m [minutes]"); | ||
// "1 minute" | ||
moment.duration(1, "minutes").format("m [mins]"); | ||
// "1 min" | ||
moment.duration(2, "minutes").format("m [minute]"); | ||
// "2 minutes" | ||
moment.duration(2, "minutes").format("m [min]"); | ||
// "2 mins" | ||
``` | ||
moment.duration.fn.format.defaults.months = /m+/; | ||
moment.duration(123, "weeks").format("m M", 2); | ||
// "28.36 M" | ||
``` | ||
#### Weeks | ||
Set `usePlural` to `false` to disable auto-correction of pluralization. | ||
Default weeks token regexp: `/[Ww]+/` | ||
```javascript | ||
moment.duration(1, "minutes").format("m [minutes]", { | ||
usePlural: false | ||
}); | ||
// "1 minutes" | ||
Define weeks token to use only lower-case `w`. | ||
moment.duration(1, "minutes").format("m [mins]", { | ||
usePlural: false | ||
}); | ||
// "1 mins" | ||
moment.duration(2, "minutes").format("m [minute]", { | ||
usePlural: false | ||
}); | ||
// "2 minute" | ||
moment.duration(2, "minutes").format("m [min]", { | ||
usePlural: false | ||
}); | ||
// "2 min" | ||
``` | ||
moment.duration.fn.format.defaults.weeks = /w+/; | ||
moment.duration(123, "days").format("w W", 2); | ||
// "17.57 W" | ||
The default pluralization function for the `"en"` locale outputs a plural unit name when a value is rendered with decimal precision. | ||
```javascript | ||
moment.duration(1, "minutes").format("m [minutes]", 2); | ||
// "1.00 minutes" | ||
``` | ||
#### Days | ||
#### useLeftUnits | ||
Default days token regexp: `/[Dd]+/` | ||
The text to the right of each moment token in a template string is treated as that token's units for the purposes of trimming, pluralizing, and localizing. To properly process a template string where the token/unit association is reversed, set `useLeftUnits` to `true`. | ||
Define days token to use only lower-case `d`. | ||
```javascript | ||
moment.duration(7322, "seconds").format("_ h, _ m, _ s", { | ||
useLeftUnits: true | ||
}); | ||
// "hrs 2, mins 2, secs 2" | ||
``` | ||
#### useGrouping | ||
Formatted numerical output is rendered using `toLocaleString` with the option `useGrouping` enabled. Set `useGrouping` to `false` to disable digit grouping. | ||
```javascript | ||
moment.duration(1234, "seconds").format("s [seconds]"); | ||
// "1,234 seconds" | ||
moment.duration(1234, "seconds").format("s [seconds]", { | ||
useGrouping: false | ||
}); | ||
// "1234 seconds" | ||
``` | ||
moment.duration.fn.format.defaults.days = /d+/; | ||
moment.duration(123, "hours").format("d D", 2); | ||
// "5.12 D" | ||
#### Decimal Separator | ||
Previous versions of the plugin used a `decimalSeparator` option. That option is no longer used and will have no effect. Decimal separators are rendered using `toLocalString` and the user's locale. | ||
```javascript | ||
moment.duration(1234567, "seconds").format("m [minutes]", 3, { | ||
userLocale: "de-DE" | ||
}); | ||
// "20.576,117 minutes" | ||
``` | ||
#### Hours | ||
#### Extending Moment's `locale` object | ||
Default hours token regexp: `/[Hh]+/` | ||
This plugin now extends the moment.js `locale` object with duration labels, duration label types, duration time-notation templates, and a pluralization function. The `"en"` locale is included with this plugin. Other locales may be defined using the moment.js locale API to provide auto-pluralized and auto-localized unit labels in different languages. If the plugin cannot find the duration locale extensions for the active moment locale, the plugin will fall back to the `"en"` locale. | ||
Define hours token to use only lower-case `h`. | ||
Below is the default `"en"` locale extension. | ||
```javascript | ||
moment.updateLocale('en', { | ||
durationLabelsStandard: { | ||
S: 'millisecond', | ||
SS: 'milliseconds', | ||
s: 'second', | ||
ss: 'seconds', | ||
m: 'minute', | ||
mm: 'minutes', | ||
h: 'hour', | ||
hh: 'hours', | ||
d: 'day', | ||
dd: 'days', | ||
w: 'week', | ||
ww: 'weeks', | ||
M: 'month', | ||
MM: 'months', | ||
y: 'year', | ||
yy: 'years' | ||
}, | ||
durationLabelsShort: { | ||
S: 'msec', | ||
SS: 'msecs', | ||
s: 'sec', | ||
ss: 'secs', | ||
m: 'min', | ||
mm: 'mins', | ||
h: 'hr', | ||
hh: 'hrs', | ||
d: 'dy', | ||
dd: 'dys', | ||
w: 'wk', | ||
ww: 'wks', | ||
M: 'mo', | ||
MM: 'mos', | ||
y: 'yr', | ||
yy: 'yrs' | ||
}, | ||
durationTimeTemplates: { | ||
HMS: 'h:mm:ss', | ||
HM: 'h:mm', | ||
MS: 'm:ss' | ||
}, | ||
durationLabelTypes: [ | ||
{ type: "standard", string: "__" }, | ||
{ type: "short", string: "_" } | ||
], | ||
durationPluralKey: function (token, integerValue, decimalValue) { | ||
// Singular for a value of `1`, but not for `1.0`. | ||
if (integerValue === 1 && decimalValue === null) { | ||
return token; | ||
} | ||
return token + token; | ||
} | ||
}); | ||
``` | ||
moment.duration.fn.format.defaults.hours = /h+/; | ||
moment.duration(123, "minutes").format("h H", 2); | ||
// "2.04 H" | ||
``` | ||
#### Minutes | ||
##### Creating a new Moment `locale` extension | ||
Default minutes token regexp: `/m+/` | ||
The duration extensions for a new locale might look something like the following example, which includes an additional unit label type, a custom time-notation template, and an additional form of plural. | ||
Define minutes token to use only lower-case `n`. | ||
New types of duration labels must have a key that begins with `durationLabels` and must be enumerated in `durationLabelTypes`. | ||
This locale uses a single token `"s"` for the singular label, a double token `"ss"` for the plural label when the value is `2`, and a triple token `"sss"` for the plural lable for values greater than `3`. For brevity, only labels for the `seconds` type are included. | ||
Unit labels are replaced after the format template string is tokenized, so they need not be escaped. Time-notation templates are replaced before the format template string is tokenized, so they must be escaped. | ||
```javascript | ||
moment.updateLocale('sample', { | ||
durationLabelsLong: { | ||
s: 'singular long second', | ||
ss: 'first long plural seconds', | ||
sss: 'next long plural seconds' | ||
// ... | ||
}, | ||
durationLabelsStandard: { | ||
s: 'singular second', | ||
ss: 'first plural seconds', | ||
sss: 'next plural seconds' | ||
// ... | ||
}, | ||
durationLabelsShort: { | ||
s: 'singular sec', | ||
ss: 'first plural secs', | ||
sss: 'next plural secs' | ||
// ... | ||
}, | ||
durationTimeTemplates: { | ||
HS: 'hh[h].ssss[s]' | ||
// ... | ||
}, | ||
durationLabelTypes: [ | ||
{ type: "long", string: "___" }, | ||
{ type: "standard", string: "__" }, | ||
{ type: "short", string: "_" } | ||
], | ||
durationPluralKey: function (token, integerValue, decimalValue) { | ||
// Decimal value does not affect unit label for this locale. | ||
// "xxx" for > 2. | ||
if (integerValue > 2) { | ||
return token + token + token; | ||
} | ||
// "x" for === 1. | ||
if (integerValue === 1) { | ||
return token; | ||
} | ||
// "xx" for others. | ||
return token + token; | ||
} | ||
}); | ||
``` | ||
moment.duration.fn.format.defaults.minutes = /n+/; | ||
moment.duration(123, "seconds").format("n:ss"); | ||
// "2:03" | ||
``` | ||
#### Seconds | ||
The function for `durationPluralKey` is passed three arguments: | ||
Default seconds token regexp: `/s+/` | ||
- `token` | ||
#### Milliseconds | ||
String. A single character representing the unit type. | ||
Default milliseconds token regexp: `/S+/` | ||
``` | ||
years: y | ||
months: M | ||
weeks: w | ||
days: d | ||
hours: h | ||
minutes: m | ||
seconds: s | ||
ms: S | ||
``` | ||
#### General | ||
- `integerValue` | ||
Default general token regexp: `/.+?/` | ||
Number. The integer portion of the token's value. | ||
Not sure why you'd want to redefine the general token regexp, but you can. Just make sure it's lazy so the other token expressions can do their jobs. | ||
- `decimalValue` | ||
Number. The decimal fraction portion of the token's value. |
$(document).ready(function() { | ||
module("Moment Duration Format"); | ||
moment.duration.fn.format.defaults.userLocale = "en-US"; | ||
@@ -37,2 +38,4 @@ test("Basic Use", function () { | ||
equal(moment.duration(1, "minutes").format("mm ss"), "01 00"); | ||
equal(moment.duration(15, "seconds").format("ssss sss ss s", { useGrouping: false }), "0015 0015 0015 0015"); | ||
equal(moment.duration(15, "seconds").format("s ss sss ssss"), "15 15 15 15"); | ||
}); | ||
@@ -56,15 +59,33 @@ | ||
equal(moment.duration(30, "seconds").format("m", 4), "0.5000"); | ||
equal(moment.duration(40, "seconds").format("m", 5), "0.66666"); | ||
equal(moment.duration(45, "seconds").format("m", 1), "0.7"); | ||
equal(moment.duration(59, "seconds").format("m", 0), "0"); | ||
equal(moment.duration(59, "seconds").format("m"), "0"); | ||
equal(moment.duration(40, "seconds").format("m", 5), "0.66667"); | ||
}); | ||
test("Zero Precision", function () { | ||
equal(moment.duration(59, "seconds").format("m", 0), "1"); | ||
equal(moment.duration(59, "seconds").format("m"), "1"); | ||
equal(moment.duration(59, "seconds").format("m", 0, { trunc: true }), "0"); | ||
equal(moment.duration(59, "seconds").format("m", { trunc: true }), "0"); | ||
}); | ||
test("Negative Precision", function () { | ||
equal(moment.duration(15, "seconds").format("s", -1), "20"); | ||
equal(moment.duration(123, "seconds").format("s", -2), "100"); | ||
}); | ||
test("Positive Precision with Trunc", function () { | ||
equal(moment.duration(15, "seconds").format("m", 2, { trunc: true }), "0.25"); | ||
equal(moment.duration(20, "seconds").format("m", 3, { trunc: true }), "0.333"); | ||
equal(moment.duration(30, "seconds").format("m", 4, { trunc: true }), "0.5000"); | ||
equal(moment.duration(40, "seconds").format("m", 5, { trunc: true }), "0.66666"); | ||
}); | ||
test("Negative Precision with Trunc", function () { | ||
equal(moment.duration(15, "seconds").format("s", -1, { trunc: true }), "10"); | ||
equal(moment.duration(159, "seconds").format("s", -1, { trunc: true }), "150"); | ||
}); | ||
test("Multiple Token Instances", function () { | ||
equal(moment.duration(123, "seconds").format("s s s"), "123 123 123"); | ||
equal(moment.duration(123, "seconds").format("s s ssssss"), "123 123 000123"); | ||
equal(moment.duration(123, "seconds").format("s s ssssss"), "123 123 123"); | ||
equal(moment.duration(123, "seconds").format("ssssss s s"), "000,123 000,123 000,123"); | ||
}); | ||
@@ -80,3 +101,3 @@ | ||
// this test ensures the internal duration/format math remains consistent | ||
equal(moment.duration(100000000013, "ms").format("y[y] M[mo] w[w] d[d] h[h] m[m] s[s] S[ms]"), "3y 2mo 0w 2d 9h 46m 40s 13ms"); | ||
equal(moment.duration(100000000013, "ms").format("y[y] M[mo] w[w] d[d] h[h] m[m] s[s] S[ms]"), "3y 2mos 0w 0d 9h 46m 40s 13ms"); | ||
}); | ||
@@ -89,15 +110,15 @@ | ||
equal(moment.duration(1, "years").format("d"), "365"); | ||
equal(moment.duration(1, "years").format("h"), "8760"); | ||
equal(moment.duration(1, "years").format("m"), "525600"); | ||
equal(moment.duration(1, "years").format("s"), "31536000"); | ||
equal(moment.duration(1, "years").format("S"), "31536000000"); | ||
equal(moment.duration(1, "years").format("h"), "8,760"); | ||
equal(moment.duration(1, "years").format("m"), "525,600"); | ||
equal(moment.duration(1, "years").format("s"), "31,536,000"); | ||
equal(moment.duration(1, "years").format("S"), "31,536,000,000"); | ||
}); | ||
test("Output To Greater Units", function () { | ||
equal(moment.duration(1, "milliseconds").format("y", 15), "0.000000000031709"); | ||
equal(moment.duration(1, "milliseconds").format("M", 13), "0.0000000003858"); | ||
equal(moment.duration(1, "milliseconds").format("w", 12), "0.000000001653"); | ||
equal(moment.duration(1, "milliseconds").format("d", 11), "0.00000001157"); | ||
equal(moment.duration(1, "milliseconds").format("h", 10), "0.0000002777"); | ||
equal(moment.duration(1, "milliseconds").format("m", 7), "0.0000166"); | ||
equal(moment.duration(1, "milliseconds").format("y", 13), "0.0000000000317"); | ||
equal(moment.duration(1, "milliseconds").format("M", 12), "0.000000000380"); | ||
equal(moment.duration(1, "milliseconds").format("w", 14), "0.00000000165344"); | ||
equal(moment.duration(1, "milliseconds").format("d", 10), "0.0000000116"); | ||
equal(moment.duration(1, "milliseconds").format("h", 9), "0.000000278"); | ||
equal(moment.duration(1, "milliseconds").format("m", 7), "0.0000167"); | ||
equal(moment.duration(1, "milliseconds").format("s", 3), "0.001"); | ||
@@ -107,30 +128,9 @@ equal(moment.duration(1, "milliseconds").format("S"), "1"); | ||
test("Custom Token Types List", function () { | ||
equal(moment.duration(12345, "seconds").format("d [days] m [minutes] h [(hours is not a token type now)]", { | ||
types: "escape years months weeks days minutes seconds milliseconds general" | ||
}), "205 minutes h (hours is not a token type now)"); | ||
}); | ||
test("Custom Escape RegExp", function () { | ||
equal(moment.duration(1234, "hours").format("d (days), h (hours)", { | ||
escape: /\((.+?)\)/ | ||
}), "51 days, 10 hours"); | ||
}); | ||
test("Custom Moment Token RegExp", function () { | ||
equal(moment.duration(1234, "days").format("x yrs", { | ||
years: /x+/, | ||
seconds: /z+/, | ||
precision: 2 | ||
}), "3.38 yrs"); | ||
}); | ||
test("Using Only Settings Argument", function () { | ||
equal(moment.duration(1234.55, "hours").format({ | ||
template: "d (days), h (hours)", | ||
escape: /\((.+?)\)/, | ||
template: "d [days], h [hours]", | ||
precision: 1 | ||
}), "51 days, 10.5 hours"); | ||
}), "51 days, 10.6 hours"); | ||
}); | ||
test("Zero Value Duration", function () { | ||
@@ -142,15 +142,15 @@ equal(moment.duration(0, "minutes").format("m"), "0"); | ||
equal(moment.duration(0, "minutes").format("m", 1), "0.0"); | ||
}); | ||
}); | ||
test("Default Template Function", function () { | ||
equal(moment.duration(100, "milliseconds").format(), "0"); | ||
equal(moment.duration(100, "milliseconds").format(), "100 milliseconds"); | ||
equal(moment.duration(100, "seconds").format(), "1:40"); | ||
equal(moment.duration(100, "minutes").format(), "1:40"); | ||
equal(moment.duration(100, "hours").format(), "4d 4h"); | ||
equal(moment.duration(100, "days").format(), "3m 10d"); | ||
equal(moment.duration(100, "weeks").format(), "23m 5d"); | ||
equal(moment.duration(100, "months").format(), "8y 4m"); | ||
equal(moment.duration(100, "years").format(), "100y"); | ||
equal(moment.duration(100, "minutes").format(), "1:40:00"); | ||
equal(moment.duration(100, "hours").format(), "4 days, 4 hours"); | ||
equal(moment.duration(100, "days").format(), "3 months, 9 days"); | ||
equal(moment.duration(100, "weeks").format(), "1 year, 10 months, 30 days"); | ||
equal(moment.duration(100, "months").format(), "8 years, 4 months"); | ||
equal(moment.duration(100, "years").format(), "100 years"); | ||
}); | ||
test("Custom Template Function", function () { | ||
@@ -165,3 +165,3 @@ equal(moment.duration(100, "days").format(function () { | ||
if (!array || !max) { return ret; } | ||
while (index < max) { | ||
@@ -171,15 +171,27 @@ ret[index] = callback(array[index], index); | ||
} | ||
return ret; | ||
} | ||
var types = this.types, | ||
dur = this.duration; | ||
return map(types.slice(1, -2), function (type) { | ||
return ((type === "months" || type === "milliseconds") ? type[0].toUpperCase() : type[0]) + " [" + type + "]"; | ||
}).join(", "); | ||
}), "3 months, 1 weeks, 3 days, 0 hours, 0 minutes, 0 seconds"); | ||
}), "3 months, 1 week, 2 days, 0 hours, 0 minutes, 0 seconds"); | ||
function customTemplate() { | ||
return this.duration.asSeconds() >= 86400 ? "w [weeks], d [days]" : "hh:mm:ss"; | ||
} | ||
equal(moment.duration(65, 'seconds').format(customTemplate, { | ||
trim: false | ||
}), "00:01:05"); | ||
equal(moment.duration(1347840, 'seconds').format(customTemplate, { | ||
trim: false | ||
}), "2 weeks, 2 days"); | ||
}); | ||
test("Negative Durations", function () { | ||
@@ -194,10 +206,518 @@ equal(moment.duration(-1, "years").format("y"), "-1"); | ||
equal(moment.duration(-1, "milliseconds").format("S"), "-1"); | ||
equal(moment.duration(-1, "years").format("s"), "-31536000"); | ||
equal(moment.duration(-1, "seconds").format("y", 15), "-0.000000031709791"); | ||
equal(moment.duration(-1, "years").format("s"), "-31,536,000"); | ||
equal(moment.duration(-1, "seconds").format("y", 10), "-0.0000000317"); | ||
equal(moment.duration(-65, "seconds").format("m:ss"), "-1:05"); | ||
equal(moment.duration(-65, "seconds").format("m:ss", 2), "-1:05.00"); | ||
equal(moment.duration(-65.667, "seconds").format("m:ss", 2), "-1:05.66"); | ||
equal(moment.duration(-65.667, "days").format("d", 2), "-65.66"); | ||
equal(moment.duration(-65.667, "seconds").format("m:ss", 2), "-1:05.67"); | ||
equal(moment.duration(-65.667, "days").format("d", 2), "-65.67"); | ||
equal(moment.duration(-65.667, "days").format("d [days], h [hours]"), "-65 days, 16 hours"); | ||
equal(moment.duration(-30, "seconds").format("m", 2), "-0.50"); | ||
}); | ||
}); | ||
test("Negative Durations that have zero value", function () { | ||
equal(moment.duration(-29, "seconds").format("m"), "0"); | ||
equal(moment.duration(-30, "seconds").format("m"), "-1"); | ||
equal(moment.duration(-30, "seconds").format("m", { trunc: true }), "0"); | ||
equal(moment.duration(-59, "seconds").format("m", { trunc: true }), "0"); | ||
}); | ||
test("Negative Durations with leading text", function () { | ||
equal(moment.duration(-42, "seconds").format("[Leading Text] s", { trim: false }), "Leading Text -42"); | ||
}); | ||
test("Negative Durations and Trimming", function () { | ||
equal(moment.duration(-42, "seconds").format("h:mm:ss"), "-42"); | ||
}); | ||
test("Stop Trimming with the * Character", function () { | ||
equal(moment.duration(15, "seconds").format("h:*mm:ss"), "0:15"); | ||
equal(moment.duration(15, "seconds").format("h:*mm:ss", { forceLength: true }), "00:15"); | ||
equal(moment.duration(15, "seconds").format("hh:*mm:ss"), "00:15"); | ||
equal(moment.duration(15, "seconds").format("*h:mm:ss"), "0:00:15"); | ||
}); | ||
// https://github.com/jsmreese/moment-duration-format/issues/59 | ||
test("Rounding errors", function () { | ||
equal(moment.duration(70300, "milliseconds").format("s", 1), "70.3"); | ||
equal(moment.duration(288957, "milliseconds").format("s", 1), "289.0"); | ||
equal(moment.duration(1087300, "milliseconds").format("s", 1), "1,087.3"); | ||
}); | ||
test("Show only the largest `x` tokens", function () { | ||
equal(moment.duration(1.55, "days").format("d [days], h [hours], m [minutes], s [seconds]", { largest: 2 }), "1 day, 13 hours"); | ||
equal(moment.duration(1454.4, "minutes").format("d [days], h [hours], m [minutes], s [seconds]", { largest: 2 }), "1 day, 14 minutes"); | ||
equal(moment.duration(1454.4, "minutes").format("d [days], h [hours], m [minutes], s [seconds]", { largest: 3 }), "1 day, 14 minutes, 24 seconds"); | ||
}); | ||
test("Trim both", function () { | ||
equal(moment.duration(1, "days").format("M [months], d [days], h [hours], m [minutes], s [seconds]", { trim: "both" }), "1 day"); | ||
equal(moment.duration(90000, "seconds").format("y [years], *M [months], d [days], h [hours], *m [minutes], s [seconds]", { trim: "both" }), "0 months, 1 day, 1 hour, 0 minutes"); | ||
equal(moment.duration(86460, "seconds").format("y [years], *M [months], d [days], h [hours], m [minutes], s [seconds]", { trim: "both" }), "0 months, 1 day, 0 hours, 1 minute"); | ||
equal(moment.duration(86460, "seconds").format("y [years], *M [months], d [days], h [hours], m [minutes], *s [seconds]", { trim: "both" }), "0 months, 1 day, 0 hours, 1 minute, 0 seconds"); | ||
equal(moment.duration(1, "days").format("y [years], M [months]", { trim: "both" }), "0 months"); | ||
equal(moment.duration(0, "days").format("M [months], d [days], h [hours], m [minutes]", { trim: "both" }), "0 minutes"); | ||
}); | ||
test("Trim both with largest", function () { | ||
equal(moment.duration(1.55, "days").format("M [months], d [days], h [hours], m [minutes], s [seconds]", { largest: 1, trim: "both" }), "1 day"); | ||
equal(moment.duration(1.5, "days").format("M [months], d [days], h [hours], m [minutes], s [seconds]", { largest: 3, trim: "both" }), "1 day, 12 hours"); | ||
}); | ||
test("usePlural", function () { | ||
equal(moment.duration(0, "ms").format("S [milliseconds]"), "0 milliseconds"); | ||
equal(moment.duration(1, "ms").format("S [milliseconds]"), "1 millisecond"); | ||
equal(moment.duration(0, "ms").format("S [msecs]"), "0 msecs"); | ||
equal(moment.duration(1, "ms").format("S [msecs]"), "1 msec"); | ||
equal(moment.duration(1, "ms").format("S [milliseconds]", { usePlural: false }), "1 milliseconds"); | ||
equal(moment.duration(0, "s").format("s [seconds]"), "0 seconds"); | ||
equal(moment.duration(1, "s").format("s [seconds]"), "1 second"); | ||
equal(moment.duration(0, "s").format("s [secs]"), "0 secs"); | ||
equal(moment.duration(1, "s").format("s [secs]"), "1 sec"); | ||
equal(moment.duration(1, "s").format("s [seconds]", { precision: 1 }), "1.0 seconds"); | ||
equal(moment.duration(1, "s").format("s [seconds]", { usePlural: false }), "1 seconds"); | ||
equal(moment.duration(0, "m").format("m [minutes]"), "0 minutes"); | ||
equal(moment.duration(1, "m").format("m [minutes]"), "1 minute"); | ||
equal(moment.duration(0, "m").format("m [mins]"), "0 mins"); | ||
equal(moment.duration(1, "m").format("m [mins]"), "1 min"); | ||
equal(moment.duration(1, "m").format("m [minutes]", { precision: 1 }), "1.0 minutes"); | ||
equal(moment.duration(1, "m").format("m [minutes]", { usePlural: false }), "1 minutes"); | ||
equal(moment.duration(0, "h").format("h [hours]"), "0 hours"); | ||
equal(moment.duration(1, "h").format("h [hours]"), "1 hour"); | ||
equal(moment.duration(0, "h").format("h [hrs]"), "0 hrs"); | ||
equal(moment.duration(1, "h").format("h [hrs]"), "1 hr"); | ||
equal(moment.duration(1, "h").format("h [hours]", { precision: 1 }), "1.0 hours"); | ||
equal(moment.duration(1, "h").format("h [hours]", { usePlural: false }), "1 hours"); | ||
equal(moment.duration(0, "d").format("d [days]"), "0 days"); | ||
equal(moment.duration(1, "d").format("d [days]"), "1 day"); | ||
equal(moment.duration(0, "d").format("d [dys]"), "0 dys"); | ||
equal(moment.duration(1, "d").format("d [dys]"), "1 dy"); | ||
equal(moment.duration(1, "d").format("d [days]", { precision: 1 }), "1.0 days"); | ||
equal(moment.duration(1, "d").format("d [days]", { usePlural: false }), "1 days"); | ||
equal(moment.duration(0, "w").format("w [weeks]"), "0 weeks"); | ||
equal(moment.duration(1, "w").format("w [weeks]"), "1 week"); | ||
equal(moment.duration(0, "w").format("w [wks]"), "0 wks"); | ||
equal(moment.duration(1, "w").format("w [wks]"), "1 wk"); | ||
equal(moment.duration(1, "w").format("w [weeks]", { precision: 1 }), "1.0 weeks"); | ||
equal(moment.duration(1, "w").format("w [weeks]", { usePlural: false }), "1 weeks"); | ||
}); | ||
test("usePlural Months and Years", function () { | ||
equal(moment.duration(0, "months").format("M [months]"), "0 months"); | ||
equal(moment.duration(1, "months").format("M [months]"), "1 month"); | ||
equal(moment.duration(0, "months").format("M [mos]"), "0 mos"); | ||
equal(moment.duration(1, "months").format("M [mos]"), "1 mo"); | ||
equal(moment.duration(1, "months").format("M [months]", { precision: 1 }), "1.0 months"); | ||
equal(moment.duration(1, "months").format("M [months]", { usePlural: false }), "1 months"); | ||
equal(moment.duration(0, "y").format("y [years]"), "0 years"); | ||
equal(moment.duration(1, "y").format("y [years]"), "1 year"); | ||
equal(moment.duration(0, "y").format("y [yrs]"), "0 yrs"); | ||
equal(moment.duration(1, "y").format("y [yrs]"), "1 yr"); | ||
equal(moment.duration(1, "y").format("y [years]", { precision: 1 }), "1.0 years"); | ||
equal(moment.duration(1, "y").format("y [years]", { usePlural: false }), "1 years"); | ||
}); | ||
test("usePlural, multiple tokens", function () { | ||
equal(moment.duration(3661, "s").format("h [hours], m [minutes], s [seconds]"), "1 hour, 1 minute, 1 second"); | ||
equal(moment.duration(3661, "s").format("h [hours], m [minutes], s [seconds]", { usePlural: false }), "1 hours, 1 minutes, 1 seconds"); | ||
equal(moment.duration(61, "s").format("m [minutes], s [seconds]", { precision: 1 }), "1 minute, 1.0 seconds"); | ||
}); | ||
test("usePlural with rounding", function () { | ||
equal(moment.duration(119, "seconds").format("m [minutes]"), "2 minutes"); | ||
equal(moment.duration(1.25, "s").format("s [secs]"), "1 sec"); | ||
equal(moment.duration(1.5, "s").format("s [secs]"), "2 secs"); | ||
equal(moment.duration(1.75, "s").format("s [secs]"), "2 secs"); | ||
equal(moment.duration(2, "s").format("s [secs]"), "2 secs"); | ||
}); | ||
test("Automatic Locale-based units", function () { | ||
equal(moment.duration(3661, "s").format("h _, m _, s _"), "1 hr, 1 min, 1 sec"); | ||
equal(moment.duration(3661, "s").format("h _, m _, s _", { usePlural: false }), "1 hr, 1 min, 1 sec"); | ||
equal(moment.duration(61, "s").format("m _, s _", { precision: 1 }), "1 min, 1.0 secs"); | ||
equal(moment.duration(1, "milliseconds").format("S _"), "1 msec"); | ||
equal(moment.duration(1, "seconds").format("s _"), "1 sec"); | ||
equal(moment.duration(1, "minutes").format("m _"), "1 min"); | ||
equal(moment.duration(1, "hours").format("h _"), "1 hr"); | ||
equal(moment.duration(1, "days").format("d _"), "1 dy"); | ||
equal(moment.duration(1, "weeks").format("w _"), "1 wk"); | ||
equal(moment.duration(1, "months").format("M _"), "1 mo"); | ||
equal(moment.duration(1, "years").format("y _"), "1 yr"); | ||
equal(moment.duration(1, "milliseconds").format("S __"), "1 millisecond"); | ||
equal(moment.duration(1, "seconds").format("s __"), "1 second"); | ||
equal(moment.duration(1, "minutes").format("m __"), "1 minute"); | ||
equal(moment.duration(1, "hours").format("h __"), "1 hour"); | ||
equal(moment.duration(1, "days").format("d __"), "1 day"); | ||
equal(moment.duration(1, "weeks").format("w __"), "1 week"); | ||
equal(moment.duration(1, "months").format("M __"), "1 month"); | ||
equal(moment.duration(1, "years").format("y __"), "1 year"); | ||
}); | ||
test("Automatic Locale-based time notation", function () { | ||
equal(moment.duration(3661, "seconds").format("_HMS_"), "1:01:01"); | ||
equal(moment.duration(3661, "seconds").format("_HM_"), "1:01"); | ||
equal(moment.duration(61, "seconds").format("_MS_"), "1:01"); | ||
}); | ||
test("Locale missing durations labels", function () { | ||
moment.locale('fr', { | ||
months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), | ||
monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), | ||
monthsParseExact : true, | ||
weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), | ||
weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), | ||
weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), | ||
weekdaysParseExact : true, | ||
longDateFormat : { | ||
LT : 'HH:mm', | ||
LTS : 'HH:mm:ss', | ||
L : 'DD/MM/YYYY', | ||
LL : 'D MMMM YYYY', | ||
LLL : 'D MMMM YYYY HH:mm', | ||
LLLL : 'dddd D MMMM YYYY HH:mm' | ||
}, | ||
calendar : { | ||
sameDay : '[Aujourd’hui à] LT', | ||
nextDay : '[Demain à] LT', | ||
nextWeek : 'dddd [à] LT', | ||
lastDay : '[Hier à] LT', | ||
lastWeek : 'dddd [dernier à] LT', | ||
sameElse : 'L' | ||
}, | ||
relativeTime : { | ||
future : 'dans %s', | ||
past : 'il y a %s', | ||
s : 'quelques secondes', | ||
m : 'une minute', | ||
mm : '%d minutes', | ||
h : 'une heure', | ||
hh : '%d heures', | ||
d : 'un jour', | ||
dd : '%d jours', | ||
M : 'un mois', | ||
MM : '%d mois', | ||
y : 'un an', | ||
yy : '%d ans' | ||
}, | ||
dayOfMonthOrdinalParse : /\d{1,2}(er|e)/, | ||
ordinal : function (number) { | ||
return number + (number === 1 ? 'er' : 'e'); | ||
}, | ||
meridiemParse : /PD|MD/, | ||
isPM : function (input) { | ||
return input.charAt(0) === 'M'; | ||
}, | ||
// In case the meridiem units are not separated around 12, then implement | ||
// this function (look at locale/id.js for an example). | ||
// meridiemHour : function (hour, meridiem) { | ||
// return /* 0-23 hour, given meridiem token and hour 1-12 */ ; | ||
// }, | ||
meridiem : function (hours, minutes, isLower) { | ||
return hours < 12 ? 'PD' : 'MD'; | ||
}, | ||
week : { | ||
dow : 1, // Monday is the first day of the week. | ||
doy : 4 // The week that contains Jan 4th is the first week of the year. | ||
} | ||
}); | ||
equal(moment.duration(3661, "s").format("h _, m _, s _"), "1 hr, 1 min, 1 sec"); | ||
equal(moment.duration(3661, "s").format("h __, m __, s __"), "1 hour, 1 minute, 1 second"); | ||
equal(moment.duration(3661, "seconds").format("_HMS_"), "1:01:01"); | ||
equal(moment.duration(3661, "seconds").format("_HM_"), "1:01"); | ||
equal(moment.duration(61, "seconds").format("_MS_"), "1:01"); | ||
moment.locale("en"); | ||
}); | ||
test("useLeftUnits", function () { | ||
equal(moment.duration(0, "s").format("[seconds] s", { useLeftUnits: true }), "seconds 0"); | ||
equal(moment.duration(1, "s").format("[seconds] s", { useLeftUnits: true }), "second 1"); | ||
equal(moment.duration(0, "s").format("__ s", { useLeftUnits: true }), "seconds 0"); | ||
equal(moment.duration(1, "s").format("__ s", { useLeftUnits: true }), "second 1"); | ||
equal(moment.duration(0, "s").format("_ s", { useLeftUnits: true }), "secs 0"); | ||
equal(moment.duration(1, "s").format("_ s", { useLeftUnits: true }), "sec 1"); | ||
equal(moment.duration(1, "s").format("[seconds] s", { precision: 1, useLeftUnits: true }), "seconds 1.0"); | ||
equal(moment.duration(1, "s").format("[seconds] s", { usePlural: false, useLeftUnits: true }), "seconds 1"); | ||
equal(moment.duration(3661, "s").format("[hours] h, [minutes] m, [seconds] s", { useLeftUnits: true }), "hour 1, minute 1, second 1"); | ||
equal(moment.duration(3661, "s").format("[hours] h, [minutes] m, [seconds] s", { usePlural: false, useLeftUnits: true }), "hours 1, minutes 1, seconds 1"); | ||
equal(moment.duration(61, "s").format("[minutes] m, [seconds] s", { precision: 1, useLeftUnits: true }), "minute 1, seconds 1.0"); | ||
equal(moment.duration(61, "minutes").format("__ d, __ h, __ m, __ s", { useLeftUnits: true, trim: "both" }), "hour 1, minute 1"); | ||
equal(moment.duration(61, "minutes").format("__ s, __ m, __ h, __ d", { useLeftUnits: true, trim: "both" }), "minute 1, hour 1"); | ||
equal(moment.duration(61, "minutes").format("__ s, __ m, __ h, __ d", { useLeftUnits: true, trim: "both", largest: 1 }), "hour 1"); | ||
}); | ||
test("userLocale and useGrouping", function () { | ||
equal(moment.duration(100000.1, "seconds").format("s", { userLocale: "en-GB", precision: 2 }), "100,000.10"); | ||
equal(moment.duration(100000.1, "seconds").format("s", { userLocale: "en-GB", precision: 2, useGrouping: false }), "100000.10"); | ||
equal(moment.duration(100000.1, "seconds").format("s", { userLocale: "de-DE", precision: 2 }), "100.000,10"); | ||
equal(moment.duration(100000.1, "seconds").format("s", { userLocale: "de-DE", precision: 2, useGrouping: false }), "100000,10"); | ||
equal(moment.duration(100000.1, "seconds").format("s", { userLocale: "en", precision: 2 }), "100,000.10"); | ||
}); | ||
test("useSignificantDigits", function () { | ||
equal(moment.duration(99999, "seconds").format("s", { useSignificantDigits: true, precision: 2 }), "100,000"); | ||
equal(moment.duration(99.99, "seconds").format("s", { useSignificantDigits: true, precision: 3 }), "100"); | ||
equal(moment.duration(99.9944, "seconds").format("s", { useSignificantDigits: true, precision: 5 }), "99.994"); | ||
equal(moment.duration(99.944, "seconds").format("m [minutes], s [seconds]", { useSignificantDigits: true, precision: 3 }), "1 minute, 40 seconds"); | ||
equal(moment.duration(99.944, "seconds").format("m [minutes], s [seconds]", { useSignificantDigits: true, precision: 5 }), "1 minute, 39.94 seconds"); | ||
equal(moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, precision: 3 }), "1 day, 3 hours, 50 minutes"); | ||
equal(moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, precision: 5 }), "1 day, 3 hours, 46 minutes, 40 seconds"); | ||
equal(moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, precision: 6 }), "1 day, 3 hours, 46 minutes, 39 seconds"); | ||
equal(moment.duration(35, "hours").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, precision: 2 }), "1 day, 10 hours"); | ||
equal(moment.duration(39, "hours").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, precision: 2 }), "1 day, 20 hours"); | ||
}); | ||
test("useSignificantDigits and trunc", function () { | ||
equal(moment.duration(99.99, "seconds").format("s", { useSignificantDigits: true, trunc: true, precision: 3 }), "99.9"); | ||
equal(moment.duration(99.944, "seconds").format("m [minutes], s [seconds]", { useSignificantDigits: true, trunc: true, precision: 3 }), "1 minute, 39 seconds"); | ||
equal(moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, trunc: true, precision: 5 }), "1 day, 3 hours, 46 minutes, 30 seconds"); | ||
equal(moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, trunc: true, precision: 3 }), "1 day, 3 hours, 40 minutes"); | ||
equal(moment.duration(35, "hours").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, trunc: true, precision: 2 }), "1 day, 10 hours"); | ||
equal(moment.duration(39, "hours").format("d [days], h [hours], m [minutes], s [seconds]", { useSignificantDigits: true, trunc: true, precision: 2 }), "1 day, 10 hours"); | ||
}); | ||
test("Documentation examples", function () { | ||
equal(moment.duration(123, "minutes").format(), "2:03:00"); | ||
equal(moment.duration(123, "months").format(), "10 years, 3 months"); | ||
equal(moment.duration(123, "minutes").format("h:mm"), "2:03"); | ||
equal(moment.duration(123, "minutes").format("h [hrs], m [min]"), "2 hrs, 3 mins"); | ||
equal(moment.duration(3661, "seconds").format("h:mm:ss"), "1:01:01"); | ||
equal(moment.duration(15, "seconds").format("sss [s]"), "015 s"); | ||
equal(moment.duration(123, "minutes").format("h [hrs]"), "2 hrs"); | ||
equal(moment.duration(123, "minutes").format("h [hrs]", 2), "2.05 hrs"); | ||
equal(moment.duration(223, "minutes").format("m [min]", -2), "200 mins"); | ||
equal(moment.duration(123, "minutes").format({ template: "h [hrs]", precision: 2 }), "2.05 hrs"); | ||
equal(moment.duration(123, "minutes").format("s [seconds], m [minutes], h [hours], d [days]"), "0 seconds, 3 minutes, 2 hours"); | ||
equal(moment.duration(123, "minutes").format("d[d] h:mm:ss", { trim: false }), "0d 2:03:00"); | ||
equal(moment.duration(123, "minutes").format("d[d] h:mm:ss"), "2:03:00"); | ||
equal(moment.duration(123, "minutes").format("d[d] h:mm:ss", { trim: "large" }), "2:03:00"); | ||
equal(moment.duration(0, "minutes").format("d[d] h:mm:ss", { trim: "large" }), "0"); | ||
equal(moment.duration(123, "minutes").format("d[d] h:mm:ss", { trim: "small" }), "0d 2:03"); | ||
equal(moment.duration(0, "minutes").format("d[d] h:mm:ss", { trim: "small" }), "0d"); | ||
equal(moment.duration(123, "minutes").format("d[d] h[h] m[m] s[s]", { trim: "both" }), "2h 3m"); | ||
equal(moment.duration(0, "minutes").format("d[d] h[h] m[m] s[s]", { trim: "both" }), "0s"); | ||
equal(moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { trim: "mid" }), "0w 1d 1m 0s"); | ||
equal(moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { trim: "large mid" }), "1d 1m 0s"); | ||
equal(moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { trim: "small mid" }), "0w 1d 1m"); | ||
equal(moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { trim: "both mid" }), "1d 1m"); | ||
equal(moment.duration(0, "minutes").format("w[w] d[d] h[h] m[m] s[s]", { trim: "both mid" }), "0s"); | ||
equal(moment.duration(0, "minutes").format("d[d] h:mm:ss", { trim: "large final" }), ""); | ||
equal(moment.duration(0, "minutes").format("d[d] h:mm:ss", { trim: "small final" }), ""); | ||
equal(moment.duration(0, "minutes").format("d[d] h[h] m[m] s[s]", { trim: "both final" }), ""); | ||
equal(moment.duration(0, "minutes").format("d[d] h[h] m[m] s[s]", { trim: "all" }), ""); | ||
equal(moment.duration(7322, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", { largest: 2 }), "2 hours, 2 minutes"); | ||
equal(moment.duration(1216922, "seconds").format("y [years], w [weeks], d [days], h [hours], m [minutes], s [seconds]", { largest: 2 }), "2 weeks, 2 hours"); | ||
equal(moment.duration(23, "minutes").format("d[d] h:mm:ss", { stopTrim: "h" }), "0:23:00"); | ||
equal(moment.duration(23, "minutes").format("d[d] *h:mm:ss"), "0:23:00"); | ||
equal(moment.duration(2, "hours").format("y [years], d [days], h [hours], m [minutes], s [seconds]", { trim: "both", stopTrim: "d m" }), "0 days, 2 hours, 0 minutes"); | ||
equal(moment.duration(2, "hours").format("y [years], *d [days], h [hours], *m [minutes], s [seconds]", { trim: "both" }), "0 days, 2 hours, 0 minutes"); | ||
equal(moment.duration(2, "hours").format("y [years], d [days], h [hours], m [minutes], s [seconds]", { trim: "both", stopTrim: "d m", largest: 2 }), "2 hours"); | ||
equal(moment.duration(179, "seconds").format("m [minutes]"), "3 minutes"); | ||
equal(moment.duration(3780, "seconds").format("h [hours]", 1), "1.1 hours"); | ||
equal(moment.duration(179, "seconds").format("m [minutes]", { trunc: true }), "2 minutes"); | ||
equal(moment.duration(3780, "seconds").format("h [hours]", 1, { trunc: true }), "1.0 hours"); | ||
equal(moment.duration(59, "seconds").format("d [days], h [hours], m [minutes]", { trunc: true, trim: "both" }), "0 minutes"); | ||
equal(moment.duration(59, "seconds").format("d [days], h [hours], m [minutes]", { trunc: true, trim: "all" }), ""); | ||
equal(moment.duration(59, "seconds").format("d [days], h [hours], m [minutes]", { trunc: true, largest: 1 }), ""); | ||
equal(moment.duration(123, "seconds").format("h:mm:ss"), "2:03"); | ||
equal(moment.duration(123, "seconds").format("hh:mm:ss"), "02:03"); | ||
equal(moment.duration(123, "seconds").format("h:mm:ss", { forceLength: true }), "02:03"); | ||
equal(moment.duration(1234567, "seconds").format("m [minutes]", 3), "20,576.117 minutes"); | ||
equal(moment.duration(1, "minutes").format("m [minutes]"), "1 minute"); | ||
equal(moment.duration(1, "minutes").format("m [mins]"), "1 min"); | ||
equal(moment.duration(1, "minutes").format("m [minutes]", { usePlural: false }), "1 minutes"); | ||
equal(moment.duration(1, "minutes").format("m [mins]", { usePlural: false }), "1 mins"); | ||
equal(moment.duration(1, "minutes").format("m [minutes]", 2), "1.00 minutes"); | ||
equal(moment.duration(7322, "seconds").format("_ h, _ m, _ s", { useLeftUnits: true }), "hrs 2, mins 2, secs 2"); | ||
equal(moment.duration(1234, "seconds").format("s [seconds]"), "1,234 seconds"); | ||
equal(moment.duration(1234, "seconds").format("s [seconds]", { useGrouping: false }), "1234 seconds"); | ||
equal(moment.duration(1234567, "seconds").format("m [minutes]", 3, { userLocale: "de-DE" }), "20.576,117 minutes"); | ||
}); | ||
test("Pluralize singular unit labels", function () { | ||
equal(moment.duration(2, "minutes").format("m [minute]"), "2 minutes"); | ||
equal(moment.duration(2, "minutes").format("m [min]"), "2 mins"); | ||
equal(moment.duration(2, "minutes").format("m [minute]", { | ||
usePlural: false | ||
}), "2 minute"); | ||
equal(moment.duration(2, "minutes").format("m [min]", { | ||
usePlural: false | ||
}), "2 min"); | ||
}); | ||
test("minValue and maxValue", function () { | ||
equal(moment.duration(59, "seconds").format("h [hours], m [minutes]", { minValue: 1 }), "< 1 minute"); | ||
equal(moment.duration(59, "seconds").format("h [hours], m [minutes]", { minValue: 1, trim: "both" }), "< 1 minute"); | ||
equal(moment.duration(3629, "seconds").format("h [hours], m [minutes]", { minValue: 1, trim: "both" }), "1 hour"); | ||
equal(moment.duration(59, "seconds").format("h [hours], m [minutes]", { minValue: 1, trunc: true, trim: "all" }), ""); | ||
equal(moment.duration(-59, "seconds").format("h [hours], m [minutes]", { minValue: 1 }), "> -1 minute"); | ||
equal(moment.duration(59, "seconds").format("h [hours], m [minutes]", { minValue: 1, trim: false, largest: 2 }), "< 1 minute"); | ||
equal(moment.duration(15, "days").format("w [weeks]", { maxValue: 2 }), "> 2 weeks"); | ||
equal(moment.duration(-15, "days").format("w [weeks]", { maxValue: 2 }), "< -2 weeks"); | ||
equal(moment.duration(15, "days").format("w [weeks], d [days]", { maxValue: 2, trim: false, largest: 2 }), "> 2 weeks"); | ||
}); | ||
test("stopTrim", function () { | ||
equal(moment.duration(2, "hours").format("y [years], d [days], h [hours], m [minutes], s [seconds]", { trim: "both", stopTrim: ["d", "m"] }), "0 days, 2 hours, 0 minutes"); | ||
equal(moment.duration(2, "hours").format("y [years], d [days], h [hours], m [minutes], s [seconds]", { trim: "both", stopTrim: ["d", "m"], largest: 2 }), "2 hours"); | ||
}); | ||
test("Milliseconds token length === 2", function () { | ||
equal(moment.duration(1003141).format("mm:ss:SS", { trim: false, precision: 0 }), "16:43:14"); | ||
equal(moment.duration(1003145).format("mm:ss:SS", { trim: false, precision: 0 }), "16:43:14"); | ||
equal(moment.duration(1003149).format("mm:ss:SS", { trim: false, precision: 0 }), "16:43:14"); | ||
equal(moment.duration(1003101).format("mm:ss:SS", { trim: false, precision: 0 }), "16:43:10"); | ||
equal(moment.duration(1003099).format("mm:ss:SS", { trim: false, precision: 0 }), "16:43:09"); | ||
equal(moment.duration(1003091).format("mm:ss:SS", { trim: false, precision: 0 }), "16:43:09"); | ||
equal(moment.duration(9, "milliseconds").format("mm:ss:SS", { trim: false }), "00:00:00"); | ||
equal(moment.duration(10, "milliseconds").format("mm:ss:SS", { trim: false }), "00:00:01"); | ||
equal(moment.duration(999, "milliseconds").format("mm:ss:SS", { trim: false }), "00:00:99"); | ||
equal(moment.duration(1011, "milliseconds").format("mm:ss:SS", { trim: false }), "00:01:01"); | ||
}); | ||
test("Remove leading/trailing space, comma, colon, dot", function () { | ||
equal(moment.duration(1, "second").format(".s "), "1"); | ||
equal(moment.duration(1, "second").format(":s,"), "1"); | ||
}); | ||
test("Invalid durations", function () { | ||
equal(moment.duration(NaN, "second").format(), "0 seconds"); | ||
}); | ||
test("Custom Locale labels, label types, pluralizer", function () { | ||
// Borowing moment's "fr" locale. | ||
moment.locale('test', { | ||
months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), | ||
monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), | ||
monthsParseExact : true, | ||
weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), | ||
weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), | ||
weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), | ||
weekdaysParseExact : true, | ||
longDateFormat : { | ||
LT : 'HH:mm', | ||
LTS : 'HH:mm:ss', | ||
L : 'DD/MM/YYYY', | ||
LL : 'D MMMM YYYY', | ||
LLL : 'D MMMM YYYY HH:mm', | ||
LLLL : 'dddd D MMMM YYYY HH:mm' | ||
}, | ||
calendar : { | ||
sameDay : '[Aujourd’hui à] LT', | ||
nextDay : '[Demain à] LT', | ||
nextWeek : 'dddd [à] LT', | ||
lastDay : '[Hier à] LT', | ||
lastWeek : 'dddd [dernier à] LT', | ||
sameElse : 'L' | ||
}, | ||
relativeTime : { | ||
future : 'dans %s', | ||
past : 'il y a %s', | ||
s : 'quelques secondes', | ||
m : 'une minute', | ||
mm : '%d minutes', | ||
h : 'une heure', | ||
hh : '%d heures', | ||
d : 'un jour', | ||
dd : '%d jours', | ||
M : 'un mois', | ||
MM : '%d mois', | ||
y : 'un an', | ||
yy : '%d ans' | ||
}, | ||
dayOfMonthOrdinalParse : /\d{1,2}(er|e)/, | ||
ordinal : function (number) { | ||
return number + (number === 1 ? 'er' : 'e'); | ||
}, | ||
meridiemParse : /PD|MD/, | ||
isPM : function (input) { | ||
return input.charAt(0) === 'M'; | ||
}, | ||
// In case the meridiem units are not separated around 12, then implement | ||
// this function (look at locale/id.js for an example). | ||
// meridiemHour : function (hour, meridiem) { | ||
// return /* 0-23 hour, given meridiem token and hour 1-12 */ ; | ||
// }, | ||
meridiem : function (hours, minutes, isLower) { | ||
return hours < 12 ? 'PD' : 'MD'; | ||
}, | ||
week : { | ||
dow : 1, // Monday is the first day of the week. | ||
doy : 4 // The week that contains Jan 4th is the first week of the year. | ||
}, | ||
durationLabelsLong: { | ||
s: 'sec1.Long', | ||
ss: 'sec2.Long', | ||
sss: 'sec3.Long', | ||
m: 'min1.Long', | ||
mm: 'min2.Long', | ||
mmm: 'min3.Long' | ||
}, | ||
durationLabelsStandard: { | ||
s: 'sec1.Standard', | ||
ss: 'sec2.Standard', | ||
sss: 'sec3.Standard', | ||
m: 'min1.Standard', | ||
mm: 'min2.Standard', | ||
mmm: 'min3.Standard' | ||
}, | ||
durationLabelsShort: { | ||
s: 'sec1.Short', | ||
ss: 'sec2.Short', | ||
sss: 'sec3.Short', | ||
m: 'min1.Short', | ||
mm: 'min2.Short', | ||
mmm: 'min3.Short' | ||
}, | ||
durationTimeTemplates: { | ||
MS: 'mmm:sss' | ||
}, | ||
durationLabelTypes: [ | ||
{ type: "long", string: "___" }, | ||
{ type: "standard", string: "__" }, | ||
{ type: "short", string: "_" } | ||
], | ||
durationPluralKey: function (token, integerValue, decimalValue) { | ||
// Decimal value does not affect plural label. | ||
// "xxx" for > 2. | ||
if (integerValue > 2) { | ||
return token + token + token; | ||
} | ||
// "x" for === 1. | ||
if (integerValue === 1) { | ||
return token; | ||
} | ||
// "xx" for others. | ||
return token + token; | ||
} | ||
}); | ||
equal(moment.duration(61, "s").format("m _, s _"), "1 min1.Short, 1 sec1.Short"); | ||
equal(moment.duration(61, "s").format("m __, s __"), "1 min1.Standard, 1 sec1.Standard"); | ||
equal(moment.duration(61, "s").format("m ___, s ___"), "1 min1.Long, 1 sec1.Long"); | ||
equal(moment.duration(122, "s").format("m _, s _"), "2 min2.Short, 2 sec2.Short"); | ||
equal(moment.duration(122, "s").format("m __, s __"), "2 min2.Standard, 2 sec2.Standard"); | ||
equal(moment.duration(122, "s").format("m ___, s ___"), "2 min2.Long, 2 sec2.Long"); | ||
equal(moment.duration(183, "s").format("m _, s _"), "3 min3.Short, 3 sec3.Short"); | ||
equal(moment.duration(183, "s").format("m __, s __"), "3 min3.Standard, 3 sec3.Standard"); | ||
equal(moment.duration(183, "s").format("m ___, s ___"), "3 min3.Long, 3 sec3.Long"); | ||
equal(moment.duration(61, "seconds").format("_MS_"), "001:001"); | ||
moment.locale("en"); | ||
}); | ||
// tests TODO: | ||
// Document updated plural API and locale extensions. | ||
// Write tests for updated API that include a 3-way plural with an additional label type. | ||
}); |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
547508
15041
969