Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

moment-duration-format

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

moment-duration-format - npm Package Compare versions

Comparing version 1.3.0 to 2.0.0

4

bower.json
{
"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": [

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc