Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance time formatting with new hide options #72

Merged
merged 10 commits into from
Nov 15, 2024
21 changes: 21 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -75,6 +75,27 @@ export type Options = {
@default false
*/
readonly colonNotation?: boolean;

/**
Hides the year and shows the hidden year additionally as days (365 per year): `1y 3d 5h 1m 45s` → `368d 5h 1m 45s`.

@default false
*/
readonly hideYear?: boolean;

/**
Hides the year and days and shows the hidden values additionally as hours: `1y 3d 5h 1m 45s` → `8837h 1m 45s`.

@default false
*/
readonly hideYearAndDays?: boolean;

/**
Hides the seconds: `1y 3d 5h 1m 45s` → `1y 3d 5h 1m`.

@default false
*/
readonly hideSeconds?: boolean;
};

/**
126 changes: 69 additions & 57 deletions index.js
Original file line number Diff line number Diff line change
@@ -61,67 +61,79 @@ export default function prettyMilliseconds(milliseconds, options) {
const parsed = parseMilliseconds(milliseconds);
const days = BigInt(parsed.days);

add(days / 365n, 'year', 'y');
add(days % 365n, 'day', 'd');
add(Number(parsed.hours), 'hour', 'h');
if (options.hideYearAndDays) {
add((BigInt(days) * 24n) + BigInt(parsed.hours), 'hour', 'h');
} else {
if (options.hideYear) {
add(days, 'day', 'd');
} else {
add(days / 365n, 'year', 'y');
add(days % 365n, 'day', 'd');
}

add(Number(parsed.hours), 'hour', 'h');
}

add(Number(parsed.minutes), 'minute', 'm');

if (
options.separateMilliseconds
|| options.formatSubMilliseconds
|| (!options.colonNotation && milliseconds < 1000)
) {
const seconds = Number(parsed.seconds);
const milliseconds = Number(parsed.milliseconds);
const microseconds = Number(parsed.microseconds);
const nanoseconds = Number(parsed.nanoseconds);

add(seconds, 'second', 's');

if (options.formatSubMilliseconds) {
add(milliseconds, 'millisecond', 'ms');
add(microseconds, 'microsecond', 'µs');
add(nanoseconds, 'nanosecond', 'ns');
if (!options.hideSeconds) {
if (
options.separateMilliseconds
|| options.formatSubMilliseconds
|| (!options.colonNotation && milliseconds < 1000)
) {
const seconds = Number(parsed.seconds);
const milliseconds = Number(parsed.milliseconds);
const microseconds = Number(parsed.microseconds);
const nanoseconds = Number(parsed.nanoseconds);

add(seconds, 'second', 's');

if (options.formatSubMilliseconds) {
add(milliseconds, 'millisecond', 'ms');
add(microseconds, 'microsecond', 'µs');
add(nanoseconds, 'nanosecond', 'ns');
} else {
const millisecondsAndBelow
= milliseconds
+ (microseconds / 1000)
+ (nanoseconds / 1e6);

const millisecondsDecimalDigits
= typeof options.millisecondsDecimalDigits === 'number'
? options.millisecondsDecimalDigits
: 0;

const roundedMilliseconds = millisecondsAndBelow >= 1
? Math.round(millisecondsAndBelow)
: Math.ceil(millisecondsAndBelow);

const millisecondsString = millisecondsDecimalDigits
? millisecondsAndBelow.toFixed(millisecondsDecimalDigits)
: roundedMilliseconds;

add(
Number.parseFloat(millisecondsString),
'millisecond',
'ms',
millisecondsString,
);
}
} else {
const millisecondsAndBelow
= milliseconds
+ (microseconds / 1000)
+ (nanoseconds / 1e6);

const millisecondsDecimalDigits
= typeof options.millisecondsDecimalDigits === 'number'
? options.millisecondsDecimalDigits
: 0;

const roundedMilliseconds = millisecondsAndBelow >= 1
? Math.round(millisecondsAndBelow)
: Math.ceil(millisecondsAndBelow);

const millisecondsString = millisecondsDecimalDigits
? millisecondsAndBelow.toFixed(millisecondsDecimalDigits)
: roundedMilliseconds;

add(
Number.parseFloat(millisecondsString),
'millisecond',
'ms',
millisecondsString,
);
const seconds = (
(isBigInt ? Number(milliseconds % ONE_DAY_IN_MILLISECONDS) : milliseconds)
/ 1000
) % 60;
const secondsDecimalDigits
= typeof options.secondsDecimalDigits === 'number'
? options.secondsDecimalDigits
: 1;
const secondsFixed = floorDecimals(seconds, secondsDecimalDigits);
const secondsString = options.keepDecimalsOnWholeSeconds
? secondsFixed
: secondsFixed.replace(/\.0+$/, '');
add(Number.parseFloat(secondsString), 'second', 's', secondsString);
}
} else {
const seconds = (
(isBigInt ? Number(milliseconds % ONE_DAY_IN_MILLISECONDS) : milliseconds)
/ 1000
) % 60;
const secondsDecimalDigits
= typeof options.secondsDecimalDigits === 'number'
? options.secondsDecimalDigits
: 1;
const secondsFixed = floorDecimals(seconds, secondsDecimalDigits);
const secondsString = options.keepDecimalsOnWholeSeconds
? secondsFixed
: secondsFixed.replace(/\.0+$/, '');
add(Number.parseFloat(secondsString), 'second', 's', secondsString);
}

if (result.length === 0) {
21 changes: 21 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -137,6 +137,27 @@ Setting `colonNotation` to `true` overrides the following options to `false`:
- `separateMilliseconds`
- `verbose`

##### hideYear

Type: `boolean`\
Default: `false`

Hides the year and shows the hidden year additionally as days (365 per year): `1y 3d 5h 1m 45s` → `368d 5h 1m 45s`.

##### hideYearAndDays

Type: `boolean`\
Default: `false`

Hides the year and days and shows the hidden values additionally as hours: `1y 3d 5h 1m 45s` → `8837h 1m 45s`.

##### hideSeconds

Type: `boolean`\
Default: `false`

Hides the seconds: `1y 3d 5h 1m 45s` → `1y 3d 5h 1m`.

## Related

- [pretty-ms-cli](https://github.com/sindresorhus/pretty-ms-cli) - CLI for this module
61 changes: 61 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -379,6 +379,67 @@ runTests({
],
});

runTests({
title: 'have a hideYear option',
cases: [
[1000 * 60, {hideYear: true}, '1m'],
[1000 * 60, {hideYear: false}, '1m'],
[1000 * 60 * 67, {hideYear: true}, '1h 7m'],
[1000 * 60 * 67, {hideYear: false}, '1h 7m'],
[1000 * 60 * 67 * 24 * 465, {hideYear: false}, '1y 154d 6h'],
[1000 * 60 * 67 * 24 * 465, {hideYear: true}, '519d 6h'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYear: false}, '1y 154d 6h 1m 6.5s'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYear: true}, '519d 6h 1m 6.5s'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYear: true, secondsDecimalDigits: 0}, '519d 6h 1m 6s'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6000, {hideYear: true, keepDecimalsOnWholeSeconds: true}, '519d 6h 1m 6.0s'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYear: true, separateMilliseconds: true}, '519d 6h 1m 6s 500ms'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYear: true, verbose: true}, '519 days 6 hours 1 minute 6.5 seconds'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYear: true, compact: true}, '519d'],
],
});

runTests({
title: 'have a hideYearAndDays option',
cases: [
[1000 * 60, {hideYearAndDays: true}, '1m'],
[1000 * 60, {hideYearAndDays: false}, '1m'],
[1000 * 60 * 67, {hideYearAndDays: false}, '1h 7m'],
[1000 * 60 * 67, {hideYearAndDays: true}, '1h 7m'],
[1000 * 60 * 67 * 24 * 465, {hideYearAndDays: false}, '1y 154d 6h'],
[1000 * 60 * 67 * 24 * 465, {hideYearAndDays: true}, '12462h'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYearAndDays: false}, '1y 154d 6h 1m 6.5s'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYearAndDays: true}, '12462h 1m 6.5s'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYearAndDays: true, secondsDecimalDigits: 0}, '12462h 1m 6s'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6000, {hideYearAndDays: true, keepDecimalsOnWholeSeconds: true}, '12462h 1m 6.0s'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYearAndDays: true, separateMilliseconds: true}, '12462h 1m 6s 500ms'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYearAndDays: true, verbose: true}, '12462 hours 1 minute 6.5 seconds'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60) + 6500, {hideYearAndDays: true, compact: true}, '12462h'],
],
});

runTests({
title: 'have a hideSeconds option',
cases: [
[(1000 * 60) + 6500, {hideSeconds: false}, '1m 6.5s'],
[(1000 * 60) + 6500, {hideSeconds: true}, '1m'],
[(1000 * 60) + 6500, {hideSeconds: true, secondsDecimalDigits: 3}, '1m'],
[(1000 * 60) + 6500, {hideSeconds: true, keepDecimalsOnWholeSeconds: true}, '1m'],
[(1000 * 60) + 6500, {hideSeconds: true, formatSubMilliseconds: true}, '1m'],
[(1000 * 60) + 6500, {hideSeconds: true, separateMilliseconds: true}, '1m'],
[(1000 * 60) + 6500, {hideSeconds: true, verbose: true}, '1 minute'],
[(1000 * 60) + 6500, {hideSeconds: true, compact: true}, '1m'],
],
});

runTests({
title: 'have hideYearAndDays,hideSeconds and colonNotation options',
cases: [
[(1000 * 60 * 60 * 15) + (1000 * 60 * 59) + (1000 * 59) + 543, {hideSeconds: true, hideYearAndDays: true, colonNotation: true}, '15:59'],
[(1000 * 60 * 67 * 24 * 465) + (1000 * 60 * 60 * 15) + (1000 * 60 * 59) + (1000 * 59) + 543, {hideSeconds: true, hideYearAndDays: true, colonNotation: true}, '12477:59'],
[BigInt(Number.MAX_VALUE), {hideSeconds: true, hideYearAndDays: true, colonNotation: true}, '49935920412842103004035395481028987999464046534956943499699299111988127994452371877941544064657466158761238598198439573398422590802628939657907651862093754718347197382375356132290413913997035817798852363459759428417939788028673041157169044258923152298554951723373534213538382550255361078125112229495590:14'],
],
});

sindresorhus marked this conversation as resolved.
Show resolved Hide resolved
test('Big numbers', t => {
t.is(
prettyMilliseconds(Number.MAX_VALUE),