Source Module (this page)
Purpose
The src module is the core of the ms library. It provides a complete set of functions for converting between human-readable time duration strings and their millisecond equivalents. The module supports two directions of conversion: parsing strings (e.g., "2 hours", "1.5d") into numeric millisecond values, and formatting numeric millisecond values back into human-readable strings (e.g., 3600000 becomes "1h" or "1 hour"). Both short (abbreviated) and long (verbose) output formats are supported.
This module is self-contained with no external dependencies and no outbound module edges. All functionality lives in a single source file, src/index.ts, with four test files covering each public function.
How It Works
Architecture Overview
The module is built around a set of constant multipliers that define the relationship between time units and milliseconds. These constants are defined at the top of src/index.ts:
const s = 1000;
const m = s * 60;
const h = m * 60;
const d = h * 24;
const w = d * 7;
const y = d * 365.25;
const mo = y / 12;The module defines a comprehensive set of TypeScript types that represent valid time unit strings. These types are used for type-safe parsing and are exported for consumer use:
| Type | Purpose |
|---|---|
Years | Union of 'years' | 'year' | 'yrs' | 'yr' | 'y' |
Months | Union of 'months' | 'month' | 'mo' |
Weeks | Union of 'weeks' | 'week' | 'w' |
Days | Union of 'days' | 'day' | 'd' |
Hours | Union of 'hours' | 'hour' | 'hrs' | 'hr' | 'h' |
Minutes | Union of 'minutes' | 'minute' | 'mins' | 'min' | 'm' |
Seconds | Union of 'seconds' | 'second' | 'secs' | 'sec' | 's' |
Milliseconds | Union of 'milliseconds' | 'millisecond' | 'msecs' | 'msec' | 'ms' |
These are combined into the Unit type src/index.ts:17-25, and then extended to UnitAnyCase which adds capitalized and uppercase variants src/index.ts:27-27. The StringValue type src/index.ts:29-32 represents all valid string inputs:
export type StringValue =
| `${number}`
| `${number}${UnitAnyCase}`
| `${number} ${UnitAnyCase}`;Function Dispatch
The main entry point is the ms() function, which acts as a dispatcher. It inspects the type of its first argument and delegates to either parse() (for string inputs) or format() (for number inputs):
export function ms(value: StringValue | number,
options?: Options,
): number | string {
if (typeof value === 'string') {
return parse(value);
} else if (typeof value === 'number') {
return format(value, options);
}
throw new Error(`Value provided to ms() must be a string or number. value=${JSON.stringify(value)}`,
);
}The Options interface src/index.ts:34-39 accepts a single optional property:
| Property | Type | Default | Description |
|---|---|---|---|
long | boolean | false | When true, uses verbose formatting (e.g., "2 hours" instead of "2h") |
Walkthrough of Main Flows
Parsing Strings to Milliseconds
The parse() Function
The parse() function src/index.ts:71-147 accepts a string and returns a number representing milliseconds. It performs input validation first:
export function parse(str: string): number {
if (typeof str!== 'string' || str.length === 0 || str.length > 100) {
throw new Error(`Value provided to ms.parse() must be a string with length between 1 and 99. value=${JSON.stringify(str)}`,
);
}The function rejects empty strings and strings longer than 100 characters. After validation, it applies a regular expression to extract the numeric value and unit:
const match =
/^(?<value>-?\d*\.?\d+) *(?<unit>milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|months?|mo|years?|yrs?|y)?$/i.exec(str,
);The regex captures:
value: An optional negative sign, followed by digits and an optional decimal point and more digits (e.g.,-1.5,.5,100)unit: An optional unit string matching any of the supported unit aliases, case-insensitive
If the regex does not match, the function returns NaN src/index.ts:82-84.
When the unit is captured, it is normalized to lowercase and matched against all known unit aliases in a switch statement src/index.ts:92-143. Each case multiplies the parsed numeric value by the corresponding constant:
| Unit aliases | Multiplier | Example |
|---|---|---|
years, year, yrs, yr, y | y (365.25 days) | "1y" → 31557600000 |
months, month, mo | mo (y/12) | "1mo" → 2629800000 |
weeks, week, w | w (7 days) | "1w" → 604800000 |
days, day, d | d (24 hours) | "2d" → 172800000 |
hours, hour, hrs, hr, h | h (60 minutes) | "1.5h" → 5400000 |
minutes, minute, mins, min, m | m (60 seconds) | "1m" → 60000 |
seconds, second, secs, sec, s | s (1000 ms) | "1s" → 1000 |
milliseconds, millisecond, msecs, msec, ms | 1 (identity) | "100ms" → 100 |
When no unit is provided, the value is treated as milliseconds directly src/index.ts:90-90.
The function handles several edge cases:
- Negative values: The regex captures an optional leading
-, so"-100ms"returns-100 - Decimal values:
parseFloat()handles decimals, so"1.5h"returns5400000 - Numbers starting with
.: The regex pattern\d*\.?\d+matches.5, so".5ms"returns0.5 - Multiple spaces: The regex allows
*(zero or more spaces) between value and unit, so"1 s"returns1000 - Case insensitivity: The
/iflag on the regex makes matching case-insensitive, so"1.5H"works the same as"1.5h" - Invalid strings: Returns
NaNfor unparseable input like"☃","10-.5", or"ms"(no numeric value)
The parseStrict() Function
The parseStrict() function src/index.ts:156-158 is a thin wrapper around parse() that provides type safety through the StringValue type:
export function parseStrict(value: StringValue): number {
return parse(value);
}The key difference is that parseStrict() accepts only StringValue as its parameter type, which restricts inputs at compile time to strings that match the pattern of an optional number followed by an optional unit. This means TypeScript consumers get compile-time validation that their input is a valid time string format. At runtime, parseStrict() behaves identically to parse().
Formatting Milliseconds to Strings
The format() Function
The format() function src/index.ts:225-231 converts a numeric millisecond value into a human-readable string:
export function format(ms: number, options?: Options): string {
if (typeof ms!== 'number' ||!Number.isFinite(ms)) {
throw new Error('Value provided to ms.format() must be of type number.');
}
return options?.long? fmtLong(ms): fmtShort(ms);
}It validates that the input is a finite number, rejecting NaN, Infinity, and -Infinity. It then delegates to either fmtShort() or fmtLong() based on the long option.
Choosing the Best Unit
Both fmtShort() src/index.ts:163-187 and fmtLong() src/index.ts:192-216 use the same algorithm to select the most appropriate unit. They work by comparing the absolute value of the input against the time constants in descending order (largest to smallest):
function fmtShort(ms: number): StringValue {
const msAbs = Math.abs(ms);
if (msAbs >= y) {
return `${Math.round(ms / y)}y`;
}
if (msAbs >= mo) {
return `${Math.round(ms / mo)}mo`;
}
if (msAbs >= w) {
return `${Math.round(ms / w)}w`;
}
if (msAbs >= d) {
return `${Math.round(ms / d)}d`;
}
if (msAbs >= h) {
return `${Math.round(ms / h)}h`;
}
if (msAbs >= m) {
return `${Math.round(ms / m)}m`;
}
if (msAbs >= s) {
return `${Math.round(ms / s)}s`;
}
return `${ms}ms`;
}The algorithm selects the largest unit for which the absolute value is at least one full unit. For example:
234234234ms has an absolute value of ~234 million, which is >=d(86400000) but <w(604800000), so it formats as"3d"(234234234 / 86400000 = 2.71, rounded to 3)500ms is less thans(1000), so it falls through to the final return and formats as"500ms"1200ms is >=sbut <m, so it formats as"1s"(1200 / 1000 = 1.2, rounded to 1)
The fmtLong() function uses the same logic but calls the plural() helper src/index.ts:236-244:
function plural(ms: number,
msAbs: number,
n: number,
name: string,
): StringValue {
const isPlural = msAbs >= n * 1.5;
return `${Math.round(ms / n)} ${name}${isPlural? 's': ''}` as StringValue;
}The pluralization threshold is n * 1.5, meaning a value of 1.5 or more times the unit is considered plural. This means:
1000ms formats as"1 second"(1000 / 1000 = 1, 1000 < 1500, so singular)1200ms formats as"1 second"(1200 / 1000 = 1.2, rounded to 1, 1200 < 1500, so singular)10000ms formats as"10 seconds"(10000 / 1000 = 10, 10000 >= 1500, so plural)
The ms() Function as Unified Entry Point
The ms() function serves as the primary API for most consumers. It combines both parsing and formatting into a single function that behaves differently based on input type:
- String input: Delegates to
parse(), returns a number (milliseconds) - Number input: Delegates to
format(), returns a string (human-readable)
This dual behavior is demonstrated in the test file src/index.test.ts. For example, ms('1m') returns 60000, while ms(60000) returns "1m".
Public API
| Symbol | Signature | Purpose |
|---|---|---|
StringValue | type StringValue = \${number}` | `${number}${UnitAnyCase}` | `${number} ${UnitAnyCase}`` | Type representing valid time string inputs for type-safe parsing |
ms | ms(value: StringValue, options?: Options): number; ms(value: number, options?: Options): string | Main entry point; parses strings to milliseconds or formats numbers to strings |
parse | parse(str: string): number | Parses a time string to milliseconds; returns NaN for invalid input |
parseStrict | parseStrict(value: StringValue): number | Type-safe version of parse() that accepts only StringValue at compile time |
format | format(ms: number, options?: Options): string | Formats milliseconds to a human-readable string (short or long form) |
Options | interface Options { long?: boolean } | Configuration object for formatting; long: true produces verbose output |
Interactions with Other Modules
The src module has no inbound or outbound module edges. It is the sole source of functionality in the repository and does not depend on any other modules. All test files (src/format.test.ts, src/index.test.ts, src/parse-strict.test.ts, src/parse.test.ts) import directly from src/index.ts and test the public API.
Configuration and Conventions
Options
The only configuration option is the long property on the Options interface:
interface Options {
/**
* Set to `true` to use verbose formatting. Defaults to `false`.
*/
long?: boolean;
}When long is false or omitted, format() produces short abbreviations:
"500ms","1s","1m","1h","1d","1w","1mo","1y"
When long is true, format() produces verbose, pluralized output:
"500 ms","1 second","1 minute","1 hour","1 day","1 week","1 month","1 year"
Error Handling Conventions
The module follows consistent error handling patterns:
- Type validation: Functions throw
Errorwhen the input is not of the expected type (e.g., passingnull,undefined, objects, or arrays) - Finite number check:
format()andms()(for number inputs) throw when givenNaN,Infinity, or-Infinity - String length check:
parse()throws when the string is empty or longer than 100 characters - Invalid parseable strings:
parse()returnsNaNfor strings that don't match the expected pattern (e.g.,"☃","foo")
Unit Aliases
Each time unit has multiple accepted aliases for parsing, all case-insensitive:
| Unit | Short aliases | Long aliases |
|---|---|---|
| Year | y, yr, yrs | year, years |
| Month | mo | month, months |
| Week | w | week, weeks |
| Day | d | day, days |
| Hour | h, hr, hrs | hour, hours |
| Minute | m, min, mins | minute, minutes |
| Second | s, sec, secs | second, seconds |
| Millisecond | ms, msec, msecs | millisecond, milliseconds |
Time Constants
The module uses these millisecond constants:
| Constant | Value (ms) | Definition |
|---|---|---|
s | 1000 | Base unit |
m | 60000 | s * 60 |
h | 3600000 | m * 60 |
d | 86400000 | h * 24 |
w | 604800000 | d * 7 |
y | 31557600000 | d * 365.25 |
mo | 2629800000 | y / 12 |
Note that a year is defined as 365.25 days (accounting for leap years), and a month is defined as exactly one-twelfth of a year.