Moment Duration Format
Format plugin for the Moment Duration object.
This is a plugin to the Moment.js JavaScript date library to add comprehensive formatting to Moment Durations.
Format template grammar is patterned on the existing Moment Date format template grammar, with a few modifications because durations are fundamentally different from dates.
This plugin does not have any dependencies beyond Moment.js itself, and may be used in the browser and in Node.js.
Installation
The plugin depends on moment.js, which is not specified as a package dependency in the currently published version.
Node.js
npm install moment-duration-format
Bower
bower install moment-duration-format
Browser
<script src="path/to/moment-duration-format.js"></script>
Usage
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.
var moment = require("moment");
var momentDurationFormatSetup = require("moment-duration-format");
The plugin exports the init function so that duration format can be initialized on other moment instances.
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.
var moment = require("moment-timezone");
var momentDurationFormatSetup = require("moment-duration-format");
momentDurationFormatSetup(moment);
typeof moment.duration.fn.format === "function"
Basics
The duration format method can format any moment duration. If no template or other arguments are provided, the default template function will generate a template string based on the duration's value.
moment.duration(123, "minutes").format();
moment.duration(123, "months").format();
The duration format method may be called with three optional arguments:
moment.duration.format([template] [, precision] [, settings])
Invalid Durations
Invalid durations are treated as having a value of 0
for formatting.
var invalidDuration = moment.duration(NaN, "second");
invalidDuration.isValid();
invalidDuration.format();
Template
template
(string|function) is the string used to create the formatted output, or a function that returns the string to be used as the format template.
Template String
moment.duration(123, "minutes").format("h:mm");
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:
years: Y or y
months: M
weeks: W or w
days: D or d
hours: H or h
minutes: m
seconds: s
ms: S
Escape token characters within the template string using square brackets.
moment.duration(123, "minutes").format("h [hrs], m [min]");
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.
moment.duration(3661, "seconds").format("h:mm:ss");
moment.duration(15, "seconds").format("sss [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.
moment.duration(123, "seconds").format("h:mm:ss");
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.
moment.duration(9, "milliseconds").format("mm:ss:SS", {
trim: false
});
moment.duration(10, "milliseconds").format("mm:ss:SS", {
trim: false
});
moment.duration(999, "milliseconds").format("mm:ss:SS", {
trim: false
});
moment.duration(1011, "milliseconds").format("mm:ss:SS", {
trim: false
});
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.
moment.duration(15, "seconds").format("ssss sss ss s");
moment.duration(15, "seconds").format("s ss sss ssss");
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).
moment.duration(100, "milliseconds").format();
moment.duration(100, "seconds").format();
moment.duration(100, "days").format();
moment.duration(100, "weeks").format();
moment.duration(100, "months").format();
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:
function customTemplate() {
return this.duration.asSeconds() >= 86400 ? "w [weeks], d [days]" : "hh:mm:ss";
}
moment.duration(65, 'seconds').format(customTemplate, {
trim: false
});
moment.duration(1347840, 'seconds').format(customTemplate, {
trim: false
});
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 decimal fraction or integer digits to display for the final value.
The default precison value is 0
.
moment.duration(123, "minutes").format("h [hrs]");
Positive precision defines the number of decimal fraction digits to display.
moment.duration(123, "minutes").format("h [hrs]", 2);
Negative precision defines the number of integer digits to truncate to zero.
moment.duration(223, "minutes").format("m [min]", -2);
Settings
settings
is an object that can override any of the default moment duration format options.
Both the template
and precision
arguments may be specified as properties of a single settings
object argument, or they may be passed separately along with an optional settings object.
moment.duration(123, "minutes").format({
template: "h [hrs]",
precision: 2
});
trim
The default trim
behaviour is "large"
.
Largest-magnitude tokens are automatically trimmed when they have no value.
moment.duration(123, "minutes").format("d[d] h:mm:ss");
Trimming also functions when the format string is oriented with token magnitude increasing from left to right.
moment.duration(123, "minutes").format("s [seconds], m [minutes], h [hours], d [days]");
To stop trimming altogether, set { trim: false }
.
moment.duration(123, "minutes").format("d[d] h:mm:ss", {
trim: false
});
trim
can be a string, a delimited list of strings, an array of strings, or a boolean. Accepted values are as follows:
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.
moment.duration(123, "minutes").format("d[d] h:mm:ss");
moment.duration(123, "minutes").format("d[d] h:mm:ss", {
trim: "large"
});
moment.duration(0, "minutes").format("d[d] h:mm:ss", {
trim: "large"
});
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.
moment.duration(123, "minutes").format("d[d] h:mm:ss", {
trim: "small"
});
moment.duration(0, "minutes").format("d[d] h:mm:ss", {
trim: "small"
});
Execute "large"
trim then "small"
trim.
moment.duration(123, "minutes").format("d[d] h[h] m[m] s[s]", {
trim: "both"
});
moment.duration(0, "minutes").format("d[d] h[h] m[m] s[s]", {
trim: "both"
});
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"
.
moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", {
trim: "mid"
});
moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", {
trim: "large mid"
});
moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", {
trim: "small mid"
});
moment.duration(1441, "minutes").format("w[w] d[d] h[h] m[m] s[s]", {
trim: "both mid"
});
moment.duration(0, "minutes").format("w[w] d[d] h[h] m[m] s[s]", {
trim: "both mid"
});
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"
.
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"
});
Trim all zero-value tokens. Shorthand for "both mid final"
.
moment.duration(0, "minutes").format("d[d] h[h] m[m] s[s]", {
trim: "all"
});
Maps to "large"
to support this plugin's version 1 API.
Maps to "large"
to support this plugin's version 1 API.
Maps to "large"
.
Maps to "large"
.
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.
moment.duration(7322, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", {
largest: 2
});
moment.duration(1216922, "seconds").format("y [years], w [weeks], d [days], h [hours], m [minutes], s [seconds]", {
largest: 2
});
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.
moment.duration(23, "minutes").format("d[d] h:mm:ss", {
stopTrim: "h"
});
moment.duration(23, "minutes").format("d[d] *h:mm:ss");
This option affects all trimming modes: "large"
, "small"
, "mid"
, and "final"
.
moment.duration(2, "hours").format("y [years], d [days], h [hours], m [minutes], s [seconds]", {
trim: "both",
stopTrim: "d m"
});
moment.duration(2, "hours").format("y [years], *d [days], h [hours], *m [minutes], s [seconds]", {
trim: "both"
});
stopTrim
is disabled when using largest
.
moment.duration(2, "hours").format("y [years], d [days], h [hours], m [minutes], s [seconds]", {
trim: "both",
stopTrim: "d m",
largest: 2
});
trunc
Default behavior rounds the final token value.
moment.duration(179, "seconds").format("m [minutes]");
moment.duration(3780, "seconds").format("h [hours]", 1);
Set trunc
to true
to truncate final token value. This was the default behavior in version 1 of this plugin.
moment.duration(179, "seconds").format("m [minutes]", {
trunc: true
});
moment.duration(3780, "seconds").format("h [hours]", 1, {
trunc: true
});
Using trunc
can affect the operation of trim
and largest
.
moment.duration(59, "seconds").format("d [days], h [hours], m [minutes]", {
trunc: true,
trim: "both"
});
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
.
moment.duration(59, "seconds").format("h [hours], m [minutes]", {
minValue: 1
});
moment.duration(59, "seconds").format("h [hours], m [minutes]", {
minValue: 1,
trim: "both"
});
moment.duration(7229, "seconds").format("h [hours], m [minutes]", {
minValue: 1,
trim: "both"
});
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.
moment.duration(-59, "seconds").format("h [hours], m [minutes]", {
minValue: 1
});
When minValue
is reached, only the least-magnitude moment token is output, regardless of trim
and largest
.
moment.duration(59, "seconds").format("h [hours], m [minutes]", {
minValue: 1,
trim: false,
largest: 2
});
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.
moment.duration(15, "days").format("w [weeks]", {
maxValue: 2
});
moment.duration(-15, "days").format("w [weeks]]", {
maxValue: 2
});
When maxValue
is reached, only the greatest-magnitude moment token is output, regardless of trim
and largest
.
moment.duration(15, "days").format("w [weeks], d [days]", {
maxValue: 2,
trim: false,
largest: 2
});
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.
moment.duration(123, "seconds").format("h:mm:ss");
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).
moment.duration(123, "seconds").format("hh:mm:ss");
Or you can use { forceLength: true }
.
moment.duration(123, "seconds").format("h:mm:ss", {
forceLength: true
});
useSignificantDigits
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.
Setting trunc
affects the operation of useSignificantDigits
.
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", {
useSignificantDigits: true,
precision: 3
});
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", {
useSignificantDigits: true,
precision: 3,
trunc: true
});
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", {
useSignificantDigits: true,
precision: 5
});
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", {
useSignificantDigits: true,
trunc: true,
precision: 5
});
moment.duration(99999, "seconds").format("d [days], h [hours], m [minutes], s [seconds]", {
useSignificantDigits: true,
precision: 6
});
Localization
Formatted numerical output is rendered using toLocaleString
.
Unit labels are automatically localized and pluralized. Unit labels are detected using the locale set in moment.js, 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).
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.
moment.duration(1234567, "seconds").format("m [minutes]", 3);
moment.duration(1234567, "seconds").format("m [minutes]", 3, {
userLocale: "de-DE"
});
Auto-Localized Unit Labels
The _
character can be used to generate auto-localized unit labels in the formatted output.
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.
moment.duration(2, "minutes").format("m _");
moment.duration(2, "minutes").format("m __");
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
.
moment.duration(3661, "seconds").format("_HMS_");
moment.duration(3661, "seconds").format("_HM_");
moment.duration(61, "seconds").format("_MS_");
These are the default "en"
locale options for duration time notation templates. Additional templates may be created in the locale object extensions (see below).
usePlural
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).
moment.duration(1, "minutes").format("m [minutes]");
moment.duration(1, "minutes").format("m [mins]");
moment.duration(2, "minutes").format("m [minute]");
moment.duration(2, "minutes").format("m [min]");
Set usePlural
to false
to disable auto-correction of pluralization.
moment.duration(1, "minutes").format("m [minutes]", {
usePlural: false
});
moment.duration(1, "minutes").format("m [mins]", {
usePlural: false
});
moment.duration(2, "minutes").format("m [minute]", {
usePlural: false
});
moment.duration(2, "minutes").format("m [min]", {
usePlural: false
});
The default pluralization function for the "en"
locale outputs a plural unit name when a value is rendered with decimal precision.
moment.duration(1, "minutes").format("m [minutes]", 2);
useLeftUnits
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
.
moment.duration(7322, "seconds").format("_ h, _ m, _ s", {
useLeftUnits: true
});
useGrouping
Formatted numerical output is rendered using toLocaleString
with the option useGrouping
enabled. Set useGrouping
to false
to disable digit grouping.
moment.duration(1234, "seconds").format("s [seconds]");
moment.duration(1234, "seconds").format("s [seconds]", {
useGrouping: false
});
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.
moment.duration(1234567, "seconds").format("m [minutes]", 3, {
userLocale: "de-DE"
});
Extending Moment's locale
object
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.
Below is the default "en"
locale extension.
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) {
if (integerValue === 1 && decimalValue === null) {
return token;
}
return token + token;
}
});
Creating a new Moment locale
extension
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.
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.
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) {
if (integerValue > 2) {
return token + token + token;
}
if (integerValue === 1) {
return token;
}
return token + token;
}
});
The function for durationPluralKey
is passed three arguments:
String. A single character representing the unit type.
years: y
months: M
weeks: w
days: d
hours: h
minutes: m
seconds: s
ms: S
Number. The integer portion of the token's value.
Number. The decimal fraction portion of the token's value.