@choksheak/ts-utils 0.1.9 → 0.2.1
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/dateTimeStr.d.ts +74 -0
- package/dateTimeStr.js +29 -4
- package/dateTimeStr.js.map +1 -1
- package/duration.d.ts +18 -6
- package/duration.js +5 -0
- package/duration.js.map +1 -1
- package/kvStore.d.ts +33 -2
- package/kvStore.js +80 -12
- package/kvStore.js.map +1 -1
- package/localStorageCache.d.ts +19 -2
- package/localStorageCache.js +29 -16
- package/localStorageCache.js.map +1 -1
- package/nonEmpty.d.ts +3 -0
- package/nonEmpty.js.map +1 -1
- package/nonNil.d.ts +3 -0
- package/nonNil.js.map +1 -1
- package/package.json +1 -1
- package/safeParseInt.d.ts +3 -0
- package/safeParseInt.js.map +1 -1
- package/sleep.d.ts +4 -0
- package/sleep.js.map +1 -1
- package/src/dateTimeStr.ts +99 -16
- package/src/duration.ts +21 -6
- package/src/kvStore.ts +77 -15
- package/src/localStorageCache.ts +47 -26
- package/src/nonEmpty.ts +3 -0
- package/src/nonNil.ts +3 -0
- package/src/safeParseInt.ts +3 -0
- package/src/sleep.ts +4 -0
package/localStorageCache.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { Duration } from "./duration";
|
|
2
|
+
export type StoredItem<T> = {
|
|
3
|
+
value: T;
|
|
4
|
+
storedMs: number;
|
|
5
|
+
expiryMs: number;
|
|
6
|
+
};
|
|
2
7
|
/**
|
|
3
8
|
* Simple local storage cache with support for auto-expiration.
|
|
4
9
|
* Note that this works in the browser context only because nodejs does not
|
|
@@ -11,13 +16,19 @@ import { Duration } from "./duration";
|
|
|
11
16
|
* In order to provide proper type-checking, please always specify the T
|
|
12
17
|
* type parameter. E.g. const item = storeItem<string>("name", 10_000);
|
|
13
18
|
*
|
|
14
|
-
*
|
|
19
|
+
* @param key The store key in local storage.
|
|
20
|
+
* @param expires Either a number in milliseconds, or a Duration object
|
|
21
|
+
* @param logError Log an error if we found an invalid object in the store.
|
|
22
|
+
* The invalid object is usually a string that cannot be parsed as JSON.
|
|
23
|
+
* @param defaultValue Specify a default value to use for the object. Defaults
|
|
24
|
+
* to undefined.
|
|
15
25
|
*/
|
|
16
26
|
export declare function storeItem<T>(key: string, expires: number | Duration, logError?: boolean, defaultValue?: T): CacheItem<T>;
|
|
17
27
|
declare class CacheItem<T> {
|
|
18
28
|
readonly key: string;
|
|
19
29
|
readonly expireDeltaMs: number;
|
|
20
30
|
readonly logError: boolean;
|
|
31
|
+
readonly defaultValue: T | undefined;
|
|
21
32
|
/**
|
|
22
33
|
* Create a cache item accessor object with auto-expiration.
|
|
23
34
|
*/
|
|
@@ -25,7 +36,13 @@ declare class CacheItem<T> {
|
|
|
25
36
|
/**
|
|
26
37
|
* Set the value of this item with auto-expiration.
|
|
27
38
|
*/
|
|
28
|
-
set(value: T): void;
|
|
39
|
+
set(value: T, expiryDelta?: number | Duration): void;
|
|
40
|
+
/**
|
|
41
|
+
* Example usage:
|
|
42
|
+
*
|
|
43
|
+
* const { value, storedMs, expiryMs } = await myItem.getStoredItem();
|
|
44
|
+
*/
|
|
45
|
+
getStoredItem(): StoredItem<T> | undefined;
|
|
29
46
|
/**
|
|
30
47
|
* Get the value of this item, or undefined if value is not set or expired.
|
|
31
48
|
*/
|
package/localStorageCache.js
CHANGED
|
@@ -40,10 +40,13 @@ function durationToMs(duration) {
|
|
|
40
40
|
const msMs = (_e = duration.milliseconds) != null ? _e : 0;
|
|
41
41
|
return daysMs + hoursMs + minsMs + secsMs + msMs;
|
|
42
42
|
}
|
|
43
|
+
function durationOrMsToMs(duration) {
|
|
44
|
+
return typeof duration === "number" ? duration : durationToMs(duration);
|
|
45
|
+
}
|
|
43
46
|
|
|
44
47
|
// src/localStorageCache.ts
|
|
45
48
|
function storeItem(key, expires, logError = true, defaultValue) {
|
|
46
|
-
const expireDeltaMs =
|
|
49
|
+
const expireDeltaMs = durationOrMsToMs(expires);
|
|
47
50
|
return new CacheItem(key, expireDeltaMs, logError, defaultValue);
|
|
48
51
|
}
|
|
49
52
|
var CacheItem = class {
|
|
@@ -54,35 +57,38 @@ var CacheItem = class {
|
|
|
54
57
|
this.key = key;
|
|
55
58
|
this.expireDeltaMs = expireDeltaMs;
|
|
56
59
|
this.logError = logError;
|
|
57
|
-
|
|
58
|
-
if (this.get() === void 0) {
|
|
59
|
-
this.set(defaultValue);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
60
|
+
this.defaultValue = defaultValue;
|
|
62
61
|
}
|
|
63
62
|
/**
|
|
64
63
|
* Set the value of this item with auto-expiration.
|
|
65
64
|
*/
|
|
66
|
-
set(value) {
|
|
67
|
-
const
|
|
68
|
-
const
|
|
65
|
+
set(value, expiryDelta = this.expireDeltaMs) {
|
|
66
|
+
const nowMs = Date.now();
|
|
67
|
+
const toStore = {
|
|
68
|
+
value,
|
|
69
|
+
storedMs: nowMs,
|
|
70
|
+
expiryMs: nowMs + durationOrMsToMs(expiryDelta)
|
|
71
|
+
};
|
|
72
|
+
const valueStr = JSON.stringify(toStore);
|
|
69
73
|
globalThis.localStorage.setItem(this.key, valueStr);
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
72
|
-
*
|
|
76
|
+
* Example usage:
|
|
77
|
+
*
|
|
78
|
+
* const { value, storedMs, expiryMs } = await myItem.getStoredItem();
|
|
73
79
|
*/
|
|
74
|
-
|
|
80
|
+
getStoredItem() {
|
|
75
81
|
const jsonStr = globalThis.localStorage.getItem(this.key);
|
|
76
|
-
if (!jsonStr
|
|
82
|
+
if (!jsonStr) {
|
|
77
83
|
return void 0;
|
|
78
84
|
}
|
|
79
85
|
try {
|
|
80
86
|
const obj = JSON.parse(jsonStr);
|
|
81
|
-
if (!obj || typeof obj !== "object" || !("value" in obj) || !("
|
|
82
|
-
|
|
87
|
+
if (!obj || typeof obj !== "object" || !("value" in obj) || !("storedMs" in obj) || typeof obj.storedMs !== "number" || !("expiryMs" in obj) || typeof obj.expiryMs !== "number" || Date.now() >= obj.expiryMs) {
|
|
88
|
+
this.remove();
|
|
83
89
|
return void 0;
|
|
84
90
|
}
|
|
85
|
-
return obj
|
|
91
|
+
return obj;
|
|
86
92
|
} catch (e) {
|
|
87
93
|
if (this.logError) {
|
|
88
94
|
console.error(
|
|
@@ -90,10 +96,17 @@ var CacheItem = class {
|
|
|
90
96
|
e
|
|
91
97
|
);
|
|
92
98
|
}
|
|
93
|
-
|
|
99
|
+
this.remove();
|
|
94
100
|
return void 0;
|
|
95
101
|
}
|
|
96
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Get the value of this item, or undefined if value is not set or expired.
|
|
105
|
+
*/
|
|
106
|
+
get() {
|
|
107
|
+
const stored = this.getStoredItem();
|
|
108
|
+
return stored !== void 0 ? stored.value : this.defaultValue;
|
|
109
|
+
}
|
|
97
110
|
/**
|
|
98
111
|
* Remove the value of this item.
|
|
99
112
|
*/
|
package/localStorageCache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/localStorageCache.ts","../src/timeConstants.ts","../src/duration.ts"],"sourcesContent":["import { Duration, durationToMs } from \"./duration\";\n\n/**\n * Simple local storage cache with support for auto-expiration.\n * Note that this works in the browser context only because nodejs does not\n * have local storage.\n *\n * Create a cache item accessor object with auto-expiration. The value will\n * always be stored as a string by applying JSON.stringify(), and will be\n * returned in the same object type by applying JSON.parse().\n *\n * In order to provide proper type-checking, please always specify the T\n * type parameter. E.g. const item = storeItem<string>(\"name\", 10_000);\n *\n * expires - Either a number in milliseconds, or a Duration object\n */\nexport function storeItem<T>(\n key: string,\n expires: number | Duration,\n logError = true,\n defaultValue?: T,\n) {\n const expireDeltaMs =\n typeof expires === \"number\" ? expires : durationToMs(expires);\n\n return new CacheItem<T>(key, expireDeltaMs, logError, defaultValue);\n}\n\nclass CacheItem<T> {\n /**\n * Create a cache item accessor object with auto-expiration.\n */\n public constructor(\n public readonly key: string,\n public readonly expireDeltaMs: number,\n public readonly logError: boolean,\n defaultValue: T | undefined,\n ) {\n if (defaultValue !== undefined) {\n if (this.get() === undefined) {\n this.set(defaultValue);\n }\n }\n }\n\n /**\n * Set the value of this item with auto-expiration.\n */\n public set(value: T): void {\n const expireMs = Date.now() + this.expireDeltaMs;\n const valueStr = JSON.stringify({ value, expireMs });\n\n globalThis.localStorage.setItem(this.key, valueStr);\n }\n\n /**\n * Get the value of this item, or undefined if value is not set or expired.\n */\n public get(): T | undefined {\n const jsonStr = globalThis.localStorage.getItem(this.key);\n\n if (!jsonStr || typeof jsonStr !== \"string\") {\n return undefined;\n }\n\n try {\n const obj: { value: T; expireMs: number } | undefined =\n JSON.parse(jsonStr);\n if (\n !obj ||\n typeof obj !== \"object\" ||\n !(\"value\" in obj) ||\n !(\"expireMs\" in obj) ||\n typeof obj.expireMs !== \"number\" ||\n Date.now() >= obj.expireMs\n ) {\n globalThis.localStorage.removeItem(this.key);\n return undefined;\n }\n\n return obj.value;\n } catch (e) {\n if (this.logError) {\n console.error(\n `Found invalid storage value: ${this.key}=${jsonStr}:`,\n e,\n );\n }\n globalThis.localStorage.removeItem(this.key);\n return undefined;\n }\n }\n\n /**\n * Remove the value of this item.\n */\n public remove(): void {\n globalThis.localStorage.removeItem(this.key);\n }\n}\n","/**\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nexport const MS_PER_SECOND = 1000;\nexport const MS_PER_MINUTE = 60_000;\nexport const MS_PER_HOUR = 3_600_000;\nexport const MS_PER_DAY = 86_400_000;\nexport const MS_PER_WEEK = 604_800_000;\n\nexport const SECONDS_PER_MINUTE = 60;\nexport const SECONDS_PER_HOUR = 3_600;\nexport const SECONDS_PER_DAY = 86_400;\nexport const SECONDS_PER_WEEK = 604_800;\n\nexport const MINUTES_PER_HOUR = 60;\nexport const MINUTES_PER_DAY = 1440;\nexport const MINUTES_PER_WEEK = 10_080;\n\nexport const HOURS_PER_DAY = 24;\nexport const HOURS_PER_WEEK = 168;\n","/**\n * Bunch of miscellaneous constants and utility functions related to handling\n * date and time durations.\n *\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nimport {\n MS_PER_SECOND,\n SECONDS_PER_MINUTE,\n MINUTES_PER_HOUR,\n HOURS_PER_DAY,\n MS_PER_DAY,\n MS_PER_MINUTE,\n MS_PER_HOUR,\n} from \"./timeConstants\";\n\nexport type Duration = {\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n};\n\nexport type DurationType = keyof Duration;\n\nexport const DURATION_TYPE_SEQUENCE: DurationType[] = [\n \"days\",\n \"hours\",\n \"minutes\",\n \"seconds\",\n \"milliseconds\",\n];\n\n/**\n * Follows the same format as Intl.DurationFormat.prototype.format().\n *\n * Short: 1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns\n * Long: 1 year, 2 months, 3 weeks, 3 days, 4 hours, 5 minutes, 6 seconds,\n * 7 milliseconds, 8 microseconds, 9 nanoseconds\n * Narrow: 1y 2mo 3w 3d 4h 5m 6s 7ms 8μs 9ns\n */\nexport type DurationStyle = \"short\" | \"long\" | \"narrow\";\n\nexport type DurationSuffixMap = {\n short: string;\n shorts: string;\n long: string;\n longs: string;\n narrow: string;\n};\n\nexport type DurationSuffixType = keyof DurationSuffixMap;\n\nexport const DURATION_STYLE_SUFFIX_MAP: Record<\n DurationType,\n DurationSuffixMap\n> = {\n days: {\n short: \"day\",\n shorts: \"days\",\n long: \"day\",\n longs: \"days\",\n narrow: \"d\",\n },\n hours: {\n short: \"hr\",\n shorts: \"hrs\",\n long: \"hour\",\n longs: \"hours\",\n narrow: \"h\",\n },\n minutes: {\n short: \"min\",\n shorts: \"mins\",\n long: \"minute\",\n longs: \"minutes\",\n narrow: \"m\",\n },\n seconds: {\n short: \"sec\",\n shorts: \"secs\",\n long: \"second\",\n longs: \"seconds\",\n narrow: \"s\",\n },\n milliseconds: {\n short: \"ms\",\n shorts: \"ms\",\n long: \"millisecond\",\n longs: \"milliseconds\",\n narrow: \"ms\",\n },\n};\n\nfunction getDurationStyleForPlural(style: DurationStyle): DurationSuffixType {\n return style == \"short\" ? \"shorts\" : style === \"long\" ? \"longs\" : style;\n}\n\nfunction getValueAndUnitSeparator(style: DurationStyle): string {\n return style === \"narrow\" ? \"\" : \" \";\n}\n\nfunction getDurationTypeSeparator(style: DurationStyle): string {\n return style === \"narrow\" ? \" \" : \", \";\n}\n\n/**\n * Convert a milliseconds duration into a Duration object. If the given ms is\n * zero, then return an object with a single field of zero.\n *\n * durationTypeForZero - Defaults to 'milliseconds'\n */\nexport function msToDuration(\n ms: number,\n durationTypeForZero?: DurationType,\n): Duration {\n if (ms === 0) {\n durationTypeForZero = durationTypeForZero ?? \"milliseconds\";\n return { [durationTypeForZero]: 0 };\n }\n\n const duration: Duration = {};\n\n for (let i = 0; i < 1; i++) {\n let seconds = Math.floor(ms / MS_PER_SECOND);\n const millis = ms - seconds * MS_PER_SECOND;\n\n if (millis > 0) {\n duration[\"milliseconds\"] = millis;\n }\n\n if (seconds === 0) {\n break;\n }\n\n let minutes = Math.floor(seconds / SECONDS_PER_MINUTE);\n seconds -= minutes * SECONDS_PER_MINUTE;\n\n if (seconds > 0) {\n duration[\"seconds\"] = seconds;\n }\n\n if (minutes === 0) {\n break;\n }\n\n let hours = Math.floor(minutes / MINUTES_PER_HOUR);\n minutes -= hours * MINUTES_PER_HOUR;\n\n if (minutes > 0) {\n duration[\"minutes\"] = minutes;\n }\n\n if (hours === 0) {\n break;\n }\n\n const days = Math.floor(hours / HOURS_PER_DAY);\n hours -= days * HOURS_PER_DAY;\n\n if (hours > 0) {\n duration[\"hours\"] = hours;\n }\n\n if (days > 0) {\n duration[\"days\"] = days;\n }\n }\n\n return duration;\n}\n\n/**\n * Returns the number of milliseconds for the given duration.\n */\nexport function durationToMs(duration: Duration): number {\n const daysMs = (duration.days ?? 0) * MS_PER_DAY;\n const hoursMs = (duration.hours ?? 0) * MS_PER_HOUR;\n const minsMs = (duration.minutes ?? 0) * MS_PER_MINUTE;\n const secsMs = (duration.seconds ?? 0) * MS_PER_SECOND;\n const msMs = duration.milliseconds ?? 0;\n\n return daysMs + hoursMs + minsMs + secsMs + msMs;\n}\n\n/**\n * Format a given Duration object into a string. If the object has no fields,\n * then returns an empty string.\n *\n * style - Defaults to 'short'\n */\nexport function formatDuration(duration: Duration, style?: DurationStyle) {\n style = style ?? \"short\";\n const stylePlural = getDurationStyleForPlural(style);\n\n const space = getValueAndUnitSeparator(style);\n\n const a: string[] = [];\n\n for (const unit of DURATION_TYPE_SEQUENCE) {\n const value = duration[unit];\n if (value === undefined) continue;\n\n const suffixMap = DURATION_STYLE_SUFFIX_MAP[unit];\n const suffix = value === 1 ? suffixMap[style] : suffixMap[stylePlural];\n a.push(value + space + suffix);\n }\n\n const separator = getDurationTypeSeparator(style);\n return a.join(separator);\n}\n\n/**\n * Convert a millisecond duration into a human-readable duration string.\n *\n * options.durationTypeForZero - Defaults to 'milliseconds'\n * options.style - Defaults to 'short'\n */\nexport function readableDuration(\n ms: number,\n options?: { durationTypeForZero?: DurationType; style?: DurationStyle },\n): string {\n const duration = msToDuration(ms, options?.durationTypeForZero);\n\n return formatDuration(duration, options?.style);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKO,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,cAAc;AACpB,IAAM,aAAa;;;AC0KnB,SAAS,aAAa,UAA4B;AAlLzD;AAmLE,QAAM,WAAU,cAAS,SAAT,YAAiB,KAAK;AACtC,QAAM,YAAW,cAAS,UAAT,YAAkB,KAAK;AACxC,QAAM,WAAU,cAAS,YAAT,YAAoB,KAAK;AACzC,QAAM,WAAU,cAAS,YAAT,YAAoB,KAAK;AACzC,QAAM,QAAO,cAAS,iBAAT,YAAyB;AAEtC,SAAO,SAAS,UAAU,SAAS,SAAS;AAC9C;;;AF1KO,SAAS,UACd,KACA,SACA,WAAW,MACX,cACA;AACA,QAAM,gBACJ,OAAO,YAAY,WAAW,UAAU,aAAa,OAAO;AAE9D,SAAO,IAAI,UAAa,KAAK,eAAe,UAAU,YAAY;AACpE;AAEA,IAAM,YAAN,MAAmB;AAAA;AAAA;AAAA;AAAA,EAIV,YACW,KACA,eACA,UAChB,cACA;AAJgB;AACA;AACA;AAGhB,QAAI,iBAAiB,QAAW;AAC9B,UAAI,KAAK,IAAI,MAAM,QAAW;AAC5B,aAAK,IAAI,YAAY;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,IAAI,OAAgB;AACzB,UAAM,WAAW,KAAK,IAAI,IAAI,KAAK;AACnC,UAAM,WAAW,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;AAEnD,eAAW,aAAa,QAAQ,KAAK,KAAK,QAAQ;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKO,MAAqB;AAC1B,UAAM,UAAU,WAAW,aAAa,QAAQ,KAAK,GAAG;AAExD,QAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,MACJ,KAAK,MAAM,OAAO;AACpB,UACE,CAAC,OACD,OAAO,QAAQ,YACf,EAAE,WAAW,QACb,EAAE,cAAc,QAChB,OAAO,IAAI,aAAa,YACxB,KAAK,IAAI,KAAK,IAAI,UAClB;AACA,mBAAW,aAAa,WAAW,KAAK,GAAG;AAC3C,eAAO;AAAA,MACT;AAEA,aAAO,IAAI;AAAA,IACb,SAAS,GAAG;AACV,UAAI,KAAK,UAAU;AACjB,gBAAQ;AAAA,UACN,gCAAgC,KAAK,GAAG,IAAI,OAAO;AAAA,UACnD;AAAA,QACF;AAAA,MACF;AACA,iBAAW,aAAa,WAAW,KAAK,GAAG;AAC3C,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,SAAe;AACpB,eAAW,aAAa,WAAW,KAAK,GAAG;AAAA,EAC7C;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/localStorageCache.ts","../src/timeConstants.ts","../src/duration.ts"],"sourcesContent":["import { Duration, durationOrMsToMs } from \"./duration\";\n\nexport type StoredItem<T> = { value: T; storedMs: number; expiryMs: number };\n\n/**\n * Simple local storage cache with support for auto-expiration.\n * Note that this works in the browser context only because nodejs does not\n * have local storage.\n *\n * Create a cache item accessor object with auto-expiration. The value will\n * always be stored as a string by applying JSON.stringify(), and will be\n * returned in the same object type by applying JSON.parse().\n *\n * In order to provide proper type-checking, please always specify the T\n * type parameter. E.g. const item = storeItem<string>(\"name\", 10_000);\n *\n * @param key The store key in local storage.\n * @param expires Either a number in milliseconds, or a Duration object\n * @param logError Log an error if we found an invalid object in the store.\n * The invalid object is usually a string that cannot be parsed as JSON.\n * @param defaultValue Specify a default value to use for the object. Defaults\n * to undefined.\n */\nexport function storeItem<T>(\n key: string,\n expires: number | Duration,\n logError = true,\n defaultValue?: T,\n) {\n const expireDeltaMs = durationOrMsToMs(expires);\n\n return new CacheItem<T>(key, expireDeltaMs, logError, defaultValue);\n}\n\nclass CacheItem<T> {\n /**\n * Create a cache item accessor object with auto-expiration.\n */\n public constructor(\n public readonly key: string,\n public readonly expireDeltaMs: number,\n public readonly logError: boolean,\n public readonly defaultValue: T | undefined,\n ) {}\n\n /**\n * Set the value of this item with auto-expiration.\n */\n public set(\n value: T,\n expiryDelta: number | Duration = this.expireDeltaMs,\n ): void {\n const nowMs = Date.now();\n const toStore: StoredItem<T> = {\n value,\n storedMs: nowMs,\n expiryMs: nowMs + durationOrMsToMs(expiryDelta),\n };\n const valueStr = JSON.stringify(toStore);\n\n globalThis.localStorage.setItem(this.key, valueStr);\n }\n\n /**\n * Example usage:\n *\n * const { value, storedMs, expiryMs } = await myItem.getStoredItem();\n */\n public getStoredItem(): StoredItem<T> | undefined {\n const jsonStr = globalThis.localStorage.getItem(this.key);\n\n if (!jsonStr) {\n return undefined;\n }\n\n try {\n const obj: StoredItem<T> | undefined = JSON.parse(jsonStr);\n\n if (\n !obj ||\n typeof obj !== \"object\" ||\n !(\"value\" in obj) ||\n !(\"storedMs\" in obj) ||\n typeof obj.storedMs !== \"number\" ||\n !(\"expiryMs\" in obj) ||\n typeof obj.expiryMs !== \"number\" ||\n Date.now() >= obj.expiryMs\n ) {\n this.remove();\n return undefined;\n }\n\n return obj;\n } catch (e) {\n if (this.logError) {\n console.error(\n `Found invalid storage value: ${this.key}=${jsonStr}:`,\n e,\n );\n }\n this.remove();\n return undefined;\n }\n }\n\n /**\n * Get the value of this item, or undefined if value is not set or expired.\n */\n public get(): T | undefined {\n const stored = this.getStoredItem();\n\n return stored !== undefined ? stored.value : this.defaultValue;\n }\n\n /**\n * Remove the value of this item.\n */\n public remove(): void {\n globalThis.localStorage.removeItem(this.key);\n }\n}\n","/**\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file.\n */\n\nexport const MS_PER_SECOND = 1000;\nexport const MS_PER_MINUTE = 60_000;\nexport const MS_PER_HOUR = 3_600_000;\nexport const MS_PER_DAY = 86_400_000;\nexport const MS_PER_WEEK = 604_800_000;\n\nexport const SECONDS_PER_MINUTE = 60;\nexport const SECONDS_PER_HOUR = 3_600;\nexport const SECONDS_PER_DAY = 86_400;\nexport const SECONDS_PER_WEEK = 604_800;\n\nexport const MINUTES_PER_HOUR = 60;\nexport const MINUTES_PER_DAY = 1440;\nexport const MINUTES_PER_WEEK = 10_080;\n\nexport const HOURS_PER_DAY = 24;\nexport const HOURS_PER_WEEK = 168;\n","/**\n * Bunch of miscellaneous constants and utility functions related to handling\n * date and time durations.\n *\n * Note that month and year do not have fixed durations, and hence are excluded\n * from this file. Weeks have fixed durations, but are excluded because we\n * use days as the max duration supported.\n */\n\nimport {\n MS_PER_SECOND,\n SECONDS_PER_MINUTE,\n MINUTES_PER_HOUR,\n HOURS_PER_DAY,\n MS_PER_DAY,\n MS_PER_MINUTE,\n MS_PER_HOUR,\n} from \"./timeConstants\";\n\nexport type Duration = {\n days?: number;\n hours?: number;\n minutes?: number;\n seconds?: number;\n milliseconds?: number;\n};\n\n/**\n * One of: days, hours, minutes, seconds, milliseconds\n */\nexport type DurationType = keyof Duration;\n\n/**\n * Order in which the duration type appears in the duration string.\n */\nexport const DURATION_TYPE_SEQUENCE: DurationType[] = [\n \"days\",\n \"hours\",\n \"minutes\",\n \"seconds\",\n \"milliseconds\",\n];\n\n/**\n * Follows the same format as Intl.DurationFormat.prototype.format().\n *\n * Short: 1 yr, 2 mths, 3 wks, 3 days, 4 hr, 5 min, 6 sec, 7 ms, 8 μs, 9 ns\n * Long: 1 year, 2 months, 3 weeks, 3 days, 4 hours, 5 minutes, 6 seconds,\n * 7 milliseconds, 8 microseconds, 9 nanoseconds\n * Narrow: 1y 2mo 3w 3d 4h 5m 6s 7ms 8μs 9ns\n */\nexport type DurationStyle = \"short\" | \"long\" | \"narrow\";\n\nexport type DurationSuffixMap = {\n short: string;\n shorts: string;\n long: string;\n longs: string;\n narrow: string;\n};\n\nexport type DurationSuffixType = keyof DurationSuffixMap;\n\nexport const DURATION_STYLE_SUFFIX_MAP: Record<\n DurationType,\n DurationSuffixMap\n> = {\n days: {\n short: \"day\",\n shorts: \"days\",\n long: \"day\",\n longs: \"days\",\n narrow: \"d\",\n },\n hours: {\n short: \"hr\",\n shorts: \"hrs\",\n long: \"hour\",\n longs: \"hours\",\n narrow: \"h\",\n },\n minutes: {\n short: \"min\",\n shorts: \"mins\",\n long: \"minute\",\n longs: \"minutes\",\n narrow: \"m\",\n },\n seconds: {\n short: \"sec\",\n shorts: \"secs\",\n long: \"second\",\n longs: \"seconds\",\n narrow: \"s\",\n },\n milliseconds: {\n short: \"ms\",\n shorts: \"ms\",\n long: \"millisecond\",\n longs: \"milliseconds\",\n narrow: \"ms\",\n },\n};\n\nfunction getDurationStyleForPlural(style: DurationStyle): DurationSuffixType {\n return style == \"short\" ? \"shorts\" : style === \"long\" ? \"longs\" : style;\n}\n\nfunction getValueAndUnitSeparator(style: DurationStyle): string {\n return style === \"narrow\" ? \"\" : \" \";\n}\n\nfunction getDurationTypeSeparator(style: DurationStyle): string {\n return style === \"narrow\" ? \" \" : \", \";\n}\n\n/**\n * Convert a milliseconds duration into a Duration object. If the given ms is\n * zero, then return an object with a single field of zero with duration type\n * of durationTypeForZero.\n *\n * @param durationTypeForZero Defaults to 'milliseconds'\n */\nexport function msToDuration(\n ms: number,\n durationTypeForZero?: DurationType,\n): Duration {\n if (ms === 0) {\n durationTypeForZero = durationTypeForZero ?? \"milliseconds\";\n return { [durationTypeForZero]: 0 };\n }\n\n const duration: Duration = {};\n\n for (let i = 0; i < 1; i++) {\n let seconds = Math.floor(ms / MS_PER_SECOND);\n const millis = ms - seconds * MS_PER_SECOND;\n\n if (millis > 0) {\n duration[\"milliseconds\"] = millis;\n }\n\n if (seconds === 0) {\n break;\n }\n\n let minutes = Math.floor(seconds / SECONDS_PER_MINUTE);\n seconds -= minutes * SECONDS_PER_MINUTE;\n\n if (seconds > 0) {\n duration[\"seconds\"] = seconds;\n }\n\n if (minutes === 0) {\n break;\n }\n\n let hours = Math.floor(minutes / MINUTES_PER_HOUR);\n minutes -= hours * MINUTES_PER_HOUR;\n\n if (minutes > 0) {\n duration[\"minutes\"] = minutes;\n }\n\n if (hours === 0) {\n break;\n }\n\n const days = Math.floor(hours / HOURS_PER_DAY);\n hours -= days * HOURS_PER_DAY;\n\n if (hours > 0) {\n duration[\"hours\"] = hours;\n }\n\n if (days > 0) {\n duration[\"days\"] = days;\n }\n }\n\n return duration;\n}\n\n/**\n * Returns the number of milliseconds for the given duration.\n */\nexport function durationToMs(duration: Duration): number {\n const daysMs = (duration.days ?? 0) * MS_PER_DAY;\n const hoursMs = (duration.hours ?? 0) * MS_PER_HOUR;\n const minsMs = (duration.minutes ?? 0) * MS_PER_MINUTE;\n const secsMs = (duration.seconds ?? 0) * MS_PER_SECOND;\n const msMs = duration.milliseconds ?? 0;\n\n return daysMs + hoursMs + minsMs + secsMs + msMs;\n}\n\n/**\n * Convenience function to return a duration given an ms or Duration.\n */\nexport function durationOrMsToMs(duration: number | Duration): number {\n return typeof duration === \"number\" ? duration : durationToMs(duration);\n}\n\n/**\n * Format a given Duration object into a string. If the object has no fields,\n * then returns an empty string.\n *\n * @param style Defaults to 'short'\n */\nexport function formatDuration(duration: Duration, style?: DurationStyle) {\n style = style ?? \"short\";\n const stylePlural = getDurationStyleForPlural(style);\n\n const space = getValueAndUnitSeparator(style);\n\n const a: string[] = [];\n\n for (const unit of DURATION_TYPE_SEQUENCE) {\n const value = duration[unit];\n if (value === undefined) continue;\n\n const suffixMap = DURATION_STYLE_SUFFIX_MAP[unit];\n const suffix = value === 1 ? suffixMap[style] : suffixMap[stylePlural];\n a.push(value + space + suffix);\n }\n\n const separator = getDurationTypeSeparator(style);\n return a.join(separator);\n}\n\n/**\n * Convert a millisecond duration into a human-readable duration string.\n *\n * @param options.durationTypeForZero - Defaults to 'milliseconds'\n * @param options.style - Defaults to 'short'\n */\nexport function readableDuration(\n ms: number,\n options?: { durationTypeForZero?: DurationType; style?: DurationStyle },\n): string {\n const duration = msToDuration(ms, options?.durationTypeForZero);\n\n return formatDuration(duration, options?.style);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKO,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AACtB,IAAM,cAAc;AACpB,IAAM,aAAa;;;ACkLnB,SAAS,aAAa,UAA4B;AA1LzD;AA2LE,QAAM,WAAU,cAAS,SAAT,YAAiB,KAAK;AACtC,QAAM,YAAW,cAAS,UAAT,YAAkB,KAAK;AACxC,QAAM,WAAU,cAAS,YAAT,YAAoB,KAAK;AACzC,QAAM,WAAU,cAAS,YAAT,YAAoB,KAAK;AACzC,QAAM,QAAO,cAAS,iBAAT,YAAyB;AAEtC,SAAO,SAAS,UAAU,SAAS,SAAS;AAC9C;AAKO,SAAS,iBAAiB,UAAqC;AACpE,SAAO,OAAO,aAAa,WAAW,WAAW,aAAa,QAAQ;AACxE;;;AFlLO,SAAS,UACd,KACA,SACA,WAAW,MACX,cACA;AACA,QAAM,gBAAgB,iBAAiB,OAAO;AAE9C,SAAO,IAAI,UAAa,KAAK,eAAe,UAAU,YAAY;AACpE;AAEA,IAAM,YAAN,MAAmB;AAAA;AAAA;AAAA;AAAA,EAIV,YACW,KACA,eACA,UACA,cAChB;AAJgB;AACA;AACA;AACA;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKI,IACL,OACA,cAAiC,KAAK,eAChC;AACN,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,UAAyB;AAAA,MAC7B;AAAA,MACA,UAAU;AAAA,MACV,UAAU,QAAQ,iBAAiB,WAAW;AAAA,IAChD;AACA,UAAM,WAAW,KAAK,UAAU,OAAO;AAEvC,eAAW,aAAa,QAAQ,KAAK,KAAK,QAAQ;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,gBAA2C;AAChD,UAAM,UAAU,WAAW,aAAa,QAAQ,KAAK,GAAG;AAExD,QAAI,CAAC,SAAS;AACZ,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,MAAiC,KAAK,MAAM,OAAO;AAEzD,UACE,CAAC,OACD,OAAO,QAAQ,YACf,EAAE,WAAW,QACb,EAAE,cAAc,QAChB,OAAO,IAAI,aAAa,YACxB,EAAE,cAAc,QAChB,OAAO,IAAI,aAAa,YACxB,KAAK,IAAI,KAAK,IAAI,UAClB;AACA,aAAK,OAAO;AACZ,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT,SAAS,GAAG;AACV,UAAI,KAAK,UAAU;AACjB,gBAAQ;AAAA,UACN,gCAAgC,KAAK,GAAG,IAAI,OAAO;AAAA,UACnD;AAAA,QACF;AAAA,MACF;AACA,WAAK,OAAO;AACZ,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,MAAqB;AAC1B,UAAM,SAAS,KAAK,cAAc;AAElC,WAAO,WAAW,SAAY,OAAO,QAAQ,KAAK;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA,EAKO,SAAe;AACpB,eAAW,aAAa,WAAW,KAAK,GAAG;AAAA,EAC7C;AACF;","names":[]}
|
package/nonEmpty.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type asserts that `t` is truthy.
|
|
3
3
|
* Throws an error if `t` is null or undefined.
|
|
4
|
+
*
|
|
5
|
+
* @param varName The variable name to include in the error to throw when t is
|
|
6
|
+
* empty. Defaults to 'value'.
|
|
4
7
|
*/
|
|
5
8
|
export declare function nonEmpty<T>(t: T | null | undefined | "" | 0 | -0 | 0n | false | typeof NaN, varName?: string): T;
|
package/nonEmpty.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nonEmpty.ts","../src/isEmpty.ts"],"sourcesContent":["import { isEmpty } from \"./isEmpty\";\n\n/**\n * Type asserts that `t` is truthy.\n * Throws an error if `t` is null or undefined.\n */\nexport function nonEmpty<T>(\n t: T | null | undefined | \"\" | 0 | -0 | 0n | false | typeof NaN,\n varName = \"value\",\n): T {\n if (isEmpty(t)) {\n throw new Error(`Empty ${varName}: ${t}`);\n }\n return t as T;\n}\n","/**\n * Returns true if `t` is empty.\n */\nexport function isEmpty(t: unknown): boolean {\n // Anything falsy is considered empty.\n if (!t) {\n return true;\n }\n\n // Arrays are also of type `object`.\n if (typeof t !== \"object\") {\n return false;\n }\n\n // `length` includes arrays as well.\n if (\"length\" in t) {\n return t.length === 0;\n }\n\n // `size` is for Set, Map, Blob etc.\n if (\"size\" in t) {\n return t.size === 0;\n }\n\n // Super fast check for object emptiness.\n // https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object\n for (const k in t) {\n return false;\n }\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,SAAS,QAAQ,GAAqB;AAE3C,MAAI,CAAC,GAAG;AACN,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,MAAM,UAAU;AACzB,WAAO;AAAA,EACT;AAGA,MAAI,YAAY,GAAG;AACjB,WAAO,EAAE,WAAW;AAAA,EACtB;AAGA,MAAI,UAAU,GAAG;AACf,WAAO,EAAE,SAAS;AAAA,EACpB;AAIA,aAAW,KAAK,GAAG;AACjB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;;
|
|
1
|
+
{"version":3,"sources":["../src/nonEmpty.ts","../src/isEmpty.ts"],"sourcesContent":["import { isEmpty } from \"./isEmpty\";\n\n/**\n * Type asserts that `t` is truthy.\n * Throws an error if `t` is null or undefined.\n *\n * @param varName The variable name to include in the error to throw when t is\n * empty. Defaults to 'value'.\n */\nexport function nonEmpty<T>(\n t: T | null | undefined | \"\" | 0 | -0 | 0n | false | typeof NaN,\n varName = \"value\",\n): T {\n if (isEmpty(t)) {\n throw new Error(`Empty ${varName}: ${t}`);\n }\n return t as T;\n}\n","/**\n * Returns true if `t` is empty.\n */\nexport function isEmpty(t: unknown): boolean {\n // Anything falsy is considered empty.\n if (!t) {\n return true;\n }\n\n // Arrays are also of type `object`.\n if (typeof t !== \"object\") {\n return false;\n }\n\n // `length` includes arrays as well.\n if (\"length\" in t) {\n return t.length === 0;\n }\n\n // `size` is for Set, Map, Blob etc.\n if (\"size\" in t) {\n return t.size === 0;\n }\n\n // Super fast check for object emptiness.\n // https://stackoverflow.com/questions/679915/how-do-i-test-for-an-empty-javascript-object\n for (const k in t) {\n return false;\n }\n\n return true;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACGO,SAAS,QAAQ,GAAqB;AAE3C,MAAI,CAAC,GAAG;AACN,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,MAAM,UAAU;AACzB,WAAO;AAAA,EACT;AAGA,MAAI,YAAY,GAAG;AACjB,WAAO,EAAE,WAAW;AAAA,EACtB;AAGA,MAAI,UAAU,GAAG;AACf,WAAO,EAAE,SAAS;AAAA,EACpB;AAIA,aAAW,KAAK,GAAG;AACjB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;;;ADtBO,SAAS,SACd,GACA,UAAU,SACP;AACH,MAAI,QAAQ,CAAC,GAAG;AACd,UAAM,IAAI,MAAM,SAAS,OAAO,KAAK,CAAC,EAAE;AAAA,EAC1C;AACA,SAAO;AACT;","names":[]}
|
package/nonNil.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type asserts that `t` is neither null nor undefined.
|
|
3
3
|
* Throws an error if `t` is null or undefined.
|
|
4
|
+
*
|
|
5
|
+
* @param varName The variable name to include in the error to throw when t is
|
|
6
|
+
* nil. Defaults to 'value'.
|
|
4
7
|
*/
|
|
5
8
|
export declare function nonNil<T>(t: T | null | undefined, varName?: string): T;
|
package/nonNil.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nonNil.ts"],"sourcesContent":["/**\n * Type asserts that `t` is neither null nor undefined.\n * Throws an error if `t` is null or undefined.\n */\nexport function nonNil<T>(t: T | null | undefined, varName = \"value\"): T {\n if (t === null || t === undefined) {\n throw new Error(`Missing ${varName}: ${t}`);\n }\n return t;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../src/nonNil.ts"],"sourcesContent":["/**\n * Type asserts that `t` is neither null nor undefined.\n * Throws an error if `t` is null or undefined.\n *\n * @param varName The variable name to include in the error to throw when t is\n * nil. Defaults to 'value'.\n */\nexport function nonNil<T>(t: T | null | undefined, varName = \"value\"): T {\n if (t === null || t === undefined) {\n throw new Error(`Missing ${varName}: ${t}`);\n }\n return t;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,SAAS,OAAU,GAAyB,UAAU,SAAY;AACvE,MAAI,MAAM,QAAQ,MAAM,QAAW;AACjC,UAAM,IAAI,MAAM,WAAW,OAAO,KAAK,CAAC,EAAE;AAAA,EAC5C;AACA,SAAO;AACT;","names":[]}
|
package/package.json
CHANGED
package/safeParseInt.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Returns 0 if the string is not a valid number.
|
|
3
|
+
*
|
|
4
|
+
* @param logError Log a console error if the given string is not a valid
|
|
5
|
+
* number. Defaults to false (don't log anything).
|
|
3
6
|
*/
|
|
4
7
|
export declare function safeParseInt(s: string, logError?: boolean): number;
|
package/safeParseInt.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/safeParseInt.ts"],"sourcesContent":["/**\n * Returns 0 if the string is not a valid number.\n */\nexport function safeParseInt(s: string, logError = false): number {\n const i = Number(s);\n\n if (isNaN(i)) {\n if (logError) {\n console.error(`Not a number: \"${s}\"`);\n }\n return 0;\n }\n\n return Math.floor(i);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../src/safeParseInt.ts"],"sourcesContent":["/**\n * Returns 0 if the string is not a valid number.\n *\n * @param logError Log a console error if the given string is not a valid\n * number. Defaults to false (don't log anything).\n */\nexport function safeParseInt(s: string, logError = false): number {\n const i = Number(s);\n\n if (isNaN(i)) {\n if (logError) {\n console.error(`Not a number: \"${s}\"`);\n }\n return 0;\n }\n\n return Math.floor(i);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMO,SAAS,aAAa,GAAW,WAAW,OAAe;AAChE,QAAM,IAAI,OAAO,CAAC;AAElB,MAAI,MAAM,CAAC,GAAG;AACZ,QAAI,UAAU;AACZ,cAAQ,MAAM,kBAAkB,CAAC,GAAG;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAEA,SAAO,KAAK,MAAM,CAAC;AACrB;","names":[]}
|
package/sleep.d.ts
CHANGED
package/sleep.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sleep.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../src/sleep.ts"],"sourcesContent":["/**\n * Sleep for a given number of milliseconds. Note that this method is async,\n * so please remember to call it with await, like `await sleep(1000);`.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;","names":[]}
|
package/src/dateTimeStr.ts
CHANGED
|
@@ -1,31 +1,74 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type AnyDateTime = number | Date | string;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert a number (epoch milliseconds), string (parseable date/time), or
|
|
5
|
+
* Date object (no conversion) into a Date object.
|
|
6
|
+
*/
|
|
7
|
+
export function toDate(ts: AnyDateTime): Date {
|
|
8
|
+
if (typeof ts === "number" || typeof ts === "string") {
|
|
9
|
+
return new Date(ts);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return ts;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns a date in yyyy-MM format. E.g. '2000-01'.
|
|
17
|
+
*
|
|
18
|
+
* @param dt Specify a date object or default to the current date.
|
|
19
|
+
* @param separator Defaults to '-'.
|
|
20
|
+
*/
|
|
21
|
+
export function yyyyMm(dt = new Date(), separator = "-"): string {
|
|
2
22
|
const yr = dt.getFullYear();
|
|
3
23
|
const mth = dt.getMonth() + 1;
|
|
24
|
+
|
|
25
|
+
return yr + separator + (mth < 10 ? "0" + mth : mth);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns a date in yyyy-MM-dd format. E.g. '2000-01-02'.
|
|
30
|
+
*
|
|
31
|
+
* @param dt Specify a date object or default to the current date.
|
|
32
|
+
* @param separator Defaults to '-'.
|
|
33
|
+
*/
|
|
34
|
+
export function yyyyMmDd(dt = new Date(), separator = "-"): string {
|
|
4
35
|
const day = dt.getDate();
|
|
5
36
|
|
|
6
|
-
return (
|
|
7
|
-
yr +
|
|
8
|
-
separator +
|
|
9
|
-
(mth < 10 ? "0" + mth : mth) +
|
|
10
|
-
separator +
|
|
11
|
-
(day < 10 ? "0" + day : day)
|
|
12
|
-
);
|
|
37
|
+
return yyyyMm(dt, separator) + separator + (day < 10 ? "0" + day : day);
|
|
13
38
|
}
|
|
14
39
|
|
|
15
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Returns a date in hh:mm format. E.g. '01:02'.
|
|
42
|
+
*
|
|
43
|
+
* @param dt Specify a date object or default to the current date/time.
|
|
44
|
+
* @param separator Defaults to ':'.
|
|
45
|
+
*/
|
|
46
|
+
export function hhMm(dt = new Date(), separator = ":"): string {
|
|
16
47
|
const hr = dt.getHours();
|
|
17
48
|
const min = dt.getMinutes();
|
|
49
|
+
|
|
50
|
+
return (hr < 10 ? "0" + hr : hr) + separator + (min < 10 ? "0" + min : min);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Returns a date in hh:mm:ss format. E.g. '01:02:03'.
|
|
55
|
+
*
|
|
56
|
+
* @param dt Specify a date object or default to the current date/time.
|
|
57
|
+
* @param separator Defaults to ':'.
|
|
58
|
+
*/
|
|
59
|
+
export function hhMmSs(dt = new Date(), separator = ":"): string {
|
|
18
60
|
const sec = dt.getSeconds();
|
|
19
61
|
|
|
20
|
-
return (
|
|
21
|
-
(hr < 10 ? "0" + hr : hr) +
|
|
22
|
-
separator +
|
|
23
|
-
(min < 10 ? "0" + min : min) +
|
|
24
|
-
separator +
|
|
25
|
-
(sec < 10 ? "0" + sec : sec)
|
|
26
|
-
);
|
|
62
|
+
return hhMm(dt, separator) + separator + (sec < 10 ? "0" + sec : sec);
|
|
27
63
|
}
|
|
28
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Returns a date in hh:mm:ss.SSS format. E.g. '01:02:03.004'.
|
|
67
|
+
*
|
|
68
|
+
* @param dt Specify a date object or default to the current date/time.
|
|
69
|
+
* @param timeSeparator Separator for hh/mm/ss. Defaults to ':'.
|
|
70
|
+
* @param msSeparator Separator before SSS. Defaults to '.'.
|
|
71
|
+
*/
|
|
29
72
|
export function hhMmSsMs(
|
|
30
73
|
dt = new Date(),
|
|
31
74
|
timeSeparator = ":",
|
|
@@ -40,6 +83,12 @@ export function hhMmSsMs(
|
|
|
40
83
|
);
|
|
41
84
|
}
|
|
42
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Returns the timezone string for the given date. E.g. '+8', '-3.5'.
|
|
88
|
+
* Returns 'Z' for UTC.
|
|
89
|
+
*
|
|
90
|
+
* @param dt Specify a date object or default to the current date/time.
|
|
91
|
+
*/
|
|
43
92
|
export function tzShort(dt = new Date()): string {
|
|
44
93
|
if (dt.getTimezoneOffset() === 0) {
|
|
45
94
|
return "Z";
|
|
@@ -49,6 +98,12 @@ export function tzShort(dt = new Date()): string {
|
|
|
49
98
|
return tzHours >= 0 ? "+" + tzHours : String(tzHours);
|
|
50
99
|
}
|
|
51
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Returns the long month name, zero-indexed. E.g. 0 for 'January'.
|
|
103
|
+
*
|
|
104
|
+
* @param month Zero-indexed month.
|
|
105
|
+
* @param locales Specify the locale, e.g. 'en-US', new Intl.Locale("en-US").
|
|
106
|
+
*/
|
|
52
107
|
export function getLongMonthNameZeroIndexed(
|
|
53
108
|
month: number,
|
|
54
109
|
locales: Intl.LocalesArgument = "default",
|
|
@@ -58,6 +113,12 @@ export function getLongMonthNameZeroIndexed(
|
|
|
58
113
|
});
|
|
59
114
|
}
|
|
60
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Returns the long month name, one-indexed. E.g. 1 for 'January'.
|
|
118
|
+
*
|
|
119
|
+
* @param month One-indexed month.
|
|
120
|
+
* @param locales Specify the locale, e.g. 'en-US', new Intl.Locale("en-US").
|
|
121
|
+
*/
|
|
61
122
|
export function getLongMonthNameOneIndexed(
|
|
62
123
|
month: number,
|
|
63
124
|
locales: Intl.LocalesArgument = "default",
|
|
@@ -65,6 +126,12 @@ export function getLongMonthNameOneIndexed(
|
|
|
65
126
|
return getLongMonthNameZeroIndexed(month - 1, locales);
|
|
66
127
|
}
|
|
67
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Returns the short month name, zero-indexed. E.g. 0 for 'Jan'.
|
|
131
|
+
*
|
|
132
|
+
* @param month Zero-indexed month.
|
|
133
|
+
* @param locales Specify the locale, e.g. 'en-US', new Intl.Locale("en-US").
|
|
134
|
+
*/
|
|
68
135
|
export function getShortMonthNameZeroIndexed(
|
|
69
136
|
month: number,
|
|
70
137
|
locales: Intl.LocalesArgument = "default",
|
|
@@ -74,9 +141,25 @@ export function getShortMonthNameZeroIndexed(
|
|
|
74
141
|
});
|
|
75
142
|
}
|
|
76
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Returns the short month name, one-indexed. E.g. 1 for 'Jan'.
|
|
146
|
+
*
|
|
147
|
+
* @param month One-indexed month.
|
|
148
|
+
* @param locales Specify the locale, e.g. 'en-US', new Intl.Locale("en-US").
|
|
149
|
+
*/
|
|
77
150
|
export function getShortMonthNameOneIndexed(
|
|
78
151
|
month: number,
|
|
79
152
|
locales: Intl.LocalesArgument = "default",
|
|
80
153
|
): string {
|
|
81
154
|
return getShortMonthNameZeroIndexed(month - 1, locales);
|
|
82
155
|
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Returns a human-readable string date/time like '2025-01-01 22:31:16Z'.
|
|
159
|
+
* Excludes the milliseconds assuming it is not necessary for display.
|
|
160
|
+
*/
|
|
161
|
+
export function getDisplayDateTime(ts: AnyDateTime) {
|
|
162
|
+
const iso = toDate(ts).toISOString();
|
|
163
|
+
const noMs = iso.slice(0, 19) + "Z";
|
|
164
|
+
return noMs.replace("T", " ");
|
|
165
|
+
}
|
package/src/duration.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* date and time durations.
|
|
4
4
|
*
|
|
5
5
|
* Note that month and year do not have fixed durations, and hence are excluded
|
|
6
|
-
* from this file.
|
|
6
|
+
* from this file. Weeks have fixed durations, but are excluded because we
|
|
7
|
+
* use days as the max duration supported.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import {
|
|
@@ -24,8 +25,14 @@ export type Duration = {
|
|
|
24
25
|
milliseconds?: number;
|
|
25
26
|
};
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* One of: days, hours, minutes, seconds, milliseconds
|
|
30
|
+
*/
|
|
27
31
|
export type DurationType = keyof Duration;
|
|
28
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Order in which the duration type appears in the duration string.
|
|
35
|
+
*/
|
|
29
36
|
export const DURATION_TYPE_SEQUENCE: DurationType[] = [
|
|
30
37
|
"days",
|
|
31
38
|
"hours",
|
|
@@ -109,9 +116,10 @@ function getDurationTypeSeparator(style: DurationStyle): string {
|
|
|
109
116
|
|
|
110
117
|
/**
|
|
111
118
|
* Convert a milliseconds duration into a Duration object. If the given ms is
|
|
112
|
-
* zero, then return an object with a single field of zero
|
|
119
|
+
* zero, then return an object with a single field of zero with duration type
|
|
120
|
+
* of durationTypeForZero.
|
|
113
121
|
*
|
|
114
|
-
* durationTypeForZero
|
|
122
|
+
* @param durationTypeForZero Defaults to 'milliseconds'
|
|
115
123
|
*/
|
|
116
124
|
export function msToDuration(
|
|
117
125
|
ms: number,
|
|
@@ -186,11 +194,18 @@ export function durationToMs(duration: Duration): number {
|
|
|
186
194
|
return daysMs + hoursMs + minsMs + secsMs + msMs;
|
|
187
195
|
}
|
|
188
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Convenience function to return a duration given an ms or Duration.
|
|
199
|
+
*/
|
|
200
|
+
export function durationOrMsToMs(duration: number | Duration): number {
|
|
201
|
+
return typeof duration === "number" ? duration : durationToMs(duration);
|
|
202
|
+
}
|
|
203
|
+
|
|
189
204
|
/**
|
|
190
205
|
* Format a given Duration object into a string. If the object has no fields,
|
|
191
206
|
* then returns an empty string.
|
|
192
207
|
*
|
|
193
|
-
* style
|
|
208
|
+
* @param style Defaults to 'short'
|
|
194
209
|
*/
|
|
195
210
|
export function formatDuration(duration: Duration, style?: DurationStyle) {
|
|
196
211
|
style = style ?? "short";
|
|
@@ -216,8 +231,8 @@ export function formatDuration(duration: Duration, style?: DurationStyle) {
|
|
|
216
231
|
/**
|
|
217
232
|
* Convert a millisecond duration into a human-readable duration string.
|
|
218
233
|
*
|
|
219
|
-
* options.durationTypeForZero - Defaults to 'milliseconds'
|
|
220
|
-
* options.style - Defaults to 'short'
|
|
234
|
+
* @param options.durationTypeForZero - Defaults to 'milliseconds'
|
|
235
|
+
* @param options.style - Defaults to 'short'
|
|
221
236
|
*/
|
|
222
237
|
export function readableDuration(
|
|
223
238
|
ms: number,
|