chrono-ms 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Francisco Luis Rios Vega
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # chrono-ms
2
+
3
+ A lightweight, TypeScript-first library for parsing and formatting time durations.
4
+
5
+ [![NPM version](https://img.shields.io/npm/v/chrono-ms?color=32A9C3&labelColor=1B3C4A&label=npm&logo=npm)](https://www.npmjs.com/package/chrono-ms)
6
+ [![NPM downloads](https://img.shields.io/npm/dm/chrono-ms?color=32A9C3&labelColor=1B3C4A&label=downloads&logo=npm)](https://www.npmjs.com/package/chrono-ms)
7
+ [![NPM License](https://img.shields.io/npm/l/chrono-ms?color=32A9C3&labelColor=1B3C4A&label=license&logo=github)](https://www.npmjs.com/package/chrono-ms)
8
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/iscodex/chrono-ms/ci.yml?color=32A9C3&labelColor=1B3C4A&label=CI&logo=github)](https://github.com/iscodex/chrono-ms/actions)
9
+
10
+ ## Features
11
+
12
+ - ๐Ÿš€ **Lightweight**: Zero dependencies
13
+ - ๐Ÿ“ **TypeScript-first**: Full type safety and IntelliSense support
14
+ - ๐Ÿ”„ **Bidirectional**: Parse strings to milliseconds and format milliseconds to strings
15
+ - ๐ŸŽฏ **Precise**: Handles decimal values and negative numbers
16
+ - ๐Ÿ“š **Comprehensive**: Supports all common time units including months
17
+ - ๐Ÿงช **Well-tested**: >95% test coverage
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install chrono-ms
23
+ ```
24
+
25
+ ```bash
26
+ yarn add chrono-ms
27
+ ```
28
+
29
+ ```bash
30
+ pnpm add chrono-ms
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Basic Usage
36
+
37
+ ```typescript
38
+ import ms from 'chrono-ms';
39
+
40
+ // Parse string to milliseconds
41
+ ms('2 hours'); // 7200000
42
+ ms('1d'); // 86400000
43
+ ms('10h'); // 36000000
44
+ ms('2.5 hrs'); // 9000000
45
+ ms('1y'); // 31557600000
46
+
47
+ // Format milliseconds to string
48
+ ms(60000); // "1m"
49
+ ms(2 * 60 * 1000); // "2m"
50
+ ms(ms('10 hours') as number); // "10h"
51
+ ms(60000, { long: true }); // "1 minute"
52
+ ms(2 * 60 * 1000, { long: true }); // "2 minutes"
53
+ ```
54
+
55
+ ### Individual Functions
56
+
57
+ ```typescript
58
+ import { parse, format } from 'chrono-ms';
59
+
60
+ // Parse only
61
+ const milliseconds = parse('1.5h'); // 5400000
62
+
63
+ // Format only
64
+ const shortFormat = format(3600000); // "1h"
65
+ const longFormat = format(3600000, { long: true }); // "1 hour"
66
+ ```
67
+
68
+ ### Compound Strings with `parseMultiple`
69
+
70
+ ```typescript
71
+ import { parseMultiple } from 'chrono-ms';
72
+
73
+ parseMultiple('1h 30m'); // 5400000
74
+ parseMultiple('2d 4h 30m'); // 189000000
75
+ parseMultiple('1w 3d'); // 864000000
76
+ ```
77
+
78
+ ### Safe Parsing with `tryParse`
79
+
80
+ ```typescript
81
+ import { tryParse } from 'chrono-ms';
82
+
83
+ // Returns null instead of throwing on invalid input
84
+ tryParse('2h'); // 7200000
85
+ tryParse('invalid'); // null
86
+ tryParse(''); // null
87
+ ```
88
+
89
+ ### Verbose Format
90
+
91
+ ```typescript
92
+ import { format } from 'chrono-ms';
93
+
94
+ format(5400000, { verbose: true }); // "1h 30m"
95
+ format(5400000, { verbose: true, long: true }); // "1 hour 30 minutes"
96
+ format(3661000, { verbose: true }); // "1h 1m 1s"
97
+ format(90000, { verbose: true }); // "1m 30s"
98
+ ```
99
+
100
+ ### Advanced Usage
101
+
102
+ ```typescript
103
+ import ms, {
104
+ parse,
105
+ format,
106
+ type FormatOptions,
107
+ type ParseOptions,
108
+ } from 'chrono-ms';
109
+
110
+ // Custom parsing options
111
+ const result = parse('30m', { maxLength: 50 });
112
+
113
+ // Custom formatting options
114
+ const formatted = format(7200000, { long: true });
115
+
116
+ // Type-safe string values
117
+ type TimeString = `${number}h` | `${number}m` | `${number}s`;
118
+ const timeValue: TimeString = '2h';
119
+ const parsed = ms(timeValue); // 7200000
120
+ ```
121
+
122
+ ## Supported Units
123
+
124
+ | Unit | Long Format | Short Format |
125
+ | ------------ | ---------------------------------------------- | ------------ |
126
+ | Years | `years`, `year`, `yrs`, `yr` | `y` |
127
+ | Weeks | `weeks`, `week` | `w` |
128
+ | Months | `months`, `month` | `mo` |
129
+ | Days | `days`, `day` | `d` |
130
+ | Hours | `hours`, `hour`, `hrs`, `hr` | `h` |
131
+ | Minutes | `minutes`, `minute`, `mins`, `min` | `m` |
132
+ | Seconds | `seconds`, `second`, `secs`, `sec` | `s` |
133
+ | Milliseconds | `milliseconds`, `millisecond`, `msecs`, `msec` | `ms` |
134
+
135
+ > **Note:** `mo` is used for months to avoid ambiguity with `m` (minutes). Month is defined as exactly 30 days.
136
+
137
+ All units are case-insensitive and support both singular and plural forms.
138
+
139
+ ## API Reference
140
+
141
+ ### `ms(value, options?)`
142
+
143
+ Main function that can parse strings or format numbers.
144
+
145
+ **Parameters:**
146
+
147
+ - `value: string | number` - String to parse or number to format
148
+ - `options?: FormatOptions | ParseOptions` - Configuration options
149
+
150
+ **Returns:**
151
+
152
+ - `number` when parsing strings
153
+ - `string` when formatting numbers
154
+
155
+ ### `parse(value, options?)`
156
+
157
+ Parse a time string into milliseconds.
158
+
159
+ **Parameters:**
160
+
161
+ - `value: string` - Time string to parse
162
+ - `options?: ParseOptions` - Parsing configuration
163
+
164
+ **Returns:** `number` - Parsed time in milliseconds
165
+
166
+ **Options:**
167
+
168
+ - `maxLength?: number` - Maximum string length, must be a positive number (default: 100)
169
+
170
+ ### `tryParse(value, options?)`
171
+
172
+ Parse a time string and return `null` instead of throwing on invalid input.
173
+
174
+ **Parameters:**
175
+
176
+ - `value: string` - Time string to parse
177
+ - `options?: ParseOptions` - Parsing configuration
178
+
179
+ **Returns:** `number | null` - Parsed milliseconds, or `null` on failure
180
+
181
+ ### `parseMultiple(value, options?)`
182
+
183
+ Parse a compound time string with multiple space-separated tokens.
184
+
185
+ **Parameters:**
186
+
187
+ - `value: string` - Compound time string (e.g., `"1h 30m"`, `"2d 4h 30m"`)
188
+ - `options?: ParseOptions` - Parsing configuration applied to each token
189
+
190
+ **Returns:** `number` - Total duration in milliseconds
191
+
192
+ ### `format(ms, options?)`
193
+
194
+ Format milliseconds into a human-readable string.
195
+
196
+ **Parameters:**
197
+
198
+ - `ms: number` - Milliseconds to format
199
+ - `options?: FormatOptions` - Formatting configuration
200
+
201
+ **Returns:** `string` - Formatted time string
202
+
203
+ **Options:**
204
+
205
+ - `long?: boolean` - Use long format (default: false)
206
+ - `verbose?: boolean` - Show all non-zero components decomposed (default: false)
207
+
208
+ ## Examples
209
+
210
+ ### Basic Parsing
211
+
212
+ ```typescript
213
+ ms('1s'); // 1000
214
+ ms('1m'); // 60000
215
+ ms('1h'); // 3600000
216
+ ms('1d'); // 86400000
217
+ ms('1w'); // 604800000
218
+ ms('1y'); // 31557600000
219
+ ```
220
+
221
+ ### Decimal Values
222
+
223
+ ```typescript
224
+ ms('1.5h'); // 5400000
225
+ ms('0.5d'); // 43200000
226
+ ms('2.5 hours'); // 9000000
227
+ ```
228
+
229
+ ### Negative Values
230
+
231
+ ```typescript
232
+ ms('-1h'); // -3600000
233
+ ms('-30m'); // -1800000
234
+ ```
235
+
236
+ ### Different Formats
237
+
238
+ ```typescript
239
+ // Short format (default)
240
+ ms(60000); // "1m"
241
+ ms(3600000); // "1h"
242
+ ms(604800000); // "1w"
243
+ ms(31557600000); // "1y"
244
+
245
+ // Long format
246
+ ms(60000, { long: true }); // "1 minute"
247
+ ms(120000, { long: true }); // "2 minutes"
248
+ ms(3600000, { long: true }); // "1 hour"
249
+ ms(7200000, { long: true }); // "2 hours"
250
+
251
+ // Verbose format (decomposed)
252
+ format(5400000, { verbose: true }); // "1h 30m"
253
+ format(5400000, { verbose: true, long: true }); // "1 hour 30 minutes"
254
+ ```
255
+
256
+ ### Months
257
+
258
+ ```typescript
259
+ parse('1mo'); // 2592000000 (30 days)
260
+ parse('6 months'); // 15552000000
261
+ ```
262
+
263
+ ## Error Handling
264
+
265
+ The library throws descriptive errors for invalid inputs:
266
+
267
+ ```typescript
268
+ try {
269
+ ms('invalid');
270
+ } catch (error) {
271
+ console.log(error.message); // "Invalid time format: "invalid""
272
+ }
273
+
274
+ try {
275
+ ms('a'.repeat(101));
276
+ } catch (error) {
277
+ console.log(error.message); // "String too long. Maximum length is 100 characters"
278
+ }
279
+
280
+ try {
281
+ format(NaN);
282
+ } catch (error) {
283
+ console.log(error.message); // "Value must be a finite number"
284
+ }
285
+ ```
286
+
287
+ ## Limitations
288
+
289
+ - Maximum string length is 100 characters by default (configurable via `maxLength`)
290
+ - Uses approximate year length (365.25 days)
291
+ - Month is defined as exactly 30 days (not calendar-aware)
292
+ - `format()` does not output months; use `parse('1mo')` for input only
293
+
294
+ ## Development
295
+
296
+ ```bash
297
+ # Install dependencies
298
+ pnpm install
299
+
300
+ # Run tests
301
+ pnpm test
302
+
303
+ # Run tests in watch mode
304
+ pnpm run test:watch
305
+
306
+ # Run tests with coverage
307
+ pnpm run test:coverage
308
+
309
+ # Build the package
310
+ pnpm run build
311
+
312
+ # Lint the code
313
+ pnpm run lint
314
+
315
+ # Type check
316
+ pnpm run type-check
317
+ ```
318
+
319
+ ## Requirements
320
+
321
+ - Node.js >= 20.0.0
322
+ - Supports ES Modules
323
+
324
+ ## Contributing
325
+
326
+ 1. Fork the repository
327
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
328
+ 3. Commit your changes (`git commit -m 'feat: add some amazing feature'`)
329
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
330
+ 5. Open a Pull Request
331
+
332
+ ## License
333
+
334
+ MIT ยฉ [Francisco Luis Rios Vega](https://github.com/alckordev)
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Time constants in milliseconds
3
+ */
4
+ export declare const SECOND = 1000;
5
+ export declare const MINUTE: number;
6
+ export declare const HOUR: number;
7
+ export declare const DAY: number;
8
+ export declare const WEEK: number;
9
+ export declare const MONTH: number;
10
+ export declare const YEAR: number;
11
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,MAAM,OAAO,CAAC;AAC3B,eAAO,MAAM,MAAM,QAAc,CAAC;AAClC,eAAO,MAAM,IAAI,QAAc,CAAC;AAChC,eAAO,MAAM,GAAG,QAAY,CAAC;AAC7B,eAAO,MAAM,IAAI,QAAU,CAAC;AAC5B,eAAO,MAAM,KAAK,QAAW,CAAC;AAC9B,eAAO,MAAM,IAAI,QAAe,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { FormatOptions } from './types';
2
+ /**
3
+ * Format milliseconds to a human-readable string.
4
+ *
5
+ * @param ms - Milliseconds to format
6
+ * @param options - Formatting options
7
+ * @returns Formatted string
8
+ * @throws {Error} When the input is not a finite number
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * format(7200000); // "2h"
13
+ * format(7200000, { long: true }); // "2 hours"
14
+ * format(5400000, { verbose: true }); // "1h 30m"
15
+ * format(5400000, { verbose: true, long: true }); // "1 hour 30 minutes"
16
+ * ```
17
+ */
18
+ export declare function format(ms: number, options?: FormatOptions): string;
19
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAUtE"}
package/dist/index.cjs ADDED
@@ -0,0 +1,264 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /**
6
+ * Time constants in milliseconds
7
+ */
8
+ const SECOND = 1000;
9
+ const MINUTE = SECOND * 60;
10
+ const HOUR = MINUTE * 60;
11
+ const DAY = HOUR * 24;
12
+ const WEEK = DAY * 7;
13
+ const MONTH = DAY * 30;
14
+ const YEAR = DAY * 365.25;
15
+
16
+ /**
17
+ * Parse a time string and return milliseconds.
18
+ *
19
+ * @param value - Time string to parse (e.g., "2h", "30 minutes", "1d", "1mo")
20
+ * @param options - Parsing options
21
+ * @returns Parsed time in milliseconds
22
+ * @throws {Error} When the string format is invalid
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * parse("2h"); // 7200000
27
+ * parse("30 minutes"); // 1800000
28
+ * parse("1d"); // 86400000
29
+ * parse("1mo"); // 2592000000
30
+ * ```
31
+ */
32
+ function parse(value, options = {}) {
33
+ if (typeof value !== 'string' || value.trim().length === 0) {
34
+ throw new Error('Value must be a non-empty string');
35
+ }
36
+ const { maxLength } = options;
37
+ if (maxLength !== undefined && maxLength <= 0) {
38
+ throw new Error('maxLength must be a positive number');
39
+ }
40
+ const resolvedMaxLength = maxLength ?? 100;
41
+ if (value.length > resolvedMaxLength) {
42
+ throw new Error(`String too long. Maximum length is ${resolvedMaxLength} characters`);
43
+ }
44
+ // months?|mo must appear before minutes?|mins?|m to avoid partial match ambiguity
45
+ const match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|months?|mo|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(value.trim());
46
+ if (!match) {
47
+ throw new Error(`Invalid time format: "${value}"`);
48
+ }
49
+ const num = parseFloat(match[1]);
50
+ /* istanbul ignore next */
51
+ if (!isFinite(num)) {
52
+ throw new Error(`Invalid number: "${match[1]}"`);
53
+ }
54
+ const unit = (match[2] || 'ms').toLowerCase();
55
+ switch (unit) {
56
+ case 'years':
57
+ case 'year':
58
+ case 'yrs':
59
+ case 'yr':
60
+ case 'y':
61
+ return num * YEAR;
62
+ case 'weeks':
63
+ case 'week':
64
+ case 'w':
65
+ return num * WEEK;
66
+ case 'months':
67
+ case 'month':
68
+ case 'mo':
69
+ return num * MONTH;
70
+ case 'days':
71
+ case 'day':
72
+ case 'd':
73
+ return num * DAY;
74
+ case 'hours':
75
+ case 'hour':
76
+ case 'hrs':
77
+ case 'hr':
78
+ case 'h':
79
+ return num * HOUR;
80
+ case 'minutes':
81
+ case 'minute':
82
+ case 'mins':
83
+ case 'min':
84
+ case 'm':
85
+ return num * MINUTE;
86
+ case 'seconds':
87
+ case 'second':
88
+ case 'secs':
89
+ case 'sec':
90
+ case 's':
91
+ return num * SECOND;
92
+ case 'milliseconds':
93
+ case 'millisecond':
94
+ case 'msecs':
95
+ case 'msec':
96
+ case 'ms':
97
+ return num;
98
+ /* istanbul ignore next */
99
+ default:
100
+ throw new Error(`Unknown unit: "${unit}"`);
101
+ }
102
+ }
103
+ /**
104
+ * Parse a time string and return milliseconds, or `null` if the input is invalid.
105
+ * Useful when input is untrusted and exception handling would be verbose.
106
+ *
107
+ * @param value - Time string to parse
108
+ * @param options - Parsing options
109
+ * @returns Parsed time in milliseconds, or `null` on failure
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * tryParse("2h"); // 7200000
114
+ * tryParse("invalid"); // null
115
+ * ```
116
+ */
117
+ function tryParse(value, options) {
118
+ try {
119
+ return parse(value, options);
120
+ }
121
+ catch {
122
+ return null;
123
+ }
124
+ }
125
+ /**
126
+ * Parse a compound time string with multiple space-separated tokens.
127
+ *
128
+ * @param value - Compound time string (e.g., "1h 30m", "2d 4h 30m")
129
+ * @param options - Parsing options applied to each token
130
+ * @returns Total duration in milliseconds
131
+ * @throws {Error} When any token has an invalid format
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * parseMultiple("1h 30m"); // 5400000
136
+ * parseMultiple("2d 4h 30m"); // 189000000
137
+ * ```
138
+ */
139
+ function parseMultiple(value, options) {
140
+ if (typeof value !== 'string' || value.trim().length === 0) {
141
+ throw new Error('Value must be a non-empty string');
142
+ }
143
+ return value
144
+ .trim()
145
+ .split(/\s+/)
146
+ .reduce((acc, token) => acc + parse(token, options), 0);
147
+ }
148
+
149
+ /**
150
+ * Format milliseconds to a human-readable string.
151
+ *
152
+ * @param ms - Milliseconds to format
153
+ * @param options - Formatting options
154
+ * @returns Formatted string
155
+ * @throws {Error} When the input is not a finite number
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * format(7200000); // "2h"
160
+ * format(7200000, { long: true }); // "2 hours"
161
+ * format(5400000, { verbose: true }); // "1h 30m"
162
+ * format(5400000, { verbose: true, long: true }); // "1 hour 30 minutes"
163
+ * ```
164
+ */
165
+ function format(ms, options = {}) {
166
+ if (typeof ms !== 'number' || !isFinite(ms)) {
167
+ throw new Error('Value must be a finite number');
168
+ }
169
+ if (options.verbose) {
170
+ return formatVerbose(ms, options.long ?? false);
171
+ }
172
+ return options.long ? formatLong(ms) : formatShort(ms);
173
+ }
174
+ function formatShort(ms) {
175
+ const absMs = Math.abs(ms);
176
+ if (absMs >= YEAR)
177
+ return Math.round(ms / YEAR) + 'y';
178
+ if (absMs >= WEEK)
179
+ return Math.round(ms / WEEK) + 'w';
180
+ if (absMs >= DAY)
181
+ return Math.round(ms / DAY) + 'd';
182
+ if (absMs >= HOUR)
183
+ return Math.round(ms / HOUR) + 'h';
184
+ if (absMs >= MINUTE)
185
+ return Math.round(ms / MINUTE) + 'm';
186
+ if (absMs >= SECOND)
187
+ return Math.round(ms / SECOND) + 's';
188
+ return ms + 'ms';
189
+ }
190
+ function formatLong(ms) {
191
+ const absMs = Math.abs(ms);
192
+ if (absMs >= YEAR)
193
+ return plural(ms, absMs, YEAR, 'year');
194
+ if (absMs >= WEEK)
195
+ return plural(ms, absMs, WEEK, 'week');
196
+ if (absMs >= DAY)
197
+ return plural(ms, absMs, DAY, 'day');
198
+ if (absMs >= HOUR)
199
+ return plural(ms, absMs, HOUR, 'hour');
200
+ if (absMs >= MINUTE)
201
+ return plural(ms, absMs, MINUTE, 'minute');
202
+ if (absMs >= SECOND)
203
+ return plural(ms, absMs, SECOND, 'second');
204
+ return ms + ' ms';
205
+ }
206
+ /**
207
+ * Format milliseconds showing all non-zero components decomposed by unit.
208
+ * Handles negative values by prefixing the result with "-".
209
+ */
210
+ function formatVerbose(ms, long) {
211
+ const negative = ms < 0;
212
+ let remaining = Math.abs(ms);
213
+ const parts = [];
214
+ const units = [
215
+ [YEAR, 'y', 'year'],
216
+ [WEEK, 'w', 'week'],
217
+ [DAY, 'd', 'day'],
218
+ [HOUR, 'h', 'hour'],
219
+ [MINUTE, 'm', 'minute'],
220
+ [SECOND, 's', 'second'],
221
+ ];
222
+ for (const [unit, short, name] of units) {
223
+ if (remaining >= unit) {
224
+ const count = Math.floor(remaining / unit);
225
+ remaining -= count * unit;
226
+ parts.push(long
227
+ ? `${count} ${count === 1 ? name : name + 's'}`
228
+ : `${count}${short}`);
229
+ }
230
+ }
231
+ if (remaining > 0 || parts.length === 0) {
232
+ parts.push(long ? `${remaining} ms` : `${remaining}ms`);
233
+ }
234
+ const result = parts.join(' ');
235
+ return negative ? `-${result}` : result;
236
+ }
237
+ function plural(ms, absMs, unit, name) {
238
+ const isPlural = absMs >= unit * 1.5;
239
+ return Math.round(ms / unit) + ' ' + name + (isPlural ? 's' : '');
240
+ }
241
+
242
+ function ms(value, options) {
243
+ if (typeof value === 'string') {
244
+ return parse(value, options);
245
+ }
246
+ if (typeof value === 'number') {
247
+ return format(value, options);
248
+ }
249
+ throw new Error('Value must be a string or number');
250
+ }
251
+
252
+ exports.DAY = DAY;
253
+ exports.HOUR = HOUR;
254
+ exports.MINUTE = MINUTE;
255
+ exports.MONTH = MONTH;
256
+ exports.SECOND = SECOND;
257
+ exports.WEEK = WEEK;
258
+ exports.YEAR = YEAR;
259
+ exports.default = ms;
260
+ exports.format = format;
261
+ exports.parse = parse;
262
+ exports.parseMultiple = parseMultiple;
263
+ exports.tryParse = tryParse;
264
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/constants.ts","../src/parse.ts","../src/format.ts","../src/ms.ts"],"sourcesContent":[null,null,null,null],"names":[],"mappings":";;;;AAAA;;AAEG;AACI,MAAM,MAAM,GAAG;AACf,MAAM,MAAM,GAAG,MAAM,GAAG;AACxB,MAAM,IAAI,GAAG,MAAM,GAAG;AACtB,MAAM,GAAG,GAAG,IAAI,GAAG;AACnB,MAAM,IAAI,GAAG,GAAG,GAAG;AACnB,MAAM,KAAK,GAAG,GAAG,GAAG;AACpB,MAAM,IAAI,GAAG,GAAG,GAAG;;ACN1B;;;;;;;;;;;;;;;AAeG;SACa,KAAK,CAAC,KAAa,EAAE,UAAwB,EAAE,EAAA;AAC7D,IAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1D,QAAA,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC;IACrD;AAEA,IAAA,MAAM,EAAE,SAAS,EAAE,GAAG,OAAO;IAC7B,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,IAAI,CAAC,EAAE;AAC7C,QAAA,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC;IACxD;AAEA,IAAA,MAAM,iBAAiB,GAAG,SAAS,IAAI,GAAG;AAC1C,IAAA,IAAI,KAAK,CAAC,MAAM,GAAG,iBAAiB,EAAE;AACpC,QAAA,MAAM,IAAI,KAAK,CACb,sCAAsC,iBAAiB,CAAA,WAAA,CAAa,CACrE;IACH;;IAGA,MAAM,KAAK,GACT,6IAA6I,CAAC,IAAI,CAChJ,KAAK,CAAC,IAAI,EAAE,CACb;IAEH,IAAI,CAAC,KAAK,EAAE;AACV,QAAA,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,CAAA,CAAA,CAAG,CAAC;IACpD;IAEA,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;;AAEhC,IAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QAClB,MAAM,IAAI,KAAK,CAAC,CAAA,iBAAA,EAAoB,KAAK,CAAC,CAAC,CAAC,CAAA,CAAA,CAAG,CAAC;IAClD;AAEA,IAAA,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,WAAW,EAAE;IAE7C,QAAQ,IAAI;AACV,QAAA,KAAK,OAAO;AACZ,QAAA,KAAK,MAAM;AACX,QAAA,KAAK,KAAK;AACV,QAAA,KAAK,IAAI;AACT,QAAA,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,IAAI;AACnB,QAAA,KAAK,OAAO;AACZ,QAAA,KAAK,MAAM;AACX,QAAA,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,IAAI;AACnB,QAAA,KAAK,QAAQ;AACb,QAAA,KAAK,OAAO;AACZ,QAAA,KAAK,IAAI;YACP,OAAO,GAAG,GAAG,KAAK;AACpB,QAAA,KAAK,MAAM;AACX,QAAA,KAAK,KAAK;AACV,QAAA,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,GAAG;AAClB,QAAA,KAAK,OAAO;AACZ,QAAA,KAAK,MAAM;AACX,QAAA,KAAK,KAAK;AACV,QAAA,KAAK,IAAI;AACT,QAAA,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,IAAI;AACnB,QAAA,KAAK,SAAS;AACd,QAAA,KAAK,QAAQ;AACb,QAAA,KAAK,MAAM;AACX,QAAA,KAAK,KAAK;AACV,QAAA,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,MAAM;AACrB,QAAA,KAAK,SAAS;AACd,QAAA,KAAK,QAAQ;AACb,QAAA,KAAK,MAAM;AACX,QAAA,KAAK,KAAK;AACV,QAAA,KAAK,GAAG;YACN,OAAO,GAAG,GAAG,MAAM;AACrB,QAAA,KAAK,cAAc;AACnB,QAAA,KAAK,aAAa;AAClB,QAAA,KAAK,OAAO;AACZ,QAAA,KAAK,MAAM;AACX,QAAA,KAAK,IAAI;AACP,YAAA,OAAO,GAAG;;AAEZ,QAAA;AACE,YAAA,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,CAAA,CAAA,CAAG,CAAC;;AAEhD;AAEA;;;;;;;;;;;;;AAaG;AACG,SAAU,QAAQ,CAAC,KAAa,EAAE,OAAsB,EAAA;AAC5D,IAAA,IAAI;AACF,QAAA,OAAO,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC;IAC9B;AAAE,IAAA,MAAM;AACN,QAAA,OAAO,IAAI;IACb;AACF;AAEA;;;;;;;;;;;;;AAaG;AACG,SAAU,aAAa,CAAC,KAAa,EAAE,OAAsB,EAAA;AACjE,IAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE;AAC1D,QAAA,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC;IACrD;AAEA,IAAA,OAAO;AACJ,SAAA,IAAI;SACJ,KAAK,CAAC,KAAK;AACX,SAAA,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAC3D;;ACjJA;;;;;;;;;;;;;;;AAeG;SACa,MAAM,CAAC,EAAU,EAAE,UAAyB,EAAE,EAAA;IAC5D,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE;AAC3C,QAAA,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC;IAClD;AAEA,IAAA,IAAI,OAAO,CAAC,OAAO,EAAE;QACnB,OAAO,aAAa,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,IAAI,KAAK,CAAC;IACjD;AAEA,IAAA,OAAO,OAAO,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC;AACxD;AAEA,SAAS,WAAW,CAAC,EAAU,EAAA;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAE1B,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG;IACrD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG;IACrD,IAAI,KAAK,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG;IACnD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG;IACrD,IAAI,KAAK,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,GAAG,GAAG;IACzD,IAAI,KAAK,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,GAAG,GAAG;IACzD,OAAO,EAAE,GAAG,IAAI;AAClB;AAEA,SAAS,UAAU,CAAC,EAAU,EAAA;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAE1B,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC;IACzD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC;IACzD,IAAI,KAAK,IAAI,GAAG;QAAE,OAAO,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC;IACtD,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC;IACzD,IAAI,KAAK,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC;IAC/D,IAAI,KAAK,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC;IAC/D,OAAO,EAAE,GAAG,KAAK;AACnB;AAEA;;;AAGG;AACH,SAAS,aAAa,CAAC,EAAU,EAAE,IAAa,EAAA;AAC9C,IAAA,MAAM,QAAQ,GAAG,EAAE,GAAG,CAAC;IACvB,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAa,EAAE;AAE1B,IAAA,MAAM,KAAK,GAAoC;AAC7C,QAAA,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC;AACnB,QAAA,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC;AACnB,QAAA,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC;AACjB,QAAA,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC;AACnB,QAAA,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC;AACvB,QAAA,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC;KACxB;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE;AACvC,QAAA,IAAI,SAAS,IAAI,IAAI,EAAE;YACrB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;AAC1C,YAAA,SAAS,IAAI,KAAK,GAAG,IAAI;YACzB,KAAK,CAAC,IAAI,CACR;AACE,kBAAE,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,KAAK,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,CAAA;AAC7C,kBAAE,CAAA,EAAG,KAAK,GAAG,KAAK,CAAA,CAAE,CACvB;QACH;IACF;IAEA,IAAI,SAAS,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AACvC,QAAA,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAA,EAAG,SAAS,CAAA,GAAA,CAAK,GAAG,GAAG,SAAS,CAAA,EAAA,CAAI,CAAC;IACzD;IAEA,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IAC9B,OAAO,QAAQ,GAAG,CAAA,CAAA,EAAI,MAAM,CAAA,CAAE,GAAG,MAAM;AACzC;AAEA,SAAS,MAAM,CAAC,EAAU,EAAE,KAAa,EAAE,IAAY,EAAE,IAAY,EAAA;AACnE,IAAA,MAAM,QAAQ,GAAG,KAAK,IAAI,IAAI,GAAG,GAAG;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,IAAI,QAAQ,GAAG,GAAG,GAAG,EAAE,CAAC;AACnE;;AC5EA,SAAS,EAAE,CACT,KAA2B,EAC3B,OAAsC,EAAA;AAEtC,IAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7B,QAAA,OAAO,KAAK,CAAC,KAAK,EAAE,OAAuB,CAAC;IAC9C;AAEA,IAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7B,QAAA,OAAO,MAAM,CAAC,KAAK,EAAE,OAAwB,CAAC;IAChD;AAEA,IAAA,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC;AACrD;;;;;;;;;;;;;;;"}
@@ -0,0 +1,6 @@
1
+ export * from './constants';
2
+ export * from './types';
3
+ export * from './parse';
4
+ export * from './format';
5
+ export { default } from './ms';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC"}
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Time constants in milliseconds
3
+ */
4
+ const SECOND = 1000;
5
+ const MINUTE = SECOND * 60;
6
+ const HOUR = MINUTE * 60;
7
+ const DAY = HOUR * 24;
8
+ const WEEK = DAY * 7;
9
+ const MONTH = DAY * 30;
10
+ const YEAR = DAY * 365.25;
11
+
12
+ /**
13
+ * Parse a time string and return milliseconds.
14
+ *
15
+ * @param value - Time string to parse (e.g., "2h", "30 minutes", "1d", "1mo")
16
+ * @param options - Parsing options
17
+ * @returns Parsed time in milliseconds
18
+ * @throws {Error} When the string format is invalid
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * parse("2h"); // 7200000
23
+ * parse("30 minutes"); // 1800000
24
+ * parse("1d"); // 86400000
25
+ * parse("1mo"); // 2592000000
26
+ * ```
27
+ */
28
+ function parse(value, options = {}) {
29
+ if (typeof value !== 'string' || value.trim().length === 0) {
30
+ throw new Error('Value must be a non-empty string');
31
+ }
32
+ const { maxLength } = options;
33
+ if (maxLength !== undefined && maxLength <= 0) {
34
+ throw new Error('maxLength must be a positive number');
35
+ }
36
+ const resolvedMaxLength = maxLength ?? 100;
37
+ if (value.length > resolvedMaxLength) {
38
+ throw new Error(`String too long. Maximum length is ${resolvedMaxLength} characters`);
39
+ }
40
+ // months?|mo must appear before minutes?|mins?|m to avoid partial match ambiguity
41
+ const match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|months?|mo|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(value.trim());
42
+ if (!match) {
43
+ throw new Error(`Invalid time format: "${value}"`);
44
+ }
45
+ const num = parseFloat(match[1]);
46
+ /* istanbul ignore next */
47
+ if (!isFinite(num)) {
48
+ throw new Error(`Invalid number: "${match[1]}"`);
49
+ }
50
+ const unit = (match[2] || 'ms').toLowerCase();
51
+ switch (unit) {
52
+ case 'years':
53
+ case 'year':
54
+ case 'yrs':
55
+ case 'yr':
56
+ case 'y':
57
+ return num * YEAR;
58
+ case 'weeks':
59
+ case 'week':
60
+ case 'w':
61
+ return num * WEEK;
62
+ case 'months':
63
+ case 'month':
64
+ case 'mo':
65
+ return num * MONTH;
66
+ case 'days':
67
+ case 'day':
68
+ case 'd':
69
+ return num * DAY;
70
+ case 'hours':
71
+ case 'hour':
72
+ case 'hrs':
73
+ case 'hr':
74
+ case 'h':
75
+ return num * HOUR;
76
+ case 'minutes':
77
+ case 'minute':
78
+ case 'mins':
79
+ case 'min':
80
+ case 'm':
81
+ return num * MINUTE;
82
+ case 'seconds':
83
+ case 'second':
84
+ case 'secs':
85
+ case 'sec':
86
+ case 's':
87
+ return num * SECOND;
88
+ case 'milliseconds':
89
+ case 'millisecond':
90
+ case 'msecs':
91
+ case 'msec':
92
+ case 'ms':
93
+ return num;
94
+ /* istanbul ignore next */
95
+ default:
96
+ throw new Error(`Unknown unit: "${unit}"`);
97
+ }
98
+ }
99
+ /**
100
+ * Parse a time string and return milliseconds, or `null` if the input is invalid.
101
+ * Useful when input is untrusted and exception handling would be verbose.
102
+ *
103
+ * @param value - Time string to parse
104
+ * @param options - Parsing options
105
+ * @returns Parsed time in milliseconds, or `null` on failure
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * tryParse("2h"); // 7200000
110
+ * tryParse("invalid"); // null
111
+ * ```
112
+ */
113
+ function tryParse(value, options) {
114
+ try {
115
+ return parse(value, options);
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ /**
122
+ * Parse a compound time string with multiple space-separated tokens.
123
+ *
124
+ * @param value - Compound time string (e.g., "1h 30m", "2d 4h 30m")
125
+ * @param options - Parsing options applied to each token
126
+ * @returns Total duration in milliseconds
127
+ * @throws {Error} When any token has an invalid format
128
+ *
129
+ * @example
130
+ * ```ts
131
+ * parseMultiple("1h 30m"); // 5400000
132
+ * parseMultiple("2d 4h 30m"); // 189000000
133
+ * ```
134
+ */
135
+ function parseMultiple(value, options) {
136
+ if (typeof value !== 'string' || value.trim().length === 0) {
137
+ throw new Error('Value must be a non-empty string');
138
+ }
139
+ return value
140
+ .trim()
141
+ .split(/\s+/)
142
+ .reduce((acc, token) => acc + parse(token, options), 0);
143
+ }
144
+
145
+ /**
146
+ * Format milliseconds to a human-readable string.
147
+ *
148
+ * @param ms - Milliseconds to format
149
+ * @param options - Formatting options
150
+ * @returns Formatted string
151
+ * @throws {Error} When the input is not a finite number
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * format(7200000); // "2h"
156
+ * format(7200000, { long: true }); // "2 hours"
157
+ * format(5400000, { verbose: true }); // "1h 30m"
158
+ * format(5400000, { verbose: true, long: true }); // "1 hour 30 minutes"
159
+ * ```
160
+ */
161
+ function format(ms, options = {}) {
162
+ if (typeof ms !== 'number' || !isFinite(ms)) {
163
+ throw new Error('Value must be a finite number');
164
+ }
165
+ if (options.verbose) {
166
+ return formatVerbose(ms, options.long ?? false);
167
+ }
168
+ return options.long ? formatLong(ms) : formatShort(ms);
169
+ }
170
+ function formatShort(ms) {
171
+ const absMs = Math.abs(ms);
172
+ if (absMs >= YEAR)
173
+ return Math.round(ms / YEAR) + 'y';
174
+ if (absMs >= WEEK)
175
+ return Math.round(ms / WEEK) + 'w';
176
+ if (absMs >= DAY)
177
+ return Math.round(ms / DAY) + 'd';
178
+ if (absMs >= HOUR)
179
+ return Math.round(ms / HOUR) + 'h';
180
+ if (absMs >= MINUTE)
181
+ return Math.round(ms / MINUTE) + 'm';
182
+ if (absMs >= SECOND)
183
+ return Math.round(ms / SECOND) + 's';
184
+ return ms + 'ms';
185
+ }
186
+ function formatLong(ms) {
187
+ const absMs = Math.abs(ms);
188
+ if (absMs >= YEAR)
189
+ return plural(ms, absMs, YEAR, 'year');
190
+ if (absMs >= WEEK)
191
+ return plural(ms, absMs, WEEK, 'week');
192
+ if (absMs >= DAY)
193
+ return plural(ms, absMs, DAY, 'day');
194
+ if (absMs >= HOUR)
195
+ return plural(ms, absMs, HOUR, 'hour');
196
+ if (absMs >= MINUTE)
197
+ return plural(ms, absMs, MINUTE, 'minute');
198
+ if (absMs >= SECOND)
199
+ return plural(ms, absMs, SECOND, 'second');
200
+ return ms + ' ms';
201
+ }
202
+ /**
203
+ * Format milliseconds showing all non-zero components decomposed by unit.
204
+ * Handles negative values by prefixing the result with "-".
205
+ */
206
+ function formatVerbose(ms, long) {
207
+ const negative = ms < 0;
208
+ let remaining = Math.abs(ms);
209
+ const parts = [];
210
+ const units = [
211
+ [YEAR, 'y', 'year'],
212
+ [WEEK, 'w', 'week'],
213
+ [DAY, 'd', 'day'],
214
+ [HOUR, 'h', 'hour'],
215
+ [MINUTE, 'm', 'minute'],
216
+ [SECOND, 's', 'second'],
217
+ ];
218
+ for (const [unit, short, name] of units) {
219
+ if (remaining >= unit) {
220
+ const count = Math.floor(remaining / unit);
221
+ remaining -= count * unit;
222
+ parts.push(long
223
+ ? `${count} ${count === 1 ? name : name + 's'}`
224
+ : `${count}${short}`);
225
+ }
226
+ }
227
+ if (remaining > 0 || parts.length === 0) {
228
+ parts.push(long ? `${remaining} ms` : `${remaining}ms`);
229
+ }
230
+ const result = parts.join(' ');
231
+ return negative ? `-${result}` : result;
232
+ }
233
+ function plural(ms, absMs, unit, name) {
234
+ const isPlural = absMs >= unit * 1.5;
235
+ return Math.round(ms / unit) + ' ' + name + (isPlural ? 's' : '');
236
+ }
237
+
238
+ function ms(value, options) {
239
+ if (typeof value === 'string') {
240
+ return parse(value, options);
241
+ }
242
+ if (typeof value === 'number') {
243
+ return format(value, options);
244
+ }
245
+ throw new Error('Value must be a string or number');
246
+ }
247
+
248
+ export { DAY, HOUR, MINUTE, MONTH, SECOND, WEEK, YEAR, ms as default, format, parse, parseMultiple, tryParse };
249
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/constants.ts","../src/parse.ts","../src/format.ts","../src/ms.ts"],"sourcesContent":[null,null,null,null],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ /**
6
+ * Time constants in milliseconds
7
+ */
8
+ const SECOND = 1000;
9
+ const MINUTE = SECOND * 60;
10
+ const HOUR = MINUTE * 60;
11
+ const DAY = HOUR * 24;
12
+ const WEEK = DAY * 7;
13
+ const YEAR = DAY * 365.25;
14
+ /**
15
+ * Parse a time string and return milliseconds
16
+ *
17
+ * @param value - Time string to parse (e.g., "2h", "30 minutes", "1d")
18
+ * @param options - Parsing options
19
+ * @returns Parsed time in milliseconds
20
+ * @throws {Error} When the string format is invalid
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * parse("2h"); // 7200000
25
+ * parse("30 minutes"); // 1800000
26
+ * parse("1d"); // 86400000
27
+ * ```
28
+ */
29
+ function parse(value, options = {}) {
30
+ if (typeof value !== 'string' || value.length === 0) {
31
+ throw new Error('Value must be a non-empty string');
32
+ }
33
+ const maxLength = options.maxLength ?? 100;
34
+ if (value.length > maxLength) {
35
+ throw new Error(`String too long. Maximum length is ${maxLength} characters`);
36
+ }
37
+ const match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(value.trim());
38
+ if (!match) {
39
+ throw new Error(`Invalid time format: "${value}"`);
40
+ }
41
+ const num = parseFloat(match[1]);
42
+ if (!isFinite(num)) {
43
+ throw new Error(`Invalid number: "${match[1]}"`);
44
+ }
45
+ const unit = (match[2] || 'ms').toLowerCase();
46
+ switch (unit) {
47
+ case 'years':
48
+ case 'year':
49
+ case 'yrs':
50
+ case 'yr':
51
+ case 'y':
52
+ return num * YEAR;
53
+ case 'weeks':
54
+ case 'week':
55
+ case 'w':
56
+ return num * WEEK;
57
+ case 'days':
58
+ case 'day':
59
+ case 'd':
60
+ return num * DAY;
61
+ case 'hours':
62
+ case 'hour':
63
+ case 'hrs':
64
+ case 'hr':
65
+ case 'h':
66
+ return num * HOUR;
67
+ case 'minutes':
68
+ case 'minute':
69
+ case 'mins':
70
+ case 'min':
71
+ case 'm':
72
+ return num * MINUTE;
73
+ case 'seconds':
74
+ case 'second':
75
+ case 'secs':
76
+ case 'sec':
77
+ case 's':
78
+ return num * SECOND;
79
+ case 'milliseconds':
80
+ case 'millisecond':
81
+ case 'msecs':
82
+ case 'msec':
83
+ case 'ms':
84
+ return num;
85
+ default:
86
+ throw new Error(`Unknown unit: "${unit}"`);
87
+ }
88
+ }
89
+ /**
90
+ * Format milliseconds to a human-readable string
91
+ *
92
+ * @param ms - Milliseconds to format
93
+ * @param options - Formatting options
94
+ * @returns Formatted string
95
+ * @throws {Error} When the input is not a finite number
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * format(7200000); // "2h"
100
+ * format(7200000, { long: true }); // "2 hours"
101
+ * format(1800000); // "30m"
102
+ * ```
103
+ */
104
+ function format(ms, options = {}) {
105
+ if (typeof ms !== 'number' || !isFinite(ms)) {
106
+ throw new Error('Value must be a finite number');
107
+ }
108
+ return options.long ? formatLong(ms) : formatShort(ms);
109
+ }
110
+ /**
111
+ * Format milliseconds in short format (e.g., "2h", "30m")
112
+ */
113
+ function formatShort(ms) {
114
+ const absMs = Math.abs(ms);
115
+ if (absMs >= DAY) {
116
+ return Math.round(ms / DAY) + 'd';
117
+ }
118
+ if (absMs >= HOUR) {
119
+ return Math.round(ms / HOUR) + 'h';
120
+ }
121
+ if (absMs >= MINUTE) {
122
+ return Math.round(ms / MINUTE) + 'm';
123
+ }
124
+ if (absMs >= SECOND) {
125
+ return Math.round(ms / SECOND) + 's';
126
+ }
127
+ return ms + 'ms';
128
+ }
129
+ /**
130
+ * Format milliseconds in long format (e.g., "2 hours", "30 minutes")
131
+ */
132
+ function formatLong(ms) {
133
+ const absMs = Math.abs(ms);
134
+ if (absMs >= DAY) {
135
+ return plural(ms, absMs, DAY, 'day');
136
+ }
137
+ if (absMs >= HOUR) {
138
+ return plural(ms, absMs, HOUR, 'hour');
139
+ }
140
+ if (absMs >= MINUTE) {
141
+ return plural(ms, absMs, MINUTE, 'minute');
142
+ }
143
+ if (absMs >= SECOND) {
144
+ return plural(ms, absMs, SECOND, 'second');
145
+ }
146
+ return ms + ' ms';
147
+ }
148
+ /**
149
+ * Helper function for pluralization
150
+ */
151
+ function plural(ms, absMs, unit, name) {
152
+ const isPlural = absMs >= unit * 1.5;
153
+ return Math.round(ms / unit) + ' ' + name + (isPlural ? 's' : '');
154
+ }
155
+ function ms(value, options) {
156
+ if (typeof value === 'string') {
157
+ return parse(value, options);
158
+ }
159
+ if (typeof value === 'number') {
160
+ return format(value, options);
161
+ }
162
+ throw new Error('Value must be a string or number');
163
+ }
164
+
165
+ exports.default = ms;
166
+ exports.format = format;
167
+ exports.parse = parse;
168
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
package/dist/ms.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type { StringValue, FormatOptions } from './types';
2
+ /**
3
+ * Main function that can either parse a string or format a number.
4
+ *
5
+ * @param value - String to parse or number to format
6
+ * @param options - Options for parsing or formatting
7
+ * @returns Parsed milliseconds or formatted string
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * ms("2h"); // 7200000
12
+ * ms(7200000); // "2h"
13
+ * ms(7200000, { long: true }); // "2 hours"
14
+ * ```
15
+ */
16
+ declare function ms(value: StringValue): number;
17
+ declare function ms(value: number, options?: FormatOptions): string;
18
+ export default ms;
19
+ //# sourceMappingURL=ms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ms.d.ts","sourceRoot":"","sources":["../src/ms.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAgB,MAAM,SAAS,CAAC;AAExE;;;;;;;;;;;;;GAaG;AACH,iBAAS,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC;AACxC,iBAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;AAgB5D,eAAe,EAAE,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { ParseOptions } from './types';
2
+ /**
3
+ * Parse a time string and return milliseconds.
4
+ *
5
+ * @param value - Time string to parse (e.g., "2h", "30 minutes", "1d", "1mo")
6
+ * @param options - Parsing options
7
+ * @returns Parsed time in milliseconds
8
+ * @throws {Error} When the string format is invalid
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * parse("2h"); // 7200000
13
+ * parse("30 minutes"); // 1800000
14
+ * parse("1d"); // 86400000
15
+ * parse("1mo"); // 2592000000
16
+ * ```
17
+ */
18
+ export declare function parse(value: string, options?: ParseOptions): number;
19
+ /**
20
+ * Parse a time string and return milliseconds, or `null` if the input is invalid.
21
+ * Useful when input is untrusted and exception handling would be verbose.
22
+ *
23
+ * @param value - Time string to parse
24
+ * @param options - Parsing options
25
+ * @returns Parsed time in milliseconds, or `null` on failure
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * tryParse("2h"); // 7200000
30
+ * tryParse("invalid"); // null
31
+ * ```
32
+ */
33
+ export declare function tryParse(value: string, options?: ParseOptions): number | null;
34
+ /**
35
+ * Parse a compound time string with multiple space-separated tokens.
36
+ *
37
+ * @param value - Compound time string (e.g., "1h 30m", "2d 4h 30m")
38
+ * @param options - Parsing options applied to each token
39
+ * @returns Total duration in milliseconds
40
+ * @throws {Error} When any token has an invalid format
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * parseMultiple("1h 30m"); // 5400000
45
+ * parseMultiple("2d 4h 30m"); // 189000000
46
+ * ```
47
+ */
48
+ export declare function parseMultiple(value: string, options?: ParseOptions): number;
49
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAkFvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAM7E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,CAS3E"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Time units supported by the parser
3
+ */
4
+ export type Unit = 'Years' | 'Year' | 'Yrs' | 'Yr' | 'Y' | 'Weeks' | 'Week' | 'W' | 'Months' | 'Month' | 'Mo' | 'Days' | 'Day' | 'D' | 'Hours' | 'Hour' | 'Hrs' | 'Hr' | 'H' | 'Minutes' | 'Minute' | 'Mins' | 'Min' | 'M' | 'Seconds' | 'Second' | 'Secs' | 'Sec' | 's' | 'Milliseconds' | 'Millisecond' | 'Msecs' | 'Msec' | 'Ms';
5
+ export type UnitAnyCase = Unit | Uppercase<Unit> | Lowercase<Unit>;
6
+ export type StringValue = `${number}` | `${number}${UnitAnyCase}` | `${number} ${UnitAnyCase}`;
7
+ /**
8
+ * Configuration options for formatting
9
+ */
10
+ export interface FormatOptions {
11
+ /**
12
+ * Use long format (e.g., "1 day" instead of "1d")
13
+ * @default false
14
+ */
15
+ long?: boolean;
16
+ /**
17
+ * Use verbose format, showing all non-zero components (e.g., "1h 30m" instead of "2h")
18
+ * @default false
19
+ */
20
+ verbose?: boolean;
21
+ }
22
+ /**
23
+ * Configuration options for parsing
24
+ */
25
+ export interface ParseOptions {
26
+ /**
27
+ * Maximum string length to parse. Must be a positive number.
28
+ * @default 100
29
+ */
30
+ maxLength?: number;
31
+ }
32
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,IAAI,GACZ,OAAO,GACP,MAAM,GACN,KAAK,GACL,IAAI,GACJ,GAAG,GACH,OAAO,GACP,MAAM,GACN,GAAG,GACH,QAAQ,GACR,OAAO,GACP,IAAI,GACJ,MAAM,GACN,KAAK,GACL,GAAG,GACH,OAAO,GACP,MAAM,GACN,KAAK,GACL,IAAI,GACJ,GAAG,GACH,SAAS,GACT,QAAQ,GACR,MAAM,GACN,KAAK,GACL,GAAG,GACH,SAAS,GACT,QAAQ,GACR,MAAM,GACN,KAAK,GACL,GAAG,GACH,cAAc,GACd,aAAa,GACb,OAAO,GACP,MAAM,GACN,IAAI,CAAC;AAET,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;AAEnE,MAAM,MAAM,WAAW,GACnB,GAAG,MAAM,EAAE,GACX,GAAG,MAAM,GAAG,WAAW,EAAE,GACzB,GAAG,MAAM,IAAI,WAAW,EAAE,CAAC;AAE/B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "chrono-ms",
3
+ "version": "1.2.0",
4
+ "type": "module",
5
+ "description": "A lightweight, TypeScript-first library for parsing and formatting time durations",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.esm.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE.md"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "import": "./dist/index.esm.js",
17
+ "require": "./dist/index.cjs",
18
+ "types": "./dist/index.d.ts"
19
+ }
20
+ },
21
+ "keywords": [
22
+ "time",
23
+ "duration",
24
+ "parser",
25
+ "formatter",
26
+ "chrono",
27
+ "ms",
28
+ "milliseconds",
29
+ "typescript"
30
+ ],
31
+ "author": "Francisco Luis Rios Vega <alckordev@gmail.com>",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github.com/iscodex/chrono-ms.git"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/iscodex/chrono-ms/issues"
39
+ },
40
+ "homepage": "https://github.com/iscodex/chrono-ms#readme",
41
+ "devDependencies": {
42
+ "@eslint/js": "^9.35.0",
43
+ "@rollup/plugin-typescript": "^12.1.4",
44
+ "@types/jest": "^30.0.0",
45
+ "@typescript-eslint/eslint-plugin": "^8.44.0",
46
+ "@typescript-eslint/parser": "^8.44.0",
47
+ "eslint": "^9.35.0",
48
+ "eslint-config-prettier": "^10.1.8",
49
+ "eslint-plugin-prettier": "^5.5.4",
50
+ "globals": "^16.4.0",
51
+ "jest": "^30.1.3",
52
+ "np": "^10.2.0",
53
+ "prettier": "^3.6.2",
54
+ "rimraf": "^6.0.1",
55
+ "rollup": "^4.50.2",
56
+ "ts-jest": "^29.4.3",
57
+ "typescript": "^5.9.2"
58
+ },
59
+ "engines": {
60
+ "node": ">=20.0.0"
61
+ },
62
+ "scripts": {
63
+ "build": "rollup -c",
64
+ "dev": "rollup -c -w",
65
+ "test": "jest --verbose",
66
+ "test:watch": "jest --watch --verbose",
67
+ "test:coverage": "jest --coverage --verbose",
68
+ "test:debug": "jest --detectOpenHandles --forceExit --verbose",
69
+ "lint": "eslint src/**/*.{js,ts}",
70
+ "lint:fix": "eslint src/**/*.{js,ts} --fix",
71
+ "type-check": "tsc --noEmit",
72
+ "clean": "rimraf dist",
73
+ "release": "np"
74
+ }
75
+ }