@itshackers/utilix 0.0.0-stage → 0.3.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 +21 -0
- package/README.md +304 -3
- package/dist/common/index.cjs +102 -0
- package/dist/common/index.cjs.map +1 -0
- package/dist/common/index.d.cts +58 -0
- package/dist/common/index.d.ts +58 -0
- package/dist/common/index.js +93 -0
- package/dist/common/index.js.map +1 -0
- package/dist/date/index.cjs +154 -0
- package/dist/date/index.cjs.map +1 -0
- package/dist/date/index.d.cts +65 -0
- package/dist/date/index.d.ts +65 -0
- package/dist/date/index.js +146 -0
- package/dist/date/index.js.map +1 -0
- package/dist/format/index.cjs +136 -0
- package/dist/format/index.cjs.map +1 -0
- package/dist/format/index.d.cts +56 -0
- package/dist/format/index.d.ts +56 -0
- package/dist/format/index.js +130 -0
- package/dist/format/index.js.map +1 -0
- package/dist/id/index.cjs +66 -0
- package/dist/id/index.cjs.map +1 -0
- package/dist/id/index.d.cts +19 -0
- package/dist/id/index.d.ts +19 -0
- package/dist/id/index.js +64 -0
- package/dist/id/index.js.map +1 -0
- package/dist/index.cjs +906 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +853 -0
- package/dist/index.js.map +1 -0
- package/dist/number/index.cjs +107 -0
- package/dist/number/index.cjs.map +1 -0
- package/dist/number/index.d.cts +90 -0
- package/dist/number/index.d.ts +90 -0
- package/dist/number/index.js +99 -0
- package/dist/number/index.js.map +1 -0
- package/dist/password/index.cjs +71 -0
- package/dist/password/index.cjs.map +1 -0
- package/dist/password/index.d.cts +15 -0
- package/dist/password/index.d.ts +15 -0
- package/dist/password/index.js +69 -0
- package/dist/password/index.js.map +1 -0
- package/dist/phone/index.cjs +19 -0
- package/dist/phone/index.cjs.map +1 -0
- package/dist/phone/index.d.cts +12 -0
- package/dist/phone/index.d.ts +12 -0
- package/dist/phone/index.js +17 -0
- package/dist/phone/index.js.map +1 -0
- package/dist/regex/index.cjs +73 -0
- package/dist/regex/index.cjs.map +1 -0
- package/dist/regex/index.d.cts +65 -0
- package/dist/regex/index.d.ts +65 -0
- package/dist/regex/index.js +67 -0
- package/dist/regex/index.js.map +1 -0
- package/dist/response/index.cjs +43 -0
- package/dist/response/index.cjs.map +1 -0
- package/dist/response/index.d.cts +41 -0
- package/dist/response/index.d.ts +41 -0
- package/dist/response/index.js +40 -0
- package/dist/response/index.js.map +1 -0
- package/dist/sanitize/index.cjs +83 -0
- package/dist/sanitize/index.cjs.map +1 -0
- package/dist/sanitize/index.d.cts +20 -0
- package/dist/sanitize/index.d.ts +20 -0
- package/dist/sanitize/index.js +80 -0
- package/dist/sanitize/index.js.map +1 -0
- package/dist/slug/index.cjs +33 -0
- package/dist/slug/index.cjs.map +1 -0
- package/dist/slug/index.d.cts +38 -0
- package/dist/slug/index.d.ts +38 -0
- package/dist/slug/index.js +31 -0
- package/dist/slug/index.js.map +1 -0
- package/dist/string/index.cjs +130 -0
- package/dist/string/index.cjs.map +1 -0
- package/dist/string/index.d.cts +112 -0
- package/dist/string/index.d.ts +112 -0
- package/dist/string/index.js +115 -0
- package/dist/string/index.js.map +1 -0
- package/package.json +187 -3
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/date/index.ts
|
|
4
|
+
var IST_OFFSET_MS = 330 * 60 * 1e3;
|
|
5
|
+
var IST_DATETIME_RE = /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})(?::(\d{2}))?/;
|
|
6
|
+
function pad(value, length = 2) {
|
|
7
|
+
return String(value).padStart(length, "0");
|
|
8
|
+
}
|
|
9
|
+
function getIstDateParts(date = /* @__PURE__ */ new Date()) {
|
|
10
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
11
|
+
timeZone: "Asia/Kolkata",
|
|
12
|
+
year: "numeric",
|
|
13
|
+
month: "2-digit",
|
|
14
|
+
day: "2-digit",
|
|
15
|
+
hour: "2-digit",
|
|
16
|
+
hourCycle: "h23"
|
|
17
|
+
}).formatToParts(date);
|
|
18
|
+
const read = (type) => {
|
|
19
|
+
const value = parts.find((part) => part.type === type)?.value;
|
|
20
|
+
return Number(value);
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
year: read("year"),
|
|
24
|
+
month: read("month"),
|
|
25
|
+
day: read("day"),
|
|
26
|
+
hour: read("hour")
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function addCalendarDays(year, month, day, days) {
|
|
30
|
+
const utc = new Date(Date.UTC(year, month - 1, day + days));
|
|
31
|
+
return {
|
|
32
|
+
year: utc.getUTCFullYear(),
|
|
33
|
+
month: utc.getUTCMonth() + 1,
|
|
34
|
+
day: utc.getUTCDate()
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function istDateTime(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0) {
|
|
38
|
+
return /* @__PURE__ */ new Date(
|
|
39
|
+
`${year}-${pad(month)}-${pad(day)}T${pad(hour)}:${pad(minute)}:${pad(second)}.${pad(millisecond, 3)}+05:30`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
function parseIstDateTimeToUtc(value) {
|
|
43
|
+
if (value == null || value === "") {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
if (value instanceof Date) {
|
|
47
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
48
|
+
}
|
|
49
|
+
if (typeof value === "number") {
|
|
50
|
+
const parsed = new Date(value);
|
|
51
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
52
|
+
}
|
|
53
|
+
const raw = value.trim();
|
|
54
|
+
if (raw.length === 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (/[zZ]$/.test(raw) || /[+-]\d{2}:?\d{2}$/.test(raw)) {
|
|
58
|
+
const parsed = new Date(raw);
|
|
59
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
60
|
+
}
|
|
61
|
+
const match = raw.match(IST_DATETIME_RE);
|
|
62
|
+
if (!match) {
|
|
63
|
+
const parsed = new Date(raw);
|
|
64
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
65
|
+
}
|
|
66
|
+
const [, year = "0", month = "1", day = "1", hour = "0", minute = "0", second = "0"] = match;
|
|
67
|
+
const istAsUtcMs = Date.UTC(
|
|
68
|
+
Number(year),
|
|
69
|
+
Number(month) - 1,
|
|
70
|
+
Number(day),
|
|
71
|
+
Number(hour),
|
|
72
|
+
Number(minute),
|
|
73
|
+
Number(second)
|
|
74
|
+
);
|
|
75
|
+
return new Date(istAsUtcMs - IST_OFFSET_MS);
|
|
76
|
+
}
|
|
77
|
+
function formatUtcToIstDateTime(value) {
|
|
78
|
+
if (value == null || value === "") {
|
|
79
|
+
return "";
|
|
80
|
+
}
|
|
81
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
82
|
+
if (Number.isNaN(date.getTime())) {
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
const ist = new Date(date.getTime() + IST_OFFSET_MS);
|
|
86
|
+
return `${ist.getUTCFullYear()}-${pad(ist.getUTCMonth() + 1)}-${pad(ist.getUTCDate())} ${pad(ist.getUTCHours())}:${pad(ist.getUTCMinutes())}:${pad(ist.getUTCSeconds())}`;
|
|
87
|
+
}
|
|
88
|
+
function getExpiryInfo(expiresAt, options = {}) {
|
|
89
|
+
if (!(expiresAt instanceof Date) || Number.isNaN(expiresAt.getTime())) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const warningDays = options.warningDays ?? 7;
|
|
93
|
+
const compact = options.compact ?? false;
|
|
94
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
95
|
+
let remainingSeconds = Math.floor((expiresAt.getTime() - now.getTime()) / 1e3);
|
|
96
|
+
if (remainingSeconds < 0) {
|
|
97
|
+
remainingSeconds = 0;
|
|
98
|
+
}
|
|
99
|
+
const units = [
|
|
100
|
+
{ label: "day", short: "d", value: 86400 },
|
|
101
|
+
{ label: "hour", short: "h", value: 3600 },
|
|
102
|
+
{ label: "minute", short: "m", value: 60 },
|
|
103
|
+
{ label: "second", short: "s", value: 1 }
|
|
104
|
+
];
|
|
105
|
+
const parts = [];
|
|
106
|
+
let remaining = remainingSeconds;
|
|
107
|
+
for (const unit of units) {
|
|
108
|
+
const count = Math.floor(remaining / unit.value);
|
|
109
|
+
if (count > 0) {
|
|
110
|
+
parts.push(compact ? `${count}${unit.short}` : `${count} ${unit.label}${count > 1 ? "s" : ""}`);
|
|
111
|
+
remaining -= count * unit.value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const daysLeft = Math.ceil(remainingSeconds / 86400);
|
|
115
|
+
return {
|
|
116
|
+
expiresAt,
|
|
117
|
+
remainingSeconds,
|
|
118
|
+
remainingHuman: parts.join(" ") || "Expired",
|
|
119
|
+
daysLeft,
|
|
120
|
+
isExpired: remainingSeconds === 0,
|
|
121
|
+
needsRefresh: daysLeft <= warningDays
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function getCalendarWindow(options = {}) {
|
|
125
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
126
|
+
const cutoffHour = options.cutoffHour ?? 14;
|
|
127
|
+
const windowDays = options.windowDays ?? 36;
|
|
128
|
+
const startOffsetBeforeCutoff = options.startOffsetBeforeCutoff ?? 1;
|
|
129
|
+
const startOffsetAfterCutoff = options.startOffsetAfterCutoff ?? 2;
|
|
130
|
+
if (!Number.isInteger(cutoffHour) || cutoffHour < 0 || cutoffHour > 23) {
|
|
131
|
+
throw new RangeError("cutoffHour must be an integer between 0 and 23");
|
|
132
|
+
}
|
|
133
|
+
if (!Number.isInteger(windowDays) || windowDays < 0) {
|
|
134
|
+
throw new RangeError("windowDays must be an integer greater than or equal to 0");
|
|
135
|
+
}
|
|
136
|
+
const { year, month, day, hour } = getIstDateParts(now);
|
|
137
|
+
const startOffsetDays = hour < cutoffHour ? startOffsetBeforeCutoff : startOffsetAfterCutoff;
|
|
138
|
+
const minYmd = addCalendarDays(year, month, day, startOffsetDays);
|
|
139
|
+
const maxYmd = addCalendarDays(minYmd.year, minYmd.month, minYmd.day, windowDays);
|
|
140
|
+
return {
|
|
141
|
+
min: istDateTime(minYmd.year, minYmd.month, minYmd.day, 0, 0, 0, 0),
|
|
142
|
+
max: istDateTime(maxYmd.year, maxYmd.month, maxYmd.day, 23, 59, 59, 999)
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
exports.addCalendarDays = addCalendarDays;
|
|
147
|
+
exports.formatUtcToIstDateTime = formatUtcToIstDateTime;
|
|
148
|
+
exports.getCalendarWindow = getCalendarWindow;
|
|
149
|
+
exports.getExpiryInfo = getExpiryInfo;
|
|
150
|
+
exports.getIstDateParts = getIstDateParts;
|
|
151
|
+
exports.istDateTime = istDateTime;
|
|
152
|
+
exports.parseIstDateTimeToUtc = parseIstDateTimeToUtc;
|
|
153
|
+
//# sourceMappingURL=index.cjs.map
|
|
154
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/date/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["const IST_OFFSET_MS = 330 * 60 * 1000;\r\nconst IST_DATETIME_RE =\r\n /^(\\d{4})-(\\d{2})-(\\d{2})[ T](\\d{2}):(\\d{2})(?::(\\d{2}))?/;\r\n\r\nexport interface IstDateParts {\r\n year: number;\r\n month: number;\r\n day: number;\r\n hour: number;\r\n}\r\n\r\nexport interface ExpiryInfo {\r\n expiresAt: Date;\r\n remainingSeconds: number;\r\n remainingHuman: string;\r\n daysLeft: number;\r\n isExpired: boolean;\r\n needsRefresh: boolean;\r\n}\r\n\r\nexport interface ExpiryInfoOptions {\r\n warningDays?: number;\r\n compact?: boolean;\r\n now?: Date;\r\n}\r\n\r\nexport interface CalendarWindowOptions {\r\n now?: Date;\r\n cutoffHour?: number;\r\n windowDays?: number;\r\n startOffsetBeforeCutoff?: number;\r\n startOffsetAfterCutoff?: number;\r\n}\r\n\r\nfunction pad(value: number, length = 2): string {\r\n return String(value).padStart(length, \"0\");\r\n}\r\n\r\n/**\r\n * Reads calendar parts of a date in Asia/Kolkata.\r\n */\r\nexport function getIstDateParts(date: Date = new Date()): IstDateParts {\r\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\r\n timeZone: \"Asia/Kolkata\",\r\n year: \"numeric\",\r\n month: \"2-digit\",\r\n day: \"2-digit\",\r\n hour: \"2-digit\",\r\n hourCycle: \"h23\",\r\n }).formatToParts(date);\r\n\r\n const read = (type: Intl.DateTimeFormatPartTypes): number => {\r\n const value = parts.find((part) => part.type === type)?.value;\r\n return Number(value);\r\n };\r\n\r\n return {\r\n year: read(\"year\"),\r\n month: read(\"month\"),\r\n day: read(\"day\"),\r\n hour: read(\"hour\"),\r\n };\r\n}\r\n\r\n/**\r\n * Adds calendar days to a year/month/day triple without DST surprises.\r\n */\r\nexport function addCalendarDays(\r\n year: number,\r\n month: number,\r\n day: number,\r\n days: number,\r\n): { year: number; month: number; day: number } {\r\n const utc = new Date(Date.UTC(year, month - 1, day + days));\r\n return {\r\n year: utc.getUTCFullYear(),\r\n month: utc.getUTCMonth() + 1,\r\n day: utc.getUTCDate(),\r\n };\r\n}\r\n\r\n/**\r\n * Builds a Date for an Asia/Kolkata wall-clock time.\r\n */\r\nexport function istDateTime(\r\n year: number,\r\n month: number,\r\n day: number,\r\n hour = 0,\r\n minute = 0,\r\n second = 0,\r\n millisecond = 0,\r\n): Date {\r\n return new Date(\r\n `${year}-${pad(month)}-${pad(day)}T${pad(hour)}:${pad(minute)}:${pad(second)}.${pad(millisecond, 3)}+05:30`,\r\n );\r\n}\r\n\r\n/**\r\n * Parses a naive IST datetime as Asia/Kolkata and returns a UTC Date.\r\n * Values with an explicit timezone (`Z` or `±HH:mm`) are treated as absolute instants.\r\n */\r\nexport function parseIstDateTimeToUtc(value: Date | string | number | null | undefined): Date | null {\r\n if (value == null || value === \"\") {\r\n return null;\r\n }\r\n\r\n if (value instanceof Date) {\r\n return Number.isNaN(value.getTime()) ? null : value;\r\n }\r\n\r\n if (typeof value === \"number\") {\r\n const parsed = new Date(value);\r\n return Number.isNaN(parsed.getTime()) ? null : parsed;\r\n }\r\n\r\n const raw = value.trim();\r\n if (raw.length === 0) {\r\n return null;\r\n }\r\n\r\n if (/[zZ]$/.test(raw) || /[+-]\\d{2}:?\\d{2}$/.test(raw)) {\r\n const parsed = new Date(raw);\r\n return Number.isNaN(parsed.getTime()) ? null : parsed;\r\n }\r\n\r\n const match = raw.match(IST_DATETIME_RE);\r\n if (!match) {\r\n const parsed = new Date(raw);\r\n return Number.isNaN(parsed.getTime()) ? null : parsed;\r\n }\r\n\r\n const [, year = \"0\", month = \"1\", day = \"1\", hour = \"0\", minute = \"0\", second = \"0\"] = match;\r\n const istAsUtcMs = Date.UTC(\r\n Number(year),\r\n Number(month) - 1,\r\n Number(day),\r\n Number(hour),\r\n Number(minute),\r\n Number(second),\r\n );\r\n\r\n return new Date(istAsUtcMs - IST_OFFSET_MS);\r\n}\r\n\r\n/**\r\n * Formats a UTC instant as `YYYY-MM-DD HH:mm:ss` in Asia/Kolkata.\r\n */\r\nexport function formatUtcToIstDateTime(value: Date | string | number | null | undefined): string {\r\n if (value == null || value === \"\") {\r\n return \"\";\r\n }\r\n\r\n const date = value instanceof Date ? value : new Date(value);\r\n if (Number.isNaN(date.getTime())) {\r\n return \"\";\r\n }\r\n\r\n const ist = new Date(date.getTime() + IST_OFFSET_MS);\r\n return `${ist.getUTCFullYear()}-${pad(ist.getUTCMonth() + 1)}-${pad(ist.getUTCDate())} ${pad(ist.getUTCHours())}:${pad(ist.getUTCMinutes())}:${pad(ist.getUTCSeconds())}`;\r\n}\r\n\r\n/**\r\n * Returns remaining time until `expiresAt`.\r\n */\r\nexport function getExpiryInfo(expiresAt: Date, options: ExpiryInfoOptions = {}): ExpiryInfo | null {\r\n if (!(expiresAt instanceof Date) || Number.isNaN(expiresAt.getTime())) {\r\n return null;\r\n }\r\n\r\n const warningDays = options.warningDays ?? 7;\r\n const compact = options.compact ?? false;\r\n const now = options.now ?? new Date();\r\n\r\n let remainingSeconds = Math.floor((expiresAt.getTime() - now.getTime()) / 1000);\r\n if (remainingSeconds < 0) {\r\n remainingSeconds = 0;\r\n }\r\n\r\n const units = [\r\n { label: \"day\", short: \"d\", value: 86_400 },\r\n { label: \"hour\", short: \"h\", value: 3_600 },\r\n { label: \"minute\", short: \"m\", value: 60 },\r\n { label: \"second\", short: \"s\", value: 1 },\r\n ] as const;\r\n\r\n const parts: string[] = [];\r\n let remaining = remainingSeconds;\r\n for (const unit of units) {\r\n const count = Math.floor(remaining / unit.value);\r\n if (count > 0) {\r\n parts.push(compact ? `${count}${unit.short}` : `${count} ${unit.label}${count > 1 ? \"s\" : \"\"}`);\r\n remaining -= count * unit.value;\r\n }\r\n }\r\n\r\n const daysLeft = Math.ceil(remainingSeconds / 86_400);\r\n\r\n return {\r\n expiresAt,\r\n remainingSeconds,\r\n remainingHuman: parts.join(\" \") || \"Expired\",\r\n daysLeft,\r\n isExpired: remainingSeconds === 0,\r\n needsRefresh: daysLeft <= warningDays,\r\n };\r\n}\r\n\r\n/**\r\n * Builds a configurable IST calendar window, typically for booking start/end dates.\r\n */\r\nexport function getCalendarWindow(options: CalendarWindowOptions = {}): { min: Date; max: Date } {\r\n const now = options.now ?? new Date();\r\n const cutoffHour = options.cutoffHour ?? 14;\r\n const windowDays = options.windowDays ?? 36;\r\n const startOffsetBeforeCutoff = options.startOffsetBeforeCutoff ?? 1;\r\n const startOffsetAfterCutoff = options.startOffsetAfterCutoff ?? 2;\r\n\r\n if (!Number.isInteger(cutoffHour) || cutoffHour < 0 || cutoffHour > 23) {\r\n throw new RangeError(\"cutoffHour must be an integer between 0 and 23\");\r\n }\r\n if (!Number.isInteger(windowDays) || windowDays < 0) {\r\n throw new RangeError(\"windowDays must be an integer greater than or equal to 0\");\r\n }\r\n\r\n const { year, month, day, hour } = getIstDateParts(now);\r\n const startOffsetDays = hour < cutoffHour ? startOffsetBeforeCutoff : startOffsetAfterCutoff;\r\n const minYmd = addCalendarDays(year, month, day, startOffsetDays);\r\n const maxYmd = addCalendarDays(minYmd.year, minYmd.month, minYmd.day, windowDays);\r\n\r\n return {\r\n min: istDateTime(minYmd.year, minYmd.month, minYmd.day, 0, 0, 0, 0),\r\n max: istDateTime(maxYmd.year, maxYmd.month, maxYmd.day, 23, 59, 59, 999),\r\n };\r\n}\r\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
interface IstDateParts {
|
|
2
|
+
year: number;
|
|
3
|
+
month: number;
|
|
4
|
+
day: number;
|
|
5
|
+
hour: number;
|
|
6
|
+
}
|
|
7
|
+
interface ExpiryInfo {
|
|
8
|
+
expiresAt: Date;
|
|
9
|
+
remainingSeconds: number;
|
|
10
|
+
remainingHuman: string;
|
|
11
|
+
daysLeft: number;
|
|
12
|
+
isExpired: boolean;
|
|
13
|
+
needsRefresh: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface ExpiryInfoOptions {
|
|
16
|
+
warningDays?: number;
|
|
17
|
+
compact?: boolean;
|
|
18
|
+
now?: Date;
|
|
19
|
+
}
|
|
20
|
+
interface CalendarWindowOptions {
|
|
21
|
+
now?: Date;
|
|
22
|
+
cutoffHour?: number;
|
|
23
|
+
windowDays?: number;
|
|
24
|
+
startOffsetBeforeCutoff?: number;
|
|
25
|
+
startOffsetAfterCutoff?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Reads calendar parts of a date in Asia/Kolkata.
|
|
29
|
+
*/
|
|
30
|
+
declare function getIstDateParts(date?: Date): IstDateParts;
|
|
31
|
+
/**
|
|
32
|
+
* Adds calendar days to a year/month/day triple without DST surprises.
|
|
33
|
+
*/
|
|
34
|
+
declare function addCalendarDays(year: number, month: number, day: number, days: number): {
|
|
35
|
+
year: number;
|
|
36
|
+
month: number;
|
|
37
|
+
day: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Builds a Date for an Asia/Kolkata wall-clock time.
|
|
41
|
+
*/
|
|
42
|
+
declare function istDateTime(year: number, month: number, day: number, hour?: number, minute?: number, second?: number, millisecond?: number): Date;
|
|
43
|
+
/**
|
|
44
|
+
* Parses a naive IST datetime as Asia/Kolkata and returns a UTC Date.
|
|
45
|
+
* Values with an explicit timezone (`Z` or `±HH:mm`) are treated as absolute instants.
|
|
46
|
+
*/
|
|
47
|
+
declare function parseIstDateTimeToUtc(value: Date | string | number | null | undefined): Date | null;
|
|
48
|
+
/**
|
|
49
|
+
* Formats a UTC instant as `YYYY-MM-DD HH:mm:ss` in Asia/Kolkata.
|
|
50
|
+
*/
|
|
51
|
+
declare function formatUtcToIstDateTime(value: Date | string | number | null | undefined): string;
|
|
52
|
+
/**
|
|
53
|
+
* Returns remaining time until `expiresAt`.
|
|
54
|
+
*/
|
|
55
|
+
declare function getExpiryInfo(expiresAt: Date, options?: ExpiryInfoOptions): ExpiryInfo | null;
|
|
56
|
+
/**
|
|
57
|
+
* Builds a configurable IST calendar window, typically for booking start/end dates.
|
|
58
|
+
*/
|
|
59
|
+
declare function getCalendarWindow(options?: CalendarWindowOptions): {
|
|
60
|
+
min: Date;
|
|
61
|
+
max: Date;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export { addCalendarDays, formatUtcToIstDateTime, getCalendarWindow, getExpiryInfo, getIstDateParts, istDateTime, parseIstDateTimeToUtc };
|
|
65
|
+
export type { CalendarWindowOptions, ExpiryInfo, ExpiryInfoOptions, IstDateParts };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
interface IstDateParts {
|
|
2
|
+
year: number;
|
|
3
|
+
month: number;
|
|
4
|
+
day: number;
|
|
5
|
+
hour: number;
|
|
6
|
+
}
|
|
7
|
+
interface ExpiryInfo {
|
|
8
|
+
expiresAt: Date;
|
|
9
|
+
remainingSeconds: number;
|
|
10
|
+
remainingHuman: string;
|
|
11
|
+
daysLeft: number;
|
|
12
|
+
isExpired: boolean;
|
|
13
|
+
needsRefresh: boolean;
|
|
14
|
+
}
|
|
15
|
+
interface ExpiryInfoOptions {
|
|
16
|
+
warningDays?: number;
|
|
17
|
+
compact?: boolean;
|
|
18
|
+
now?: Date;
|
|
19
|
+
}
|
|
20
|
+
interface CalendarWindowOptions {
|
|
21
|
+
now?: Date;
|
|
22
|
+
cutoffHour?: number;
|
|
23
|
+
windowDays?: number;
|
|
24
|
+
startOffsetBeforeCutoff?: number;
|
|
25
|
+
startOffsetAfterCutoff?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Reads calendar parts of a date in Asia/Kolkata.
|
|
29
|
+
*/
|
|
30
|
+
declare function getIstDateParts(date?: Date): IstDateParts;
|
|
31
|
+
/**
|
|
32
|
+
* Adds calendar days to a year/month/day triple without DST surprises.
|
|
33
|
+
*/
|
|
34
|
+
declare function addCalendarDays(year: number, month: number, day: number, days: number): {
|
|
35
|
+
year: number;
|
|
36
|
+
month: number;
|
|
37
|
+
day: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Builds a Date for an Asia/Kolkata wall-clock time.
|
|
41
|
+
*/
|
|
42
|
+
declare function istDateTime(year: number, month: number, day: number, hour?: number, minute?: number, second?: number, millisecond?: number): Date;
|
|
43
|
+
/**
|
|
44
|
+
* Parses a naive IST datetime as Asia/Kolkata and returns a UTC Date.
|
|
45
|
+
* Values with an explicit timezone (`Z` or `±HH:mm`) are treated as absolute instants.
|
|
46
|
+
*/
|
|
47
|
+
declare function parseIstDateTimeToUtc(value: Date | string | number | null | undefined): Date | null;
|
|
48
|
+
/**
|
|
49
|
+
* Formats a UTC instant as `YYYY-MM-DD HH:mm:ss` in Asia/Kolkata.
|
|
50
|
+
*/
|
|
51
|
+
declare function formatUtcToIstDateTime(value: Date | string | number | null | undefined): string;
|
|
52
|
+
/**
|
|
53
|
+
* Returns remaining time until `expiresAt`.
|
|
54
|
+
*/
|
|
55
|
+
declare function getExpiryInfo(expiresAt: Date, options?: ExpiryInfoOptions): ExpiryInfo | null;
|
|
56
|
+
/**
|
|
57
|
+
* Builds a configurable IST calendar window, typically for booking start/end dates.
|
|
58
|
+
*/
|
|
59
|
+
declare function getCalendarWindow(options?: CalendarWindowOptions): {
|
|
60
|
+
min: Date;
|
|
61
|
+
max: Date;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export { addCalendarDays, formatUtcToIstDateTime, getCalendarWindow, getExpiryInfo, getIstDateParts, istDateTime, parseIstDateTimeToUtc };
|
|
65
|
+
export type { CalendarWindowOptions, ExpiryInfo, ExpiryInfoOptions, IstDateParts };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// src/date/index.ts
|
|
2
|
+
var IST_OFFSET_MS = 330 * 60 * 1e3;
|
|
3
|
+
var IST_DATETIME_RE = /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})(?::(\d{2}))?/;
|
|
4
|
+
function pad(value, length = 2) {
|
|
5
|
+
return String(value).padStart(length, "0");
|
|
6
|
+
}
|
|
7
|
+
function getIstDateParts(date = /* @__PURE__ */ new Date()) {
|
|
8
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
9
|
+
timeZone: "Asia/Kolkata",
|
|
10
|
+
year: "numeric",
|
|
11
|
+
month: "2-digit",
|
|
12
|
+
day: "2-digit",
|
|
13
|
+
hour: "2-digit",
|
|
14
|
+
hourCycle: "h23"
|
|
15
|
+
}).formatToParts(date);
|
|
16
|
+
const read = (type) => {
|
|
17
|
+
const value = parts.find((part) => part.type === type)?.value;
|
|
18
|
+
return Number(value);
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
year: read("year"),
|
|
22
|
+
month: read("month"),
|
|
23
|
+
day: read("day"),
|
|
24
|
+
hour: read("hour")
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function addCalendarDays(year, month, day, days) {
|
|
28
|
+
const utc = new Date(Date.UTC(year, month - 1, day + days));
|
|
29
|
+
return {
|
|
30
|
+
year: utc.getUTCFullYear(),
|
|
31
|
+
month: utc.getUTCMonth() + 1,
|
|
32
|
+
day: utc.getUTCDate()
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function istDateTime(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0) {
|
|
36
|
+
return /* @__PURE__ */ new Date(
|
|
37
|
+
`${year}-${pad(month)}-${pad(day)}T${pad(hour)}:${pad(minute)}:${pad(second)}.${pad(millisecond, 3)}+05:30`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
function parseIstDateTimeToUtc(value) {
|
|
41
|
+
if (value == null || value === "") {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (value instanceof Date) {
|
|
45
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
46
|
+
}
|
|
47
|
+
if (typeof value === "number") {
|
|
48
|
+
const parsed = new Date(value);
|
|
49
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
50
|
+
}
|
|
51
|
+
const raw = value.trim();
|
|
52
|
+
if (raw.length === 0) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (/[zZ]$/.test(raw) || /[+-]\d{2}:?\d{2}$/.test(raw)) {
|
|
56
|
+
const parsed = new Date(raw);
|
|
57
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
58
|
+
}
|
|
59
|
+
const match = raw.match(IST_DATETIME_RE);
|
|
60
|
+
if (!match) {
|
|
61
|
+
const parsed = new Date(raw);
|
|
62
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed;
|
|
63
|
+
}
|
|
64
|
+
const [, year = "0", month = "1", day = "1", hour = "0", minute = "0", second = "0"] = match;
|
|
65
|
+
const istAsUtcMs = Date.UTC(
|
|
66
|
+
Number(year),
|
|
67
|
+
Number(month) - 1,
|
|
68
|
+
Number(day),
|
|
69
|
+
Number(hour),
|
|
70
|
+
Number(minute),
|
|
71
|
+
Number(second)
|
|
72
|
+
);
|
|
73
|
+
return new Date(istAsUtcMs - IST_OFFSET_MS);
|
|
74
|
+
}
|
|
75
|
+
function formatUtcToIstDateTime(value) {
|
|
76
|
+
if (value == null || value === "") {
|
|
77
|
+
return "";
|
|
78
|
+
}
|
|
79
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
80
|
+
if (Number.isNaN(date.getTime())) {
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
const ist = new Date(date.getTime() + IST_OFFSET_MS);
|
|
84
|
+
return `${ist.getUTCFullYear()}-${pad(ist.getUTCMonth() + 1)}-${pad(ist.getUTCDate())} ${pad(ist.getUTCHours())}:${pad(ist.getUTCMinutes())}:${pad(ist.getUTCSeconds())}`;
|
|
85
|
+
}
|
|
86
|
+
function getExpiryInfo(expiresAt, options = {}) {
|
|
87
|
+
if (!(expiresAt instanceof Date) || Number.isNaN(expiresAt.getTime())) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const warningDays = options.warningDays ?? 7;
|
|
91
|
+
const compact = options.compact ?? false;
|
|
92
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
93
|
+
let remainingSeconds = Math.floor((expiresAt.getTime() - now.getTime()) / 1e3);
|
|
94
|
+
if (remainingSeconds < 0) {
|
|
95
|
+
remainingSeconds = 0;
|
|
96
|
+
}
|
|
97
|
+
const units = [
|
|
98
|
+
{ label: "day", short: "d", value: 86400 },
|
|
99
|
+
{ label: "hour", short: "h", value: 3600 },
|
|
100
|
+
{ label: "minute", short: "m", value: 60 },
|
|
101
|
+
{ label: "second", short: "s", value: 1 }
|
|
102
|
+
];
|
|
103
|
+
const parts = [];
|
|
104
|
+
let remaining = remainingSeconds;
|
|
105
|
+
for (const unit of units) {
|
|
106
|
+
const count = Math.floor(remaining / unit.value);
|
|
107
|
+
if (count > 0) {
|
|
108
|
+
parts.push(compact ? `${count}${unit.short}` : `${count} ${unit.label}${count > 1 ? "s" : ""}`);
|
|
109
|
+
remaining -= count * unit.value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const daysLeft = Math.ceil(remainingSeconds / 86400);
|
|
113
|
+
return {
|
|
114
|
+
expiresAt,
|
|
115
|
+
remainingSeconds,
|
|
116
|
+
remainingHuman: parts.join(" ") || "Expired",
|
|
117
|
+
daysLeft,
|
|
118
|
+
isExpired: remainingSeconds === 0,
|
|
119
|
+
needsRefresh: daysLeft <= warningDays
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function getCalendarWindow(options = {}) {
|
|
123
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
124
|
+
const cutoffHour = options.cutoffHour ?? 14;
|
|
125
|
+
const windowDays = options.windowDays ?? 36;
|
|
126
|
+
const startOffsetBeforeCutoff = options.startOffsetBeforeCutoff ?? 1;
|
|
127
|
+
const startOffsetAfterCutoff = options.startOffsetAfterCutoff ?? 2;
|
|
128
|
+
if (!Number.isInteger(cutoffHour) || cutoffHour < 0 || cutoffHour > 23) {
|
|
129
|
+
throw new RangeError("cutoffHour must be an integer between 0 and 23");
|
|
130
|
+
}
|
|
131
|
+
if (!Number.isInteger(windowDays) || windowDays < 0) {
|
|
132
|
+
throw new RangeError("windowDays must be an integer greater than or equal to 0");
|
|
133
|
+
}
|
|
134
|
+
const { year, month, day, hour } = getIstDateParts(now);
|
|
135
|
+
const startOffsetDays = hour < cutoffHour ? startOffsetBeforeCutoff : startOffsetAfterCutoff;
|
|
136
|
+
const minYmd = addCalendarDays(year, month, day, startOffsetDays);
|
|
137
|
+
const maxYmd = addCalendarDays(minYmd.year, minYmd.month, minYmd.day, windowDays);
|
|
138
|
+
return {
|
|
139
|
+
min: istDateTime(minYmd.year, minYmd.month, minYmd.day, 0, 0, 0, 0),
|
|
140
|
+
max: istDateTime(maxYmd.year, maxYmd.month, maxYmd.day, 23, 59, 59, 999)
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export { addCalendarDays, formatUtcToIstDateTime, getCalendarWindow, getExpiryInfo, getIstDateParts, istDateTime, parseIstDateTimeToUtc };
|
|
145
|
+
//# sourceMappingURL=index.js.map
|
|
146
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/date/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["const IST_OFFSET_MS = 330 * 60 * 1000;\r\nconst IST_DATETIME_RE =\r\n /^(\\d{4})-(\\d{2})-(\\d{2})[ T](\\d{2}):(\\d{2})(?::(\\d{2}))?/;\r\n\r\nexport interface IstDateParts {\r\n year: number;\r\n month: number;\r\n day: number;\r\n hour: number;\r\n}\r\n\r\nexport interface ExpiryInfo {\r\n expiresAt: Date;\r\n remainingSeconds: number;\r\n remainingHuman: string;\r\n daysLeft: number;\r\n isExpired: boolean;\r\n needsRefresh: boolean;\r\n}\r\n\r\nexport interface ExpiryInfoOptions {\r\n warningDays?: number;\r\n compact?: boolean;\r\n now?: Date;\r\n}\r\n\r\nexport interface CalendarWindowOptions {\r\n now?: Date;\r\n cutoffHour?: number;\r\n windowDays?: number;\r\n startOffsetBeforeCutoff?: number;\r\n startOffsetAfterCutoff?: number;\r\n}\r\n\r\nfunction pad(value: number, length = 2): string {\r\n return String(value).padStart(length, \"0\");\r\n}\r\n\r\n/**\r\n * Reads calendar parts of a date in Asia/Kolkata.\r\n */\r\nexport function getIstDateParts(date: Date = new Date()): IstDateParts {\r\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\r\n timeZone: \"Asia/Kolkata\",\r\n year: \"numeric\",\r\n month: \"2-digit\",\r\n day: \"2-digit\",\r\n hour: \"2-digit\",\r\n hourCycle: \"h23\",\r\n }).formatToParts(date);\r\n\r\n const read = (type: Intl.DateTimeFormatPartTypes): number => {\r\n const value = parts.find((part) => part.type === type)?.value;\r\n return Number(value);\r\n };\r\n\r\n return {\r\n year: read(\"year\"),\r\n month: read(\"month\"),\r\n day: read(\"day\"),\r\n hour: read(\"hour\"),\r\n };\r\n}\r\n\r\n/**\r\n * Adds calendar days to a year/month/day triple without DST surprises.\r\n */\r\nexport function addCalendarDays(\r\n year: number,\r\n month: number,\r\n day: number,\r\n days: number,\r\n): { year: number; month: number; day: number } {\r\n const utc = new Date(Date.UTC(year, month - 1, day + days));\r\n return {\r\n year: utc.getUTCFullYear(),\r\n month: utc.getUTCMonth() + 1,\r\n day: utc.getUTCDate(),\r\n };\r\n}\r\n\r\n/**\r\n * Builds a Date for an Asia/Kolkata wall-clock time.\r\n */\r\nexport function istDateTime(\r\n year: number,\r\n month: number,\r\n day: number,\r\n hour = 0,\r\n minute = 0,\r\n second = 0,\r\n millisecond = 0,\r\n): Date {\r\n return new Date(\r\n `${year}-${pad(month)}-${pad(day)}T${pad(hour)}:${pad(minute)}:${pad(second)}.${pad(millisecond, 3)}+05:30`,\r\n );\r\n}\r\n\r\n/**\r\n * Parses a naive IST datetime as Asia/Kolkata and returns a UTC Date.\r\n * Values with an explicit timezone (`Z` or `±HH:mm`) are treated as absolute instants.\r\n */\r\nexport function parseIstDateTimeToUtc(value: Date | string | number | null | undefined): Date | null {\r\n if (value == null || value === \"\") {\r\n return null;\r\n }\r\n\r\n if (value instanceof Date) {\r\n return Number.isNaN(value.getTime()) ? null : value;\r\n }\r\n\r\n if (typeof value === \"number\") {\r\n const parsed = new Date(value);\r\n return Number.isNaN(parsed.getTime()) ? null : parsed;\r\n }\r\n\r\n const raw = value.trim();\r\n if (raw.length === 0) {\r\n return null;\r\n }\r\n\r\n if (/[zZ]$/.test(raw) || /[+-]\\d{2}:?\\d{2}$/.test(raw)) {\r\n const parsed = new Date(raw);\r\n return Number.isNaN(parsed.getTime()) ? null : parsed;\r\n }\r\n\r\n const match = raw.match(IST_DATETIME_RE);\r\n if (!match) {\r\n const parsed = new Date(raw);\r\n return Number.isNaN(parsed.getTime()) ? null : parsed;\r\n }\r\n\r\n const [, year = \"0\", month = \"1\", day = \"1\", hour = \"0\", minute = \"0\", second = \"0\"] = match;\r\n const istAsUtcMs = Date.UTC(\r\n Number(year),\r\n Number(month) - 1,\r\n Number(day),\r\n Number(hour),\r\n Number(minute),\r\n Number(second),\r\n );\r\n\r\n return new Date(istAsUtcMs - IST_OFFSET_MS);\r\n}\r\n\r\n/**\r\n * Formats a UTC instant as `YYYY-MM-DD HH:mm:ss` in Asia/Kolkata.\r\n */\r\nexport function formatUtcToIstDateTime(value: Date | string | number | null | undefined): string {\r\n if (value == null || value === \"\") {\r\n return \"\";\r\n }\r\n\r\n const date = value instanceof Date ? value : new Date(value);\r\n if (Number.isNaN(date.getTime())) {\r\n return \"\";\r\n }\r\n\r\n const ist = new Date(date.getTime() + IST_OFFSET_MS);\r\n return `${ist.getUTCFullYear()}-${pad(ist.getUTCMonth() + 1)}-${pad(ist.getUTCDate())} ${pad(ist.getUTCHours())}:${pad(ist.getUTCMinutes())}:${pad(ist.getUTCSeconds())}`;\r\n}\r\n\r\n/**\r\n * Returns remaining time until `expiresAt`.\r\n */\r\nexport function getExpiryInfo(expiresAt: Date, options: ExpiryInfoOptions = {}): ExpiryInfo | null {\r\n if (!(expiresAt instanceof Date) || Number.isNaN(expiresAt.getTime())) {\r\n return null;\r\n }\r\n\r\n const warningDays = options.warningDays ?? 7;\r\n const compact = options.compact ?? false;\r\n const now = options.now ?? new Date();\r\n\r\n let remainingSeconds = Math.floor((expiresAt.getTime() - now.getTime()) / 1000);\r\n if (remainingSeconds < 0) {\r\n remainingSeconds = 0;\r\n }\r\n\r\n const units = [\r\n { label: \"day\", short: \"d\", value: 86_400 },\r\n { label: \"hour\", short: \"h\", value: 3_600 },\r\n { label: \"minute\", short: \"m\", value: 60 },\r\n { label: \"second\", short: \"s\", value: 1 },\r\n ] as const;\r\n\r\n const parts: string[] = [];\r\n let remaining = remainingSeconds;\r\n for (const unit of units) {\r\n const count = Math.floor(remaining / unit.value);\r\n if (count > 0) {\r\n parts.push(compact ? `${count}${unit.short}` : `${count} ${unit.label}${count > 1 ? \"s\" : \"\"}`);\r\n remaining -= count * unit.value;\r\n }\r\n }\r\n\r\n const daysLeft = Math.ceil(remainingSeconds / 86_400);\r\n\r\n return {\r\n expiresAt,\r\n remainingSeconds,\r\n remainingHuman: parts.join(\" \") || \"Expired\",\r\n daysLeft,\r\n isExpired: remainingSeconds === 0,\r\n needsRefresh: daysLeft <= warningDays,\r\n };\r\n}\r\n\r\n/**\r\n * Builds a configurable IST calendar window, typically for booking start/end dates.\r\n */\r\nexport function getCalendarWindow(options: CalendarWindowOptions = {}): { min: Date; max: Date } {\r\n const now = options.now ?? new Date();\r\n const cutoffHour = options.cutoffHour ?? 14;\r\n const windowDays = options.windowDays ?? 36;\r\n const startOffsetBeforeCutoff = options.startOffsetBeforeCutoff ?? 1;\r\n const startOffsetAfterCutoff = options.startOffsetAfterCutoff ?? 2;\r\n\r\n if (!Number.isInteger(cutoffHour) || cutoffHour < 0 || cutoffHour > 23) {\r\n throw new RangeError(\"cutoffHour must be an integer between 0 and 23\");\r\n }\r\n if (!Number.isInteger(windowDays) || windowDays < 0) {\r\n throw new RangeError(\"windowDays must be an integer greater than or equal to 0\");\r\n }\r\n\r\n const { year, month, day, hour } = getIstDateParts(now);\r\n const startOffsetDays = hour < cutoffHour ? startOffsetBeforeCutoff : startOffsetAfterCutoff;\r\n const minYmd = addCalendarDays(year, month, day, startOffsetDays);\r\n const maxYmd = addCalendarDays(minYmd.year, minYmd.month, minYmd.day, windowDays);\r\n\r\n return {\r\n min: istDateTime(minYmd.year, minYmd.month, minYmd.day, 0, 0, 0, 0),\r\n max: istDateTime(maxYmd.year, maxYmd.month, maxYmd.day, 23, 59, 59, 999),\r\n };\r\n}\r\n"]}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/internal/validation.ts
|
|
4
|
+
function assertFiniteNumber(value, name) {
|
|
5
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
6
|
+
throw new TypeError(`${name} must be a finite number`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// src/format/index.ts
|
|
11
|
+
var DEFAULT_LOCALE = "en-IN";
|
|
12
|
+
var DEFAULT_TIME_ZONE = "Asia/Kolkata";
|
|
13
|
+
var FILE_SIZE_UNITS = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
|
|
14
|
+
function formatDateTime(value, options = {}) {
|
|
15
|
+
if (value == null || value === "") {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
19
|
+
if (Number.isNaN(date.getTime())) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const { locale = DEFAULT_LOCALE, ...intlOptions } = options;
|
|
23
|
+
if (typeof locale !== "string" || locale.trim().length === 0) {
|
|
24
|
+
throw new TypeError("locale must be a non-empty BCP 47 locale tag");
|
|
25
|
+
}
|
|
26
|
+
return new Intl.DateTimeFormat(locale, {
|
|
27
|
+
timeZone: DEFAULT_TIME_ZONE,
|
|
28
|
+
day: "2-digit",
|
|
29
|
+
month: "short",
|
|
30
|
+
year: "numeric",
|
|
31
|
+
hour: "2-digit",
|
|
32
|
+
minute: "2-digit",
|
|
33
|
+
second: "2-digit",
|
|
34
|
+
hour12: true,
|
|
35
|
+
...intlOptions
|
|
36
|
+
}).format(date);
|
|
37
|
+
}
|
|
38
|
+
function formatFileSize(bytes, decimals = 2) {
|
|
39
|
+
assertFiniteNumber(bytes, "bytes");
|
|
40
|
+
if (bytes < 0) {
|
|
41
|
+
throw new RangeError("bytes must be greater than or equal to 0");
|
|
42
|
+
}
|
|
43
|
+
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {
|
|
44
|
+
throw new RangeError("decimals must be an integer between 0 and 8");
|
|
45
|
+
}
|
|
46
|
+
if (bytes === 0) {
|
|
47
|
+
return "0 Bytes";
|
|
48
|
+
}
|
|
49
|
+
const unitIndex = Math.min(
|
|
50
|
+
Math.floor(Math.log(bytes) / Math.log(1024)),
|
|
51
|
+
FILE_SIZE_UNITS.length - 1
|
|
52
|
+
);
|
|
53
|
+
const value = bytes / 1024 ** unitIndex;
|
|
54
|
+
const unit = FILE_SIZE_UNITS[unitIndex] ?? "Bytes";
|
|
55
|
+
return `${Number(value.toFixed(decimals))} ${unit}`;
|
|
56
|
+
}
|
|
57
|
+
function formatDuration(seconds) {
|
|
58
|
+
assertFiniteNumber(seconds, "seconds");
|
|
59
|
+
if (seconds <= 0) {
|
|
60
|
+
return "0 sec";
|
|
61
|
+
}
|
|
62
|
+
const total = Math.floor(seconds);
|
|
63
|
+
const hours = Math.floor(total / 3600);
|
|
64
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
65
|
+
const remainingSeconds = total % 60;
|
|
66
|
+
const parts = [];
|
|
67
|
+
if (hours > 0) {
|
|
68
|
+
parts.push(`${hours} hr`);
|
|
69
|
+
}
|
|
70
|
+
if (minutes > 0) {
|
|
71
|
+
parts.push(`${minutes} min`);
|
|
72
|
+
}
|
|
73
|
+
if (remainingSeconds > 0 || parts.length === 0) {
|
|
74
|
+
parts.push(`${remainingSeconds} sec`);
|
|
75
|
+
}
|
|
76
|
+
return parts.join(" ");
|
|
77
|
+
}
|
|
78
|
+
function formatDurationCompact(value, options = {}) {
|
|
79
|
+
assertFiniteNumber(value, "value");
|
|
80
|
+
const unit = options.unit ?? "seconds";
|
|
81
|
+
if (unit !== "seconds" && unit !== "milliseconds") {
|
|
82
|
+
throw new TypeError('unit must be "seconds" or "milliseconds"');
|
|
83
|
+
}
|
|
84
|
+
let remaining = Math.floor(unit === "milliseconds" ? value / 1e3 : value);
|
|
85
|
+
if (remaining <= 0) {
|
|
86
|
+
return "0s";
|
|
87
|
+
}
|
|
88
|
+
const years = Math.floor(remaining / 31536e3);
|
|
89
|
+
remaining %= 31536e3;
|
|
90
|
+
const months = Math.floor(remaining / 2592e3);
|
|
91
|
+
remaining %= 2592e3;
|
|
92
|
+
const days = Math.floor(remaining / 86400);
|
|
93
|
+
remaining %= 86400;
|
|
94
|
+
const hours = Math.floor(remaining / 3600);
|
|
95
|
+
remaining %= 3600;
|
|
96
|
+
const minutes = Math.floor(remaining / 60);
|
|
97
|
+
const seconds = remaining % 60;
|
|
98
|
+
const parts = [];
|
|
99
|
+
if (years > 0) {
|
|
100
|
+
parts.push(`${years}y`);
|
|
101
|
+
}
|
|
102
|
+
if (months > 0) {
|
|
103
|
+
parts.push(`${months}mo`);
|
|
104
|
+
}
|
|
105
|
+
if (days > 0) {
|
|
106
|
+
parts.push(`${days}d`);
|
|
107
|
+
}
|
|
108
|
+
if (hours > 0) {
|
|
109
|
+
parts.push(`${hours}h`);
|
|
110
|
+
}
|
|
111
|
+
if (minutes > 0) {
|
|
112
|
+
parts.push(`${minutes}m`);
|
|
113
|
+
}
|
|
114
|
+
if (seconds > 0 || parts.length === 0) {
|
|
115
|
+
parts.push(`${seconds}s`);
|
|
116
|
+
}
|
|
117
|
+
return parts.join(" ");
|
|
118
|
+
}
|
|
119
|
+
function formatPriceToLakh(price, decimals = 2) {
|
|
120
|
+
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {
|
|
121
|
+
throw new RangeError("decimals must be an integer between 0 and 8");
|
|
122
|
+
}
|
|
123
|
+
const numeric = typeof price === "number" ? price : typeof price === "string" ? Number(price.trim()) : Number.NaN;
|
|
124
|
+
if (!Number.isFinite(numeric)) {
|
|
125
|
+
return "";
|
|
126
|
+
}
|
|
127
|
+
return `${(numeric / 1e5).toFixed(decimals)} Lakh`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
exports.formatDateTime = formatDateTime;
|
|
131
|
+
exports.formatDuration = formatDuration;
|
|
132
|
+
exports.formatDurationCompact = formatDurationCompact;
|
|
133
|
+
exports.formatFileSize = formatFileSize;
|
|
134
|
+
exports.formatPriceToLakh = formatPriceToLakh;
|
|
135
|
+
//# sourceMappingURL=index.cjs.map
|
|
136
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/internal/validation.ts","../../src/format/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["export function assertFiniteNumber(\r\n value: unknown,\r\n name: string,\r\n): asserts value is number {\r\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\r\n throw new TypeError(`${name} must be a finite number`);\r\n }\r\n}\r\n\r\nexport function escapeRegExp(value: string): string {\r\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n","import { assertFiniteNumber } from \"../internal/validation.js\";\r\n\r\nconst DEFAULT_LOCALE = \"en-IN\";\r\nconst DEFAULT_TIME_ZONE = \"Asia/Kolkata\";\r\nconst FILE_SIZE_UNITS = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"] as const;\r\n\r\nexport interface FormatDateTimeOptions extends Intl.DateTimeFormatOptions {\r\n locale?: string;\r\n}\r\n\r\nexport interface FormatDurationOptions {\r\n /**\r\n * Interpret `value` as milliseconds instead of seconds.\r\n * @default \"seconds\"\r\n */\r\n unit?: \"seconds\" | \"milliseconds\";\r\n}\r\n\r\n/**\r\n * Formats a date with `Intl.DateTimeFormat`.\r\n * Defaults to `en-IN` in the `Asia/Kolkata` time zone, including time.\r\n * Returns `null` for missing or invalid values.\r\n *\r\n * @example\r\n * formatDateTime(\"2024-01-15T00:00:00.000Z\")\r\n * formatDateTime(date, { hour: undefined, minute: undefined, second: undefined })\r\n */\r\nexport function formatDateTime(\r\n value: Date | string | number | null | undefined,\r\n options: FormatDateTimeOptions = {},\r\n): string | null {\r\n if (value == null || value === \"\") {\r\n return null;\r\n }\r\n\r\n const date = value instanceof Date ? value : new Date(value);\r\n if (Number.isNaN(date.getTime())) {\r\n return null;\r\n }\r\n\r\n const { locale = DEFAULT_LOCALE, ...intlOptions } = options;\r\n if (typeof locale !== \"string\" || locale.trim().length === 0) {\r\n throw new TypeError(\"locale must be a non-empty BCP 47 locale tag\");\r\n }\r\n\r\n return new Intl.DateTimeFormat(locale, {\r\n timeZone: DEFAULT_TIME_ZONE,\r\n day: \"2-digit\",\r\n month: \"short\",\r\n year: \"numeric\",\r\n hour: \"2-digit\",\r\n minute: \"2-digit\",\r\n second: \"2-digit\",\r\n hour12: true,\r\n ...intlOptions,\r\n }).format(date);\r\n}\r\n\r\n/**\r\n * Formats a byte count as a human-readable file size.\r\n *\r\n * @example\r\n * formatFileSize(0) // \"0 Bytes\"\r\n * formatFileSize(1536) // \"1.5 KB\"\r\n */\r\nexport function formatFileSize(bytes: number, decimals = 2): string {\r\n assertFiniteNumber(bytes, \"bytes\");\r\n if (bytes < 0) {\r\n throw new RangeError(\"bytes must be greater than or equal to 0\");\r\n }\r\n\r\n if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {\r\n throw new RangeError(\"decimals must be an integer between 0 and 8\");\r\n }\r\n\r\n if (bytes === 0) {\r\n return \"0 Bytes\";\r\n }\r\n\r\n const unitIndex = Math.min(\r\n Math.floor(Math.log(bytes) / Math.log(1024)),\r\n FILE_SIZE_UNITS.length - 1,\r\n );\r\n const value = bytes / 1024 ** unitIndex;\r\n const unit = FILE_SIZE_UNITS[unitIndex] ?? \"Bytes\";\r\n\r\n return `${Number(value.toFixed(decimals))} ${unit}`;\r\n}\r\n\r\n/**\r\n * Formats a duration in hours, minutes, and seconds.\r\n *\r\n * @example\r\n * formatDuration(3661) // \"1 hr 1 min 1 sec\"\r\n * formatDuration(0) // \"0 sec\"\r\n */\r\nexport function formatDuration(seconds: number): string {\r\n assertFiniteNumber(seconds, \"seconds\");\r\n\r\n if (seconds <= 0) {\r\n return \"0 sec\";\r\n }\r\n\r\n const total = Math.floor(seconds);\r\n const hours = Math.floor(total / 3600);\r\n const minutes = Math.floor((total % 3600) / 60);\r\n const remainingSeconds = total % 60;\r\n\r\n const parts: string[] = [];\r\n if (hours > 0) {\r\n parts.push(`${hours} hr`);\r\n }\r\n if (minutes > 0) {\r\n parts.push(`${minutes} min`);\r\n }\r\n if (remainingSeconds > 0 || parts.length === 0) {\r\n parts.push(`${remainingSeconds} sec`);\r\n }\r\n\r\n return parts.join(\" \");\r\n}\r\n\r\n/**\r\n * Formats a duration as a compact `y d h m s` string.\r\n * Month and year units use fixed 30-day and 365-day approximations.\r\n *\r\n * @example\r\n * formatDurationCompact(3661) // \"1h 1m 1s\"\r\n * formatDurationCompact(1500, { unit: \"milliseconds\" }) // \"1s\"\r\n */\r\nexport function formatDurationCompact(\r\n value: number,\r\n options: FormatDurationOptions = {},\r\n): string {\r\n assertFiniteNumber(value, \"value\");\r\n\r\n const unit = options.unit ?? \"seconds\";\r\n if (unit !== \"seconds\" && unit !== \"milliseconds\") {\r\n throw new TypeError('unit must be \"seconds\" or \"milliseconds\"');\r\n }\r\n\r\n let remaining = Math.floor(unit === \"milliseconds\" ? value / 1000 : value);\r\n if (remaining <= 0) {\r\n return \"0s\";\r\n }\r\n\r\n const years = Math.floor(remaining / 31_536_000);\r\n remaining %= 31_536_000;\r\n const months = Math.floor(remaining / 2_592_000);\r\n remaining %= 2_592_000;\r\n const days = Math.floor(remaining / 86_400);\r\n remaining %= 86_400;\r\n const hours = Math.floor(remaining / 3600);\r\n remaining %= 3600;\r\n const minutes = Math.floor(remaining / 60);\r\n const seconds = remaining % 60;\r\n\r\n const parts: string[] = [];\r\n if (years > 0) {\r\n parts.push(`${years}y`);\r\n }\r\n if (months > 0) {\r\n parts.push(`${months}mo`);\r\n }\r\n if (days > 0) {\r\n parts.push(`${days}d`);\r\n }\r\n if (hours > 0) {\r\n parts.push(`${hours}h`);\r\n }\r\n if (minutes > 0) {\r\n parts.push(`${minutes}m`);\r\n }\r\n if (seconds > 0 || parts.length === 0) {\r\n parts.push(`${seconds}s`);\r\n }\r\n\r\n return parts.join(\" \");\r\n}\r\n\r\n/**\r\n * Formats a price in Indian lakh units.\r\n * Returns an empty string for missing or invalid values.\r\n *\r\n * @example\r\n * formatPriceToLakh(2_000_000) // \"20.00 Lakh\"\r\n */\r\nexport function formatPriceToLakh(price: unknown, decimals = 2): string {\r\n if (!Number.isInteger(decimals) || decimals < 0 || decimals > 8) {\r\n throw new RangeError(\"decimals must be an integer between 0 and 8\");\r\n }\r\n\r\n const numeric =\r\n typeof price === \"number\"\r\n ? price\r\n : typeof price === \"string\"\r\n ? Number(price.trim())\r\n : Number.NaN;\r\n\r\n if (!Number.isFinite(numeric)) {\r\n return \"\";\r\n }\r\n\r\n return `${(numeric / 100_000).toFixed(decimals)} Lakh`;\r\n}\r\n"]}
|