@prairielearn/formatter 1.0.1 → 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/CHANGELOG.md +12 -0
- package/dist/date.d.ts +16 -0
- package/dist/date.js +38 -0
- package/dist/date.js.map +1 -1
- package/dist/date.test.js +37 -1
- package/dist/date.test.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/interval.d.ts +11 -0
- package/dist/interval.js +35 -0
- package/dist/interval.js.map +1 -0
- package/dist/interval.test.d.ts +1 -0
- package/dist/interval.test.js +31 -0
- package/dist/interval.test.js.map +1 -0
- package/package.json +1 -1
- package/src/date.test.ts +39 -1
- package/src/date.ts +40 -0
- package/src/index.ts +2 -1
- package/src/interval.test.ts +35 -0
- package/src/interval.ts +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @prairielearn/formatter
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d3336c2: Add `formatInterval` function
|
|
8
|
+
|
|
9
|
+
## 1.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 03a363f: Add functions `formatDateYMD` and `formatDateYMDHM`
|
|
14
|
+
|
|
3
15
|
## 1.0.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/date.d.ts
CHANGED
|
@@ -11,3 +11,19 @@ export declare function formatDate(date: Date, timeZone: string, { includeTz, lo
|
|
|
11
11
|
includeTz?: boolean;
|
|
12
12
|
longTz?: boolean;
|
|
13
13
|
}): string;
|
|
14
|
+
/**
|
|
15
|
+
* Format a date to a human-readable string like '2020-03-27'.
|
|
16
|
+
*
|
|
17
|
+
* @param date The date to format.
|
|
18
|
+
* @param timeZone The time zone to use for formatting.
|
|
19
|
+
* @returns Human-readable string representing the date.
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatDateYMD(date: Date, timeZone: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Format a date to a human-readable string like '2020-03-27 14:27'.
|
|
24
|
+
*
|
|
25
|
+
* @param date The date to format.
|
|
26
|
+
* @param timeZone The time zone to use for formatting.
|
|
27
|
+
* @returns Human-readable string representing the date.
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatDateYMDHM(date: Date, timeZone: string): string;
|
package/dist/date.js
CHANGED
|
@@ -27,4 +27,42 @@ export function formatDate(date, timeZone, { includeTz = true, longTz = false }
|
|
|
27
27
|
}
|
|
28
28
|
return dateFormatted;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Format a date to a human-readable string like '2020-03-27'.
|
|
32
|
+
*
|
|
33
|
+
* @param date The date to format.
|
|
34
|
+
* @param timeZone The time zone to use for formatting.
|
|
35
|
+
* @returns Human-readable string representing the date.
|
|
36
|
+
*/
|
|
37
|
+
export function formatDateYMD(date, timeZone) {
|
|
38
|
+
const options = {
|
|
39
|
+
timeZone,
|
|
40
|
+
hourCycle: 'h23',
|
|
41
|
+
year: 'numeric',
|
|
42
|
+
month: '2-digit',
|
|
43
|
+
day: '2-digit',
|
|
44
|
+
};
|
|
45
|
+
const parts = keyBy(new Intl.DateTimeFormat('en-US', options).formatToParts(date), (x) => x.type);
|
|
46
|
+
return `${parts.year.value}-${parts.month.value}-${parts.day.value}`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Format a date to a human-readable string like '2020-03-27 14:27'.
|
|
50
|
+
*
|
|
51
|
+
* @param date The date to format.
|
|
52
|
+
* @param timeZone The time zone to use for formatting.
|
|
53
|
+
* @returns Human-readable string representing the date.
|
|
54
|
+
*/
|
|
55
|
+
export function formatDateYMDHM(date, timeZone) {
|
|
56
|
+
const options = {
|
|
57
|
+
timeZone,
|
|
58
|
+
hourCycle: 'h23',
|
|
59
|
+
year: 'numeric',
|
|
60
|
+
month: '2-digit',
|
|
61
|
+
day: '2-digit',
|
|
62
|
+
hour: '2-digit',
|
|
63
|
+
minute: '2-digit',
|
|
64
|
+
};
|
|
65
|
+
const parts = keyBy(new Intl.DateTimeFormat('en-US', options).formatToParts(date), (x) => x.type);
|
|
66
|
+
return `${parts.year.value}-${parts.month.value}-${parts.day.value} ${parts.hour.value}:${parts.minute.value}`;
|
|
67
|
+
}
|
|
30
68
|
//# sourceMappingURL=date.js.map
|
package/dist/date.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date.js","sourceRoot":"","sources":["../src/date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,iBAAiB,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CACxB,IAAU,EACV,QAAgB,EAChB,EAAE,SAAS,GAAG,IAAI,EAAE,MAAM,GAAG,KAAK,KAAgD,EAAE;IAEpF,MAAM,OAAO,GAA+B;QAC1C,QAAQ;QACR,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;KACxC,CAAC;IACF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClG,IAAI,aAAa,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAClJ,IAAI,SAAS,EAAE,CAAC;QACd,aAAa,GAAG,GAAG,aAAa,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC;IACnE,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC","sourcesContent":["import keyBy from 'lodash/keyBy.js';\n\n/**\n * Format a date to a human-readable string like '2020-03-27T12:34:56 (CDT)'.\n *\n * @param date The date to format.\n * @param timeZone The time zone to use for formatting.\n * @param param2.includeTz Whether to include the time zone in the output (default true).\n * @param param2.longTz Whether to use the long time zone name (default false).\n * @returns Human-readable string representing the date.\n */\nexport function formatDate(\n date: Date,\n timeZone: string,\n { includeTz = true, longTz = false }: { includeTz?: boolean; longTz?: boolean } = {},\n): string {\n const options: Intl.DateTimeFormatOptions = {\n timeZone,\n hourCycle: 'h23',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n timeZoneName: longTz ? 'long' : 'short',\n };\n const parts = keyBy(new Intl.DateTimeFormat('en-US', options).formatToParts(date), (x) => x.type);\n let dateFormatted = `${parts.year.value}-${parts.month.value}-${parts.day.value} ${parts.hour.value}:${parts.minute.value}:${parts.second.value}`;\n if (includeTz) {\n dateFormatted = `${dateFormatted} (${parts.timeZoneName.value})`;\n }\n return dateFormatted;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"date.js","sourceRoot":"","sources":["../src/date.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,iBAAiB,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CACxB,IAAU,EACV,QAAgB,EAChB,EAAE,SAAS,GAAG,IAAI,EAAE,MAAM,GAAG,KAAK,KAAgD,EAAE;IAEpF,MAAM,OAAO,GAA+B;QAC1C,QAAQ;QACR,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;KACxC,CAAC;IACF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClG,IAAI,aAAa,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAClJ,IAAI,SAAS,EAAE,CAAC;QACd,aAAa,GAAG,GAAG,aAAa,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC;IACnE,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAU,EAAE,QAAgB;IACxD,MAAM,OAAO,GAA+B;QAC1C,QAAQ;QACR,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;KACf,CAAC;IACF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClG,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;AACvE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,IAAU,EAAE,QAAgB;IAC1D,MAAM,OAAO,GAA+B;QAC1C,QAAQ;QACR,SAAS,EAAE,KAAK;QAChB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;KAClB,CAAC;IACF,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClG,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;AACjH,CAAC","sourcesContent":["import keyBy from 'lodash/keyBy.js';\n\n/**\n * Format a date to a human-readable string like '2020-03-27T12:34:56 (CDT)'.\n *\n * @param date The date to format.\n * @param timeZone The time zone to use for formatting.\n * @param param2.includeTz Whether to include the time zone in the output (default true).\n * @param param2.longTz Whether to use the long time zone name (default false).\n * @returns Human-readable string representing the date.\n */\nexport function formatDate(\n date: Date,\n timeZone: string,\n { includeTz = true, longTz = false }: { includeTz?: boolean; longTz?: boolean } = {},\n): string {\n const options: Intl.DateTimeFormatOptions = {\n timeZone,\n hourCycle: 'h23',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n timeZoneName: longTz ? 'long' : 'short',\n };\n const parts = keyBy(new Intl.DateTimeFormat('en-US', options).formatToParts(date), (x) => x.type);\n let dateFormatted = `${parts.year.value}-${parts.month.value}-${parts.day.value} ${parts.hour.value}:${parts.minute.value}:${parts.second.value}`;\n if (includeTz) {\n dateFormatted = `${dateFormatted} (${parts.timeZoneName.value})`;\n }\n return dateFormatted;\n}\n\n/**\n * Format a date to a human-readable string like '2020-03-27'.\n *\n * @param date The date to format.\n * @param timeZone The time zone to use for formatting.\n * @returns Human-readable string representing the date.\n */\nexport function formatDateYMD(date: Date, timeZone: string): string {\n const options: Intl.DateTimeFormatOptions = {\n timeZone,\n hourCycle: 'h23',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n };\n const parts = keyBy(new Intl.DateTimeFormat('en-US', options).formatToParts(date), (x) => x.type);\n return `${parts.year.value}-${parts.month.value}-${parts.day.value}`;\n}\n\n/**\n * Format a date to a human-readable string like '2020-03-27 14:27'.\n *\n * @param date The date to format.\n * @param timeZone The time zone to use for formatting.\n * @returns Human-readable string representing the date.\n */\nexport function formatDateYMDHM(date: Date, timeZone: string): string {\n const options: Intl.DateTimeFormatOptions = {\n timeZone,\n hourCycle: 'h23',\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n };\n const parts = keyBy(new Intl.DateTimeFormat('en-US', options).formatToParts(date), (x) => x.type);\n return `${parts.year.value}-${parts.month.value}-${parts.day.value} ${parts.hour.value}:${parts.minute.value}`;\n}\n"]}
|
package/dist/date.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assert } from 'chai';
|
|
2
|
-
import { formatDate } from './date.js';
|
|
2
|
+
import { formatDate, formatDateYMD, formatDateYMDHM } from './date.js';
|
|
3
3
|
describe('date formatting', () => {
|
|
4
4
|
describe('formatDate', () => {
|
|
5
5
|
it('formats a UTC date', () => {
|
|
@@ -27,5 +27,41 @@ describe('date formatting', () => {
|
|
|
27
27
|
assert.equal(formatDate(date, 'America/Chicago', { longTz: true }), '2018-01-01 06:00:00 (Central Standard Time)');
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
|
+
describe('formatDateYMD()', () => {
|
|
31
|
+
it('should handle a UTC date', () => {
|
|
32
|
+
const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0));
|
|
33
|
+
assert.equal(formatDateYMD(date, 'UTC'), '2018-01-01');
|
|
34
|
+
});
|
|
35
|
+
it('should handle a CST date', () => {
|
|
36
|
+
const date = new Date(Date.UTC(2018, 0, 3, 5, 0, 0));
|
|
37
|
+
assert.equal(formatDateYMD(date, 'America/Chicago'), '2018-01-02');
|
|
38
|
+
});
|
|
39
|
+
it('should handle a CDT date', () => {
|
|
40
|
+
const date = new Date(Date.UTC(2018, 6, 1, 12, 0, 0));
|
|
41
|
+
assert.equal(formatDateYMD(date, 'America/Chicago'), '2018-07-01');
|
|
42
|
+
});
|
|
43
|
+
it('should correctly format dates with zero hours', () => {
|
|
44
|
+
const date = new Date(Date.UTC(2018, 0, 1, 0, 1, 0));
|
|
45
|
+
assert.equal(formatDateYMD(date, 'UTC'), '2018-01-01');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe('formatDateYMDHM()', () => {
|
|
49
|
+
it('should handle a UTC date', () => {
|
|
50
|
+
const date = new Date(Date.UTC(2018, 0, 1, 12, 34, 7));
|
|
51
|
+
assert.equal(formatDateYMDHM(date, 'UTC'), '2018-01-01 12:34');
|
|
52
|
+
});
|
|
53
|
+
it('should handle a CST date', () => {
|
|
54
|
+
const date = new Date(Date.UTC(2018, 0, 3, 5, 10, 50));
|
|
55
|
+
assert.equal(formatDateYMDHM(date, 'America/Chicago'), '2018-01-02 23:10');
|
|
56
|
+
});
|
|
57
|
+
it('should handle a CDT date', () => {
|
|
58
|
+
const date = new Date(Date.UTC(2018, 6, 1, 19, 8, 19));
|
|
59
|
+
assert.equal(formatDateYMDHM(date, 'America/Chicago'), '2018-07-01 14:08');
|
|
60
|
+
});
|
|
61
|
+
it('should correctly format dates with zero hours', () => {
|
|
62
|
+
const date = new Date(Date.UTC(2018, 0, 1, 0, 1, 0));
|
|
63
|
+
assert.equal(formatDateYMDHM(date, 'UTC'), '2018-01-01 00:01');
|
|
64
|
+
});
|
|
65
|
+
});
|
|
30
66
|
});
|
|
31
67
|
//# sourceMappingURL=date.test.js.map
|
package/dist/date.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date.test.js","sourceRoot":"","sources":["../src/date.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"date.test.js","sourceRoot":"","sources":["../src/date.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert } from 'chai';\n\nimport { formatDate, formatDateYMD, formatDateYMDHM } from './date.js';\n\ndescribe('date formatting', () => {\n describe('formatDate', () => {\n it('formats a UTC date', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0));\n assert.equal(formatDate(date, 'UTC'), '2018-01-01 12:00:00 (UTC)');\n });\n it('formats a CST date', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0));\n assert.equal(formatDate(date, 'America/Chicago'), '2018-01-01 06:00:00 (CST)');\n });\n it('formats a CDT date', () => {\n const date = new Date(Date.UTC(2018, 6, 1, 12, 0, 0));\n assert.equal(formatDate(date, 'America/Chicago'), '2018-07-01 07:00:00 (CDT)');\n });\n it('formats dates with zero hours', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 0, 1, 0));\n assert.equal(formatDate(date, 'UTC'), '2018-01-01 00:01:00 (UTC)');\n });\n it('formats dates without the timezone', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0));\n assert.equal(formatDate(date, 'UTC', { includeTz: false }), '2018-01-01 12:00:00');\n });\n it('formats dates with the long timezone name', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0));\n assert.equal(\n formatDate(date, 'America/Chicago', { longTz: true }),\n '2018-01-01 06:00:00 (Central Standard Time)',\n );\n });\n });\n\n describe('formatDateYMD()', () => {\n it('should handle a UTC date', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0));\n assert.equal(formatDateYMD(date, 'UTC'), '2018-01-01');\n });\n it('should handle a CST date', () => {\n const date = new Date(Date.UTC(2018, 0, 3, 5, 0, 0));\n assert.equal(formatDateYMD(date, 'America/Chicago'), '2018-01-02');\n });\n it('should handle a CDT date', () => {\n const date = new Date(Date.UTC(2018, 6, 1, 12, 0, 0));\n assert.equal(formatDateYMD(date, 'America/Chicago'), '2018-07-01');\n });\n it('should correctly format dates with zero hours', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 0, 1, 0));\n assert.equal(formatDateYMD(date, 'UTC'), '2018-01-01');\n });\n });\n\n describe('formatDateYMDHM()', () => {\n it('should handle a UTC date', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 12, 34, 7));\n assert.equal(formatDateYMDHM(date, 'UTC'), '2018-01-01 12:34');\n });\n it('should handle a CST date', () => {\n const date = new Date(Date.UTC(2018, 0, 3, 5, 10, 50));\n assert.equal(formatDateYMDHM(date, 'America/Chicago'), '2018-01-02 23:10');\n });\n it('should handle a CDT date', () => {\n const date = new Date(Date.UTC(2018, 6, 1, 19, 8, 19));\n assert.equal(formatDateYMDHM(date, 'America/Chicago'), '2018-07-01 14:08');\n });\n it('should correctly format dates with zero hours', () => {\n const date = new Date(Date.UTC(2018, 0, 1, 0, 1, 0));\n assert.equal(formatDateYMDHM(date, 'UTC'), '2018-01-01 00:01');\n });\n });\n});\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { formatDate } from './date.js';
|
|
1
|
+
export { formatDate, formatDateYMD, formatDateYMDHM } from './date.js';
|
|
2
|
+
export { formatInterval } from './interval.js';
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC","sourcesContent":["export { formatDate } from './date.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC","sourcesContent":["export { formatDate, formatDateYMD, formatDateYMDHM } from './date.js';\nexport { formatInterval } from './interval.js';\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const SECOND_IN_MILLISECONDS = 1000;
|
|
2
|
+
export declare const MINUTE_IN_MILLISECONDS: number;
|
|
3
|
+
export declare const HOUR_IN_MILLISECONDS: number;
|
|
4
|
+
export declare const DAY_IN_MILLISECONDS: number;
|
|
5
|
+
/**
|
|
6
|
+
* Format an interval (in milliseconds) to a human-readable string like '3 h 40 m'.
|
|
7
|
+
*
|
|
8
|
+
* @param interval Time interval in milliseconds.
|
|
9
|
+
* @returns Human-readable string representing the interval.
|
|
10
|
+
*/
|
|
11
|
+
export declare function formatInterval(interval: number): string;
|
package/dist/interval.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const SECOND_IN_MILLISECONDS = 1000;
|
|
2
|
+
export const MINUTE_IN_MILLISECONDS = 60 * SECOND_IN_MILLISECONDS;
|
|
3
|
+
export const HOUR_IN_MILLISECONDS = 60 * MINUTE_IN_MILLISECONDS;
|
|
4
|
+
export const DAY_IN_MILLISECONDS = 24 * HOUR_IN_MILLISECONDS;
|
|
5
|
+
/**
|
|
6
|
+
* Format an interval (in milliseconds) to a human-readable string like '3 h 40 m'.
|
|
7
|
+
*
|
|
8
|
+
* @param interval Time interval in milliseconds.
|
|
9
|
+
* @returns Human-readable string representing the interval.
|
|
10
|
+
*/
|
|
11
|
+
export function formatInterval(interval) {
|
|
12
|
+
const sign = interval < 0 ? '-' : '';
|
|
13
|
+
const days = Math.floor(Math.abs(interval) / DAY_IN_MILLISECONDS);
|
|
14
|
+
const hours = Math.floor(Math.abs(interval) / HOUR_IN_MILLISECONDS) % 24;
|
|
15
|
+
const mins = Math.floor(Math.abs(interval) / MINUTE_IN_MILLISECONDS) % 60;
|
|
16
|
+
const secs = Math.floor(Math.abs(interval) / SECOND_IN_MILLISECONDS) % 60;
|
|
17
|
+
const parts = [];
|
|
18
|
+
if (days > 0) {
|
|
19
|
+
parts.push(`${sign}${days} d`);
|
|
20
|
+
}
|
|
21
|
+
if (hours > 0) {
|
|
22
|
+
parts.push(`${sign}${hours} h`);
|
|
23
|
+
}
|
|
24
|
+
if (mins > 0) {
|
|
25
|
+
parts.push(`${sign}${mins} min`);
|
|
26
|
+
}
|
|
27
|
+
if (secs > 0) {
|
|
28
|
+
parts.push(`${sign}${secs} s`);
|
|
29
|
+
}
|
|
30
|
+
if (parts.length === 0) {
|
|
31
|
+
parts.push('0 s');
|
|
32
|
+
}
|
|
33
|
+
return parts.join(' ');
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=interval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interval.js","sourceRoot":"","sources":["../src/interval.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAC3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,sBAAsB,CAAC;AAClE,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,GAAG,sBAAsB,CAAC;AAChE,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,oBAAoB,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,IAAI,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAErC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,mBAAmB,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,oBAAoB,CAAC,GAAG,EAAE,CAAC;IACzE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,sBAAsB,CAAC,GAAG,EAAE,CAAC;IAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,sBAAsB,CAAC,GAAG,EAAE,CAAC;IAE1E,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,IAAI,CAAC,CAAC;IAClC,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,MAAM,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC","sourcesContent":["export const SECOND_IN_MILLISECONDS = 1000;\nexport const MINUTE_IN_MILLISECONDS = 60 * SECOND_IN_MILLISECONDS;\nexport const HOUR_IN_MILLISECONDS = 60 * MINUTE_IN_MILLISECONDS;\nexport const DAY_IN_MILLISECONDS = 24 * HOUR_IN_MILLISECONDS;\n\n/**\n * Format an interval (in milliseconds) to a human-readable string like '3 h 40 m'.\n *\n * @param interval Time interval in milliseconds.\n * @returns Human-readable string representing the interval.\n */\nexport function formatInterval(interval: number): string {\n const sign = interval < 0 ? '-' : '';\n\n const days = Math.floor(Math.abs(interval) / DAY_IN_MILLISECONDS);\n const hours = Math.floor(Math.abs(interval) / HOUR_IN_MILLISECONDS) % 24;\n const mins = Math.floor(Math.abs(interval) / MINUTE_IN_MILLISECONDS) % 60;\n const secs = Math.floor(Math.abs(interval) / SECOND_IN_MILLISECONDS) % 60;\n\n const parts: string[] = [];\n\n if (days > 0) {\n parts.push(`${sign}${days} d`);\n }\n if (hours > 0) {\n parts.push(`${sign}${hours} h`);\n }\n if (mins > 0) {\n parts.push(`${sign}${mins} min`);\n }\n if (secs > 0) {\n parts.push(`${sign}${secs} s`);\n }\n if (parts.length === 0) {\n parts.push('0 s');\n }\n\n return parts.join(' ');\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { assert } from 'chai';
|
|
2
|
+
import { formatInterval } from './interval.js';
|
|
3
|
+
describe('interval formatting', () => {
|
|
4
|
+
describe('formatInterval()', () => {
|
|
5
|
+
it('should handle seconds', () => {
|
|
6
|
+
assert.equal(formatInterval(7000), '7 s');
|
|
7
|
+
});
|
|
8
|
+
it('should handle fractional seconds', () => {
|
|
9
|
+
assert.equal(formatInterval(7321), '7 s');
|
|
10
|
+
});
|
|
11
|
+
it('should handle minutes', () => {
|
|
12
|
+
assert.equal(formatInterval(2 * 60 * 1000), '2 min');
|
|
13
|
+
});
|
|
14
|
+
it('should handle hours', () => {
|
|
15
|
+
assert.equal(formatInterval(3 * 60 * 60 * 1000), '3 h');
|
|
16
|
+
});
|
|
17
|
+
it('should handle hours', () => {
|
|
18
|
+
assert.equal(formatInterval(4 * 24 * 60 * 60 * 1000), '4 d');
|
|
19
|
+
});
|
|
20
|
+
it('should handle complex intervals', () => {
|
|
21
|
+
assert.equal(formatInterval((((4 * 24 + 3) * 60 + 2) * 60 + 7) * 1000), '4 d 3 h 2 min 7 s');
|
|
22
|
+
});
|
|
23
|
+
it('should handle zero', () => {
|
|
24
|
+
assert.equal(formatInterval(0), '0 s');
|
|
25
|
+
});
|
|
26
|
+
it('should handle negative intervals', () => {
|
|
27
|
+
assert.equal(formatInterval(-(((4 * 24 + 3) * 60 + 2) * 60 + 7) * 1000), '-4 d -3 h -2 min -7 s');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=interval.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interval.test.js","sourceRoot":"","sources":["../src/interval.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;YAC/B,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;YAC7B,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;QAC1D,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;YAC7B,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;YACzC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,mBAAmB,CAAC,CAAC;QAC/F,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;YAC5B,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;YAC1C,MAAM,CAAC,KAAK,CACV,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAC1D,uBAAuB,CACxB,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { assert } from 'chai';\n\nimport { formatInterval } from './interval.js';\n\ndescribe('interval formatting', () => {\n describe('formatInterval()', () => {\n it('should handle seconds', () => {\n assert.equal(formatInterval(7000), '7 s');\n });\n it('should handle fractional seconds', () => {\n assert.equal(formatInterval(7321), '7 s');\n });\n it('should handle minutes', () => {\n assert.equal(formatInterval(2 * 60 * 1000), '2 min');\n });\n it('should handle hours', () => {\n assert.equal(formatInterval(3 * 60 * 60 * 1000), '3 h');\n });\n it('should handle hours', () => {\n assert.equal(formatInterval(4 * 24 * 60 * 60 * 1000), '4 d');\n });\n it('should handle complex intervals', () => {\n assert.equal(formatInterval((((4 * 24 + 3) * 60 + 2) * 60 + 7) * 1000), '4 d 3 h 2 min 7 s');\n });\n it('should handle zero', () => {\n assert.equal(formatInterval(0), '0 s');\n });\n it('should handle negative intervals', () => {\n assert.equal(\n formatInterval(-(((4 * 24 + 3) * 60 + 2) * 60 + 7) * 1000),\n '-4 d -3 h -2 min -7 s',\n );\n });\n });\n});\n"]}
|
package/package.json
CHANGED
package/src/date.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assert } from 'chai';
|
|
2
2
|
|
|
3
|
-
import { formatDate } from './date.js';
|
|
3
|
+
import { formatDate, formatDateYMD, formatDateYMDHM } from './date.js';
|
|
4
4
|
|
|
5
5
|
describe('date formatting', () => {
|
|
6
6
|
describe('formatDate', () => {
|
|
@@ -32,4 +32,42 @@ describe('date formatting', () => {
|
|
|
32
32
|
);
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
describe('formatDateYMD()', () => {
|
|
37
|
+
it('should handle a UTC date', () => {
|
|
38
|
+
const date = new Date(Date.UTC(2018, 0, 1, 12, 0, 0));
|
|
39
|
+
assert.equal(formatDateYMD(date, 'UTC'), '2018-01-01');
|
|
40
|
+
});
|
|
41
|
+
it('should handle a CST date', () => {
|
|
42
|
+
const date = new Date(Date.UTC(2018, 0, 3, 5, 0, 0));
|
|
43
|
+
assert.equal(formatDateYMD(date, 'America/Chicago'), '2018-01-02');
|
|
44
|
+
});
|
|
45
|
+
it('should handle a CDT date', () => {
|
|
46
|
+
const date = new Date(Date.UTC(2018, 6, 1, 12, 0, 0));
|
|
47
|
+
assert.equal(formatDateYMD(date, 'America/Chicago'), '2018-07-01');
|
|
48
|
+
});
|
|
49
|
+
it('should correctly format dates with zero hours', () => {
|
|
50
|
+
const date = new Date(Date.UTC(2018, 0, 1, 0, 1, 0));
|
|
51
|
+
assert.equal(formatDateYMD(date, 'UTC'), '2018-01-01');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('formatDateYMDHM()', () => {
|
|
56
|
+
it('should handle a UTC date', () => {
|
|
57
|
+
const date = new Date(Date.UTC(2018, 0, 1, 12, 34, 7));
|
|
58
|
+
assert.equal(formatDateYMDHM(date, 'UTC'), '2018-01-01 12:34');
|
|
59
|
+
});
|
|
60
|
+
it('should handle a CST date', () => {
|
|
61
|
+
const date = new Date(Date.UTC(2018, 0, 3, 5, 10, 50));
|
|
62
|
+
assert.equal(formatDateYMDHM(date, 'America/Chicago'), '2018-01-02 23:10');
|
|
63
|
+
});
|
|
64
|
+
it('should handle a CDT date', () => {
|
|
65
|
+
const date = new Date(Date.UTC(2018, 6, 1, 19, 8, 19));
|
|
66
|
+
assert.equal(formatDateYMDHM(date, 'America/Chicago'), '2018-07-01 14:08');
|
|
67
|
+
});
|
|
68
|
+
it('should correctly format dates with zero hours', () => {
|
|
69
|
+
const date = new Date(Date.UTC(2018, 0, 1, 0, 1, 0));
|
|
70
|
+
assert.equal(formatDateYMDHM(date, 'UTC'), '2018-01-01 00:01');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
35
73
|
});
|
package/src/date.ts
CHANGED
|
@@ -32,3 +32,43 @@ export function formatDate(
|
|
|
32
32
|
}
|
|
33
33
|
return dateFormatted;
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format a date to a human-readable string like '2020-03-27'.
|
|
38
|
+
*
|
|
39
|
+
* @param date The date to format.
|
|
40
|
+
* @param timeZone The time zone to use for formatting.
|
|
41
|
+
* @returns Human-readable string representing the date.
|
|
42
|
+
*/
|
|
43
|
+
export function formatDateYMD(date: Date, timeZone: string): string {
|
|
44
|
+
const options: Intl.DateTimeFormatOptions = {
|
|
45
|
+
timeZone,
|
|
46
|
+
hourCycle: 'h23',
|
|
47
|
+
year: 'numeric',
|
|
48
|
+
month: '2-digit',
|
|
49
|
+
day: '2-digit',
|
|
50
|
+
};
|
|
51
|
+
const parts = keyBy(new Intl.DateTimeFormat('en-US', options).formatToParts(date), (x) => x.type);
|
|
52
|
+
return `${parts.year.value}-${parts.month.value}-${parts.day.value}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Format a date to a human-readable string like '2020-03-27 14:27'.
|
|
57
|
+
*
|
|
58
|
+
* @param date The date to format.
|
|
59
|
+
* @param timeZone The time zone to use for formatting.
|
|
60
|
+
* @returns Human-readable string representing the date.
|
|
61
|
+
*/
|
|
62
|
+
export function formatDateYMDHM(date: Date, timeZone: string): string {
|
|
63
|
+
const options: Intl.DateTimeFormatOptions = {
|
|
64
|
+
timeZone,
|
|
65
|
+
hourCycle: 'h23',
|
|
66
|
+
year: 'numeric',
|
|
67
|
+
month: '2-digit',
|
|
68
|
+
day: '2-digit',
|
|
69
|
+
hour: '2-digit',
|
|
70
|
+
minute: '2-digit',
|
|
71
|
+
};
|
|
72
|
+
const parts = keyBy(new Intl.DateTimeFormat('en-US', options).formatToParts(date), (x) => x.type);
|
|
73
|
+
return `${parts.year.value}-${parts.month.value}-${parts.day.value} ${parts.hour.value}:${parts.minute.value}`;
|
|
74
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export { formatDate } from './date.js';
|
|
1
|
+
export { formatDate, formatDateYMD, formatDateYMDHM } from './date.js';
|
|
2
|
+
export { formatInterval } from './interval.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { assert } from 'chai';
|
|
2
|
+
|
|
3
|
+
import { formatInterval } from './interval.js';
|
|
4
|
+
|
|
5
|
+
describe('interval formatting', () => {
|
|
6
|
+
describe('formatInterval()', () => {
|
|
7
|
+
it('should handle seconds', () => {
|
|
8
|
+
assert.equal(formatInterval(7000), '7 s');
|
|
9
|
+
});
|
|
10
|
+
it('should handle fractional seconds', () => {
|
|
11
|
+
assert.equal(formatInterval(7321), '7 s');
|
|
12
|
+
});
|
|
13
|
+
it('should handle minutes', () => {
|
|
14
|
+
assert.equal(formatInterval(2 * 60 * 1000), '2 min');
|
|
15
|
+
});
|
|
16
|
+
it('should handle hours', () => {
|
|
17
|
+
assert.equal(formatInterval(3 * 60 * 60 * 1000), '3 h');
|
|
18
|
+
});
|
|
19
|
+
it('should handle hours', () => {
|
|
20
|
+
assert.equal(formatInterval(4 * 24 * 60 * 60 * 1000), '4 d');
|
|
21
|
+
});
|
|
22
|
+
it('should handle complex intervals', () => {
|
|
23
|
+
assert.equal(formatInterval((((4 * 24 + 3) * 60 + 2) * 60 + 7) * 1000), '4 d 3 h 2 min 7 s');
|
|
24
|
+
});
|
|
25
|
+
it('should handle zero', () => {
|
|
26
|
+
assert.equal(formatInterval(0), '0 s');
|
|
27
|
+
});
|
|
28
|
+
it('should handle negative intervals', () => {
|
|
29
|
+
assert.equal(
|
|
30
|
+
formatInterval(-(((4 * 24 + 3) * 60 + 2) * 60 + 7) * 1000),
|
|
31
|
+
'-4 d -3 h -2 min -7 s',
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
package/src/interval.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export const SECOND_IN_MILLISECONDS = 1000;
|
|
2
|
+
export const MINUTE_IN_MILLISECONDS = 60 * SECOND_IN_MILLISECONDS;
|
|
3
|
+
export const HOUR_IN_MILLISECONDS = 60 * MINUTE_IN_MILLISECONDS;
|
|
4
|
+
export const DAY_IN_MILLISECONDS = 24 * HOUR_IN_MILLISECONDS;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format an interval (in milliseconds) to a human-readable string like '3 h 40 m'.
|
|
8
|
+
*
|
|
9
|
+
* @param interval Time interval in milliseconds.
|
|
10
|
+
* @returns Human-readable string representing the interval.
|
|
11
|
+
*/
|
|
12
|
+
export function formatInterval(interval: number): string {
|
|
13
|
+
const sign = interval < 0 ? '-' : '';
|
|
14
|
+
|
|
15
|
+
const days = Math.floor(Math.abs(interval) / DAY_IN_MILLISECONDS);
|
|
16
|
+
const hours = Math.floor(Math.abs(interval) / HOUR_IN_MILLISECONDS) % 24;
|
|
17
|
+
const mins = Math.floor(Math.abs(interval) / MINUTE_IN_MILLISECONDS) % 60;
|
|
18
|
+
const secs = Math.floor(Math.abs(interval) / SECOND_IN_MILLISECONDS) % 60;
|
|
19
|
+
|
|
20
|
+
const parts: string[] = [];
|
|
21
|
+
|
|
22
|
+
if (days > 0) {
|
|
23
|
+
parts.push(`${sign}${days} d`);
|
|
24
|
+
}
|
|
25
|
+
if (hours > 0) {
|
|
26
|
+
parts.push(`${sign}${hours} h`);
|
|
27
|
+
}
|
|
28
|
+
if (mins > 0) {
|
|
29
|
+
parts.push(`${sign}${mins} min`);
|
|
30
|
+
}
|
|
31
|
+
if (secs > 0) {
|
|
32
|
+
parts.push(`${sign}${secs} s`);
|
|
33
|
+
}
|
|
34
|
+
if (parts.length === 0) {
|
|
35
|
+
parts.push('0 s');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return parts.join(' ');
|
|
39
|
+
}
|