Skip to content

Commit

Permalink
feat: add parse() and format() exports
Browse files Browse the repository at this point in the history
  • Loading branch information
styfle committed Mar 18, 2024
1 parent dfd1803 commit 6caf64f
Show file tree
Hide file tree
Showing 3 changed files with 390 additions and 7 deletions.
174 changes: 174 additions & 0 deletions src/format.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { format } from './index';

// numbers

describe('format(number, { long: true })', () => {
it('should not throw an error', () => {
expect(() => {
format(500, { long: true });
}).not.toThrowError();
});

it('should support milliseconds', () => {
expect(format(500, { long: true })).toBe('500 ms');

expect(format(-500, { long: true })).toBe('-500 ms');
});

it('should support seconds', () => {
expect(format(1000, { long: true })).toBe('1 second');
expect(format(1200, { long: true })).toBe('1 second');
expect(format(10000, { long: true })).toBe('10 seconds');

expect(format(-1000, { long: true })).toBe('-1 second');
expect(format(-1200, { long: true })).toBe('-1 second');
expect(format(-10000, { long: true })).toBe('-10 seconds');
});

it('should support minutes', () => {
expect(format(60 * 1000, { long: true })).toBe('1 minute');
expect(format(60 * 1200, { long: true })).toBe('1 minute');
expect(format(60 * 10000, { long: true })).toBe('10 minutes');

expect(format(-1 * 60 * 1000, { long: true })).toBe('-1 minute');
expect(format(-1 * 60 * 1200, { long: true })).toBe('-1 minute');
expect(format(-1 * 60 * 10000, { long: true })).toBe('-10 minutes');
});

it('should support hours', () => {
expect(format(60 * 60 * 1000, { long: true })).toBe('1 hour');
expect(format(60 * 60 * 1200, { long: true })).toBe('1 hour');
expect(format(60 * 60 * 10000, { long: true })).toBe('10 hours');

expect(format(-1 * 60 * 60 * 1000, { long: true })).toBe('-1 hour');
expect(format(-1 * 60 * 60 * 1200, { long: true })).toBe('-1 hour');
expect(format(-1 * 60 * 60 * 10000, { long: true })).toBe('-10 hours');
});

it('should support days', () => {
expect(format(24 * 60 * 60 * 1000, { long: true })).toBe('1 day');
expect(format(24 * 60 * 60 * 1200, { long: true })).toBe('1 day');
expect(format(24 * 60 * 60 * 10000, { long: true })).toBe('10 days');

expect(format(-1 * 24 * 60 * 60 * 1000, { long: true })).toBe('-1 day');
expect(format(-1 * 24 * 60 * 60 * 1200, { long: true })).toBe('-1 day');
expect(format(-1 * 24 * 60 * 60 * 10000, { long: true })).toBe('-10 days');
});

it('should round', () => {
expect(format(234234234, { long: true })).toBe('3 days');

expect(format(-234234234, { long: true })).toBe('-3 days');
});
});

// numbers

describe('format(number)', () => {
it('should not throw an error', () => {
expect(() => {
format(500);
}).not.toThrowError();
});

it('should support milliseconds', () => {
expect(format(500)).toBe('500ms');

expect(format(-500)).toBe('-500ms');
});

it('should support seconds', () => {
expect(format(1000)).toBe('1s');
expect(format(10000)).toBe('10s');

expect(format(-1000)).toBe('-1s');
expect(format(-10000)).toBe('-10s');
});

it('should support minutes', () => {
expect(format(60 * 1000)).toBe('1m');
expect(format(60 * 10000)).toBe('10m');

expect(format(-1 * 60 * 1000)).toBe('-1m');
expect(format(-1 * 60 * 10000)).toBe('-10m');
});

it('should support hours', () => {
expect(format(60 * 60 * 1000)).toBe('1h');
expect(format(60 * 60 * 10000)).toBe('10h');

expect(format(-1 * 60 * 60 * 1000)).toBe('-1h');
expect(format(-1 * 60 * 60 * 10000)).toBe('-10h');
});

it('should support days', () => {
expect(format(24 * 60 * 60 * 1000)).toBe('1d');
expect(format(24 * 60 * 60 * 10000)).toBe('10d');

expect(format(-1 * 24 * 60 * 60 * 1000)).toBe('-1d');
expect(format(-1 * 24 * 60 * 60 * 10000)).toBe('-10d');
});

it('should round', () => {
expect(format(234234234)).toBe('3d');

expect(format(-234234234)).toBe('-3d');
});
});

// invalid inputs

describe('format(invalid inputs)', () => {
it('should throw an error, when format("")', () => {
expect(() => {
// @ts-expect-error - We expect this to throw.
format('');
}).toThrowError();
});

it('should throw an error, when format(undefined)', () => {
expect(() => {
// @ts-expect-error - We expect this to throw.
format(undefined);
}).toThrowError();
});

it('should throw an error, when format(null)', () => {
expect(() => {
// @ts-expect-error - We expect this to throw.
format(null);
}).toThrowError();
});

it('should throw an error, when format([])', () => {
expect(() => {
// @ts-expect-error - We expect this to throw.
format([]);
}).toThrowError();
});

it('should throw an error, when format({})', () => {
expect(() => {
// @ts-expect-error - We expect this to throw.
format({});
}).toThrowError();
});

it('should throw an error, when format(NaN)', () => {
expect(() => {
format(NaN);
}).toThrowError();
});

it('should throw an error, when format(Infinity)', () => {
expect(() => {
format(Infinity);
}).toThrowError();
});

it('should throw an error, when format(-Infinity)', () => {
expect(() => {
format(-Infinity);
}).toThrowError();
});
});
30 changes: 23 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ function msFn(value: StringValue, options?: Options): number;
function msFn(value: number, options?: Options): string;
function msFn(value: StringValue | number, options?: Options): number | string {
try {
if (typeof value === 'string' && value.length > 0) {
if (typeof value === 'string') {
return parse(value);
} else if (typeof value === 'number' && isFinite(value)) {
return options?.long ? fmtLong(value) : fmtShort(value);
} else if (typeof value === 'number') {
return format(value, options);
}
throw new Error('Value is not a string or number.');
throw new Error('Value of ms() must be a string or number.');
} catch (error) {
const message = isError(error)
? `${error.message}. value=${JSON.stringify(value)}`
Expand All @@ -85,9 +85,11 @@ function msFn(value: StringValue | number, options?: Options): number | string {
* @returns The parsed value in milliseconds, or `NaN` if the string can't be
* parsed
*/
function parse(str: string): number {
if (str.length > 100) {
throw new Error('Value exceeds the maximum length of 100 characters.');
export function parse(str: string): number {
if (typeof str !== 'string' || str.length === 0 || str.length > 100) {
throw new Error(
'Value of ms.parse() must be a string with length between 1 and 99.',
);
}
const match =
/^(?<value>-?(?:\d+)?\.?\d+) *(?<type>milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
Expand Down Expand Up @@ -191,6 +193,20 @@ function fmtLong(ms: number): StringValue {
return `${ms} ms`;
}

/**
* Format the given integer as a string.
*
* @param ms - milliseconds
* @param options - Options for the conversion
* @returns The formatted string
*/
export function format(ms: number, options?: Options): string {
if (typeof ms !== 'number' || !isFinite(ms)) {
throw new Error('Value of ms.format() must be of type number.');
}
return options?.long ? fmtLong(ms) : fmtShort(ms);
}

/**
* Pluralization helper.
*/
Expand Down

0 comments on commit 6caf64f

Please sign in to comment.