@nivinjoseph/n-date 1.0.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/.editorconfig +14 -0
- package/.vscode/launch.json +56 -0
- package/.vscode/settings.json +111 -0
- package/.vscode/tasks.json +12 -0
- package/.yarn/releases/yarn-4.14.1.cjs +940 -0
- package/.yarnrc.yml +8 -0
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/date-time-format.d.ts +11 -0
- package/dist/date-time-format.d.ts.map +1 -0
- package/dist/date-time-format.js +11 -0
- package/dist/date-time-format.js.map +1 -0
- package/dist/date-time-span.d.ts +70 -0
- package/dist/date-time-span.d.ts.map +1 -0
- package/dist/date-time-span.js +122 -0
- package/dist/date-time-span.js.map +1 -0
- package/dist/date-time.d.ts +391 -0
- package/dist/date-time.d.ts.map +1 -0
- package/dist/date-time.js +753 -0
- package/dist/date-time.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/tsconfig.json +13 -0
- package/docs/README.md +33 -0
- package/docs/date-time-span.md +72 -0
- package/docs/date-time.md +169 -0
- package/docs/formats.md +56 -0
- package/docs/getting-started.md +151 -0
- package/eslint.config.js +596 -0
- package/package.json +57 -0
- package/src/date-time-format.ts +35 -0
- package/src/date-time-span.ts +130 -0
- package/src/date-time.ts +950 -0
- package/src/index.ts +3 -0
- package/test/date-time-comparison.test.ts +1579 -0
- package/test/date-time-create.test.ts +1147 -0
- package/test/date-time-math.test.ts +324 -0
- package/test/date-time-properties.test.ts +200 -0
- package/test/date-time-utility.test.ts +432 -0
- package/test/date-time-validations.test.ts +521 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
import { __esDecorate, __runInitializers } from "tslib";
|
|
2
|
+
import { given } from "@nivinjoseph/n-defensive";
|
|
3
|
+
import { DateTime as LuxonDateTime, Interval as LuxonInterval } from "luxon";
|
|
4
|
+
import { Serializable, serialize, Duration, TypeHelper } from "@nivinjoseph/n-util";
|
|
5
|
+
import { DateTimeFormat, DateTimeFormat_DEFAULT } from "./date-time-format.js";
|
|
6
|
+
/**
|
|
7
|
+
* A robust date and time handling system with timezone support.
|
|
8
|
+
* This class provides comprehensive functionality for date/time manipulation, comparison, and formatting.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const now = DateTime.now("UTC");
|
|
13
|
+
* const future = now.addTime(Duration.fromHours(2));
|
|
14
|
+
* const isAfter = future.isAfter(now);
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
let DateTime = (() => {
|
|
18
|
+
let _classDecorators = [serialize("Ndate")];
|
|
19
|
+
let _classDescriptor;
|
|
20
|
+
let _classExtraInitializers = [];
|
|
21
|
+
let _classThis;
|
|
22
|
+
let _classSuper = Serializable;
|
|
23
|
+
let _instanceExtraInitializers = [];
|
|
24
|
+
let _get_value_decorators;
|
|
25
|
+
let _get_zone_decorators;
|
|
26
|
+
var DateTime = class extends _classSuper {
|
|
27
|
+
static { _classThis = this; }
|
|
28
|
+
static {
|
|
29
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
30
|
+
_get_value_decorators = [serialize];
|
|
31
|
+
_get_zone_decorators = [serialize];
|
|
32
|
+
__esDecorate(this, null, _get_value_decorators, { kind: "getter", name: "value", static: false, private: false, access: { has: obj => "value" in obj, get: obj => obj.value }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
33
|
+
__esDecorate(this, null, _get_zone_decorators, { kind: "getter", name: "zone", static: false, private: false, access: { has: obj => "zone" in obj, get: obj => obj.zone }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
34
|
+
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
35
|
+
DateTime = _classThis = _classDescriptor.value;
|
|
36
|
+
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
37
|
+
}
|
|
38
|
+
static _fixedNow = null;
|
|
39
|
+
static _relativeNow = null;
|
|
40
|
+
_value = __runInitializers(this, _instanceExtraInitializers);
|
|
41
|
+
_zone;
|
|
42
|
+
_dateTime;
|
|
43
|
+
_timestamp;
|
|
44
|
+
_dateCode;
|
|
45
|
+
_timeCode;
|
|
46
|
+
_dateValue;
|
|
47
|
+
_timeValue;
|
|
48
|
+
/**
|
|
49
|
+
* Gets the system's local timezone.
|
|
50
|
+
*
|
|
51
|
+
* @returns The local timezone identifier.
|
|
52
|
+
*/
|
|
53
|
+
static get currentZone() { return LuxonDateTime.local().zoneName; }
|
|
54
|
+
/**
|
|
55
|
+
* Gets the formatted date and time string.
|
|
56
|
+
*/
|
|
57
|
+
get value() { return this._value; }
|
|
58
|
+
/**
|
|
59
|
+
* Gets the timezone identifier.
|
|
60
|
+
*/
|
|
61
|
+
get zone() { return this._zone; }
|
|
62
|
+
/**
|
|
63
|
+
* Gets the Unix timestamp in seconds.
|
|
64
|
+
*/
|
|
65
|
+
get timestamp() { return this._timestamp; }
|
|
66
|
+
/**
|
|
67
|
+
* Gets the date code in YYYYMMDD format.
|
|
68
|
+
*/
|
|
69
|
+
get dateCode() { return this._dateCode; }
|
|
70
|
+
/**
|
|
71
|
+
* Gets the time code in HHMMSS format.
|
|
72
|
+
*/
|
|
73
|
+
get timeCode() { return this._timeCode; }
|
|
74
|
+
/**
|
|
75
|
+
* Gets the date value in YYYY-MM-DD format.
|
|
76
|
+
*/
|
|
77
|
+
get dateValue() { return this._dateValue; }
|
|
78
|
+
/**
|
|
79
|
+
* Gets the time value in HH:mm:ss format.
|
|
80
|
+
*/
|
|
81
|
+
get timeValue() { return this._timeValue; }
|
|
82
|
+
/**
|
|
83
|
+
* Gets whether this DateTime is in the past.
|
|
84
|
+
*/
|
|
85
|
+
get isPast() { return this.isBefore(DateTime.now()); }
|
|
86
|
+
/**
|
|
87
|
+
* Gets whether this DateTime is in the future.
|
|
88
|
+
*/
|
|
89
|
+
get isFuture() { return this.isAfter(DateTime.now()); }
|
|
90
|
+
/**
|
|
91
|
+
* Creates a new DateTime instance.
|
|
92
|
+
*
|
|
93
|
+
* @param data - The DateTime data containing value and zone.
|
|
94
|
+
* @throws Error if the value or zone is invalid.
|
|
95
|
+
*/
|
|
96
|
+
constructor(data) {
|
|
97
|
+
super(data);
|
|
98
|
+
let { value, zone } = data;
|
|
99
|
+
given(value, "value").ensureHasValue().ensureIsString();
|
|
100
|
+
value = value.trim()
|
|
101
|
+
.split("")
|
|
102
|
+
.take(DateTimeFormat.yearMonthDayHourMinuteSecond.length)
|
|
103
|
+
.join("");
|
|
104
|
+
if (value.matchesFormat("####"))
|
|
105
|
+
value = `${value}-01`; // MM
|
|
106
|
+
if (value.matchesFormat("####-##"))
|
|
107
|
+
value = `${value}-01`; // dd
|
|
108
|
+
if (value.matchesFormat("####-##-##"))
|
|
109
|
+
value = `${value} 00`; // HH
|
|
110
|
+
if (value.matchesFormat("####-##-## ##"))
|
|
111
|
+
value = `${value}:00`; // mm
|
|
112
|
+
if (value.matchesFormat("####-##-## ##:##"))
|
|
113
|
+
value = `${value}:00`; // ss
|
|
114
|
+
given(value, "value")
|
|
115
|
+
.ensure(t => t.matchesFormat("####-##-## ##:##:##"), "Invalid format");
|
|
116
|
+
const [date, time] = value.split(" ");
|
|
117
|
+
const dateSplit = date.split("-");
|
|
118
|
+
// const _year = Number.parseInt(dateSplit[0]);
|
|
119
|
+
const month = Number.parseInt(dateSplit[1]);
|
|
120
|
+
const day = Number.parseInt(dateSplit[2]);
|
|
121
|
+
given(month, "month").ensureHasValue().ensureIsNumber().ensure(t => t >= 1 && t <= 12);
|
|
122
|
+
given(day, "day").ensureHasValue().ensureIsNumber().ensure(t => t >= 1 && t <= 31);
|
|
123
|
+
const timeSplit = time.split(":");
|
|
124
|
+
const hour = Number.parseInt(timeSplit[0]);
|
|
125
|
+
const minute = Number.parseInt(timeSplit[1]);
|
|
126
|
+
const second = Number.parseInt(timeSplit[2]);
|
|
127
|
+
given(hour, "hour").ensureHasValue().ensureIsNumber().ensure(t => t >= 0 && t <= 23);
|
|
128
|
+
given(minute, "minute").ensureHasValue().ensureIsNumber().ensure(t => t >= 0 && t <= 59);
|
|
129
|
+
given(second, "second").ensureHasValue().ensureIsNumber().ensure(t => t >= 0 && t <= 59);
|
|
130
|
+
given(zone, "zone").ensureHasValue().ensureIsString();
|
|
131
|
+
zone = zone.trim();
|
|
132
|
+
if (zone.toLowerCase() === "utc")
|
|
133
|
+
zone = zone.toLowerCase();
|
|
134
|
+
DateTime._validateZone(zone);
|
|
135
|
+
const dateTime = LuxonDateTime.fromFormat(value, DateTimeFormat.yearMonthDayHourMinuteSecond, { zone });
|
|
136
|
+
given(data, "data")
|
|
137
|
+
.ensure(_ => dateTime.isValid, `value and zone is invalid (${dateTime.invalidReason}: ${dateTime.invalidExplanation})`);
|
|
138
|
+
this._value = value;
|
|
139
|
+
this._zone = zone;
|
|
140
|
+
this._dateTime = dateTime;
|
|
141
|
+
this._timestamp = this._dateTime.toUnixInteger();
|
|
142
|
+
this._dateCode = dateSplit.join("");
|
|
143
|
+
this._timeCode = timeSplit.join("");
|
|
144
|
+
this._dateValue = date;
|
|
145
|
+
this._timeValue = time;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Sets a fixed timestamp for testing purposes. All calls to DateTime.now() will return this fixed time.
|
|
149
|
+
*
|
|
150
|
+
* @param timestamp - The Unix timestamp in seconds to use as the fixed "now" time.
|
|
151
|
+
* @throws Error if timestamp is not a valid number.
|
|
152
|
+
*/
|
|
153
|
+
static useFixedNow(timestamp) {
|
|
154
|
+
given(timestamp, "timestamp").ensureHasValue().ensureIsNumber();
|
|
155
|
+
DateTime._fixedNow = timestamp;
|
|
156
|
+
DateTime._relativeNow = null;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Sets a relative timestamp for testing purposes. DateTime.now() will return times relative to this base timestamp,
|
|
160
|
+
* advancing as real time advances.
|
|
161
|
+
*
|
|
162
|
+
* @param timestamp - The Unix timestamp in seconds to use as the base "now" time.
|
|
163
|
+
* @throws Error if timestamp is not a valid number.
|
|
164
|
+
*/
|
|
165
|
+
static useRelativeNow(timestamp) {
|
|
166
|
+
given(timestamp, "timestamp").ensureHasValue().ensureIsNumber();
|
|
167
|
+
DateTime._relativeNow = {
|
|
168
|
+
baseTimestamp: timestamp,
|
|
169
|
+
baseRealTime: Date.now()
|
|
170
|
+
};
|
|
171
|
+
DateTime._fixedNow = null;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Resets any fixed or relative "now" time set by useFixedNow or useRelativeNow.
|
|
175
|
+
* DateTime.now() will return the actual current time after calling this method.
|
|
176
|
+
*/
|
|
177
|
+
static resetFixedOrRelativeNow() {
|
|
178
|
+
DateTime._fixedNow = null;
|
|
179
|
+
DateTime._relativeNow = null;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Creates a DateTime instance for the current time.
|
|
183
|
+
*
|
|
184
|
+
* @param zone - The timezone identifier. If not specified, UTC is used.
|
|
185
|
+
* @returns A new DateTime instance representing the current time.
|
|
186
|
+
*/
|
|
187
|
+
static now(zone) {
|
|
188
|
+
given(zone, "zone").ensureIsString();
|
|
189
|
+
// Check if we're using fixed or relative now for testing
|
|
190
|
+
let timestamp = null;
|
|
191
|
+
if (DateTime._fixedNow !== null) {
|
|
192
|
+
timestamp = DateTime._fixedNow;
|
|
193
|
+
}
|
|
194
|
+
else if (DateTime._relativeNow !== null) {
|
|
195
|
+
const elapsedMs = Date.now() - DateTime._relativeNow.baseRealTime;
|
|
196
|
+
timestamp = DateTime._relativeNow.baseTimestamp + Math.floor(elapsedMs / 1000);
|
|
197
|
+
}
|
|
198
|
+
// If we have a timestamp, use it
|
|
199
|
+
if (timestamp !== null) {
|
|
200
|
+
const targetZone = zone ?? "utc";
|
|
201
|
+
return DateTime.createFromTimestamp(timestamp, targetZone);
|
|
202
|
+
}
|
|
203
|
+
// Otherwise, use real current time
|
|
204
|
+
if (zone != null) {
|
|
205
|
+
return new DateTime({
|
|
206
|
+
value: LuxonDateTime.now().setZone(zone).toFormat(DateTimeFormat_DEFAULT),
|
|
207
|
+
zone
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
return new DateTime({
|
|
212
|
+
value: LuxonDateTime.utc().toFormat(DateTimeFormat_DEFAULT),
|
|
213
|
+
zone: "utc"
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Creates a DateTime from a Unix timestamp.
|
|
219
|
+
*
|
|
220
|
+
* @param timestamp - The number of seconds since the Unix epoch.
|
|
221
|
+
* @param zone - The timezone identifier.
|
|
222
|
+
* @returns A new DateTime instance.
|
|
223
|
+
*/
|
|
224
|
+
static createFromTimestamp(timestamp, zone) {
|
|
225
|
+
given(timestamp, "timestamp").ensureHasValue().ensureIsNumber();
|
|
226
|
+
given(zone, "zone").ensureHasValue().ensureIsString();
|
|
227
|
+
const dateTimeString = LuxonDateTime.fromSeconds(timestamp)
|
|
228
|
+
.setZone(zone).toFormat(DateTimeFormat_DEFAULT);
|
|
229
|
+
return new DateTime({
|
|
230
|
+
value: dateTimeString,
|
|
231
|
+
zone
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Creates a DateTime from milliseconds since the Unix epoch.
|
|
236
|
+
*
|
|
237
|
+
* @param milliseconds - The number of milliseconds since the Unix epoch.
|
|
238
|
+
* @param zone - The timezone identifier.
|
|
239
|
+
* @returns A new DateTime instance.
|
|
240
|
+
*/
|
|
241
|
+
static createFromMilliSecondsSinceEpoch(milliseconds, zone) {
|
|
242
|
+
given(milliseconds, "milliseconds").ensureHasValue().ensureIsNumber();
|
|
243
|
+
given(zone, "zone").ensureHasValue().ensureIsString();
|
|
244
|
+
const dateTimeString = LuxonDateTime.fromMillis(milliseconds)
|
|
245
|
+
.setZone(zone).toFormat(DateTimeFormat_DEFAULT);
|
|
246
|
+
return new DateTime({
|
|
247
|
+
value: dateTimeString,
|
|
248
|
+
zone
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Creates a DateTime from date and time codes.
|
|
253
|
+
*
|
|
254
|
+
* @param dateCode - The date code in YYYYMMDD format.
|
|
255
|
+
* @param timeCode - The time code in HHMM format.
|
|
256
|
+
* @param zone - The timezone identifier.
|
|
257
|
+
* @returns A new DateTime instance.
|
|
258
|
+
*/
|
|
259
|
+
static createFromCodes(dateCode, timeCode, zone) {
|
|
260
|
+
given(dateCode, "dateCode").ensureHasValue().ensureIsString()
|
|
261
|
+
.ensure(t => t.matchesFormat("########"));
|
|
262
|
+
given(timeCode, "timeCode").ensureHasValue().ensureIsString()
|
|
263
|
+
.ensure(t => t.matchesFormat("######"));
|
|
264
|
+
given(zone, "zone").ensureHasValue().ensureIsString();
|
|
265
|
+
const dateCodeSplit = dateCode.split("");
|
|
266
|
+
const timeCodeSplit = timeCode.split("");
|
|
267
|
+
const year = dateCodeSplit.take(4).join("");
|
|
268
|
+
const month = dateCodeSplit.skip(4).take(2).join("");
|
|
269
|
+
const day = dateCodeSplit.skip(6).join("");
|
|
270
|
+
const hour = timeCodeSplit.take(2).join("");
|
|
271
|
+
const minute = timeCodeSplit.skip(2).take(2).join("");
|
|
272
|
+
const second = timeCodeSplit.skip(4).join("");
|
|
273
|
+
const dateTimeString = `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
|
274
|
+
return new DateTime({
|
|
275
|
+
value: dateTimeString,
|
|
276
|
+
zone
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Creates a DateTime from date and time values.
|
|
281
|
+
*
|
|
282
|
+
* @param dateValue - The date in YYYY-MM-DD format.
|
|
283
|
+
* @param timeValue - The time in HH:mm format.
|
|
284
|
+
* @param zone - The timezone identifier.
|
|
285
|
+
* @returns A new DateTime instance.
|
|
286
|
+
*/
|
|
287
|
+
static createFromValues(dateValue, timeValue, zone) {
|
|
288
|
+
given(dateValue, "dateValue").ensureHasValue().ensureIsString()
|
|
289
|
+
.ensure(t => t.matchesFormat("####-##-##"));
|
|
290
|
+
given(timeValue, "timeValue").ensureHasValue().ensureIsString()
|
|
291
|
+
.ensure(t => t.matchesFormat("##:##:##"));
|
|
292
|
+
given(zone, "zone").ensureHasValue().ensureIsString();
|
|
293
|
+
const dateTimeString = `${dateValue} ${timeValue}`;
|
|
294
|
+
return new DateTime({
|
|
295
|
+
value: dateTimeString,
|
|
296
|
+
zone
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Returns the earlier of two DateTime instances.
|
|
301
|
+
*
|
|
302
|
+
* @param dateTime1 - The first DateTime instance.
|
|
303
|
+
* @param dateTime2 - The second DateTime instance.
|
|
304
|
+
* @returns The earlier DateTime instance.
|
|
305
|
+
*/
|
|
306
|
+
static min(dateTime1, dateTime2) {
|
|
307
|
+
given(dateTime1, "dateTime1").ensureHasValue().ensureIsType(DateTime);
|
|
308
|
+
given(dateTime2, "dateTime2").ensureHasValue().ensureIsType(DateTime);
|
|
309
|
+
if (dateTime1.valueOf() < dateTime2.valueOf())
|
|
310
|
+
return dateTime1;
|
|
311
|
+
return dateTime2;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Returns the later of two DateTime instances.
|
|
315
|
+
*
|
|
316
|
+
* @param dateTime1 - The first DateTime instance.
|
|
317
|
+
* @param dateTime2 - The second DateTime instance.
|
|
318
|
+
* @returns The later DateTime instance.
|
|
319
|
+
*/
|
|
320
|
+
static max(dateTime1, dateTime2) {
|
|
321
|
+
given(dateTime1, "dateTime1").ensureHasValue().ensureIsType(DateTime);
|
|
322
|
+
given(dateTime2, "dateTime2").ensureHasValue().ensureIsType(DateTime);
|
|
323
|
+
if (dateTime1.valueOf() > dateTime2.valueOf())
|
|
324
|
+
return dateTime1;
|
|
325
|
+
return dateTime2;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Validates if a string matches the DateTime format "yyyy-MM-dd HH:mm".
|
|
329
|
+
*
|
|
330
|
+
* @param value - The string to validate.
|
|
331
|
+
* @returns True if the string matches the format, false otherwise.
|
|
332
|
+
*/
|
|
333
|
+
static validateDateTimeFormat(value, format) {
|
|
334
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
335
|
+
if (value == null || value.isEmptyOrWhiteSpace())
|
|
336
|
+
return false;
|
|
337
|
+
return LuxonDateTime.fromFormat(value, format).isValid;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Validates if a string matches the date format "yyyy-MM-dd".
|
|
341
|
+
*
|
|
342
|
+
* @param value - The string to validate.
|
|
343
|
+
* @returns True if the string matches the format, false otherwise.
|
|
344
|
+
*/
|
|
345
|
+
static validateDateFormat(value) {
|
|
346
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
347
|
+
if (value == null || value.isEmptyOrWhiteSpace())
|
|
348
|
+
return false;
|
|
349
|
+
return LuxonDateTime.fromFormat(value, "yyyy-MM-dd").isValid;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Validates if a string matches the time format "HH:mm".
|
|
353
|
+
*
|
|
354
|
+
* @param value - The string to validate.
|
|
355
|
+
* @returns True if the string matches the format, false otherwise.
|
|
356
|
+
*/
|
|
357
|
+
static validateTimeFormat(value) {
|
|
358
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
359
|
+
if (value == null || value.isEmptyOrWhiteSpace())
|
|
360
|
+
return false;
|
|
361
|
+
return LuxonDateTime.fromFormat(value, "HH:mm").isValid;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Validates if a string is a valid timezone.
|
|
365
|
+
*
|
|
366
|
+
* @param zone - The timezone string to validate.
|
|
367
|
+
* @returns True if the timezone is valid, false otherwise.
|
|
368
|
+
*/
|
|
369
|
+
static validateTimeZone(zone) {
|
|
370
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
371
|
+
if (zone == null || zone.isEmptyOrWhiteSpace())
|
|
372
|
+
return false;
|
|
373
|
+
try {
|
|
374
|
+
DateTime._validateZone(zone);
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
return LuxonDateTime.now().setZone(zone).isValid;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Validates a timezone string.
|
|
383
|
+
*
|
|
384
|
+
* @param zone - The timezone string to validate.
|
|
385
|
+
* @throws Error if the timezone is invalid.
|
|
386
|
+
* @private
|
|
387
|
+
*/
|
|
388
|
+
static _validateZone(zone) {
|
|
389
|
+
zone = zone.trim();
|
|
390
|
+
if (zone.toLowerCase() === "utc")
|
|
391
|
+
return;
|
|
392
|
+
given(zone, "zone")
|
|
393
|
+
.ensureWhen(zone.toLowerCase() === "local", _ => false, "should not use local zone")
|
|
394
|
+
.ensureWhen(zone.toLowerCase().startsWith("utc+"), t => {
|
|
395
|
+
// range is +00:00 to +14:00 (https://en.wikipedia.org/wiki/List_of_UTC_offsets)
|
|
396
|
+
let offset = t.split("+").takeLast().trim();
|
|
397
|
+
if (!offset.contains(":"))
|
|
398
|
+
offset = `${offset}:00`;
|
|
399
|
+
const [hour, minute] = offset.split(":").map(t => TypeHelper.parseNumber(t));
|
|
400
|
+
if (hour == null || minute == null)
|
|
401
|
+
return false;
|
|
402
|
+
return (hour >= 0 && hour < 14 && minute >= 0 && minute < 60)
|
|
403
|
+
|| (hour === 14 && minute === 0);
|
|
404
|
+
}, "Invalid UTC offset for zone")
|
|
405
|
+
.ensureWhen(zone.toLowerCase().startsWith("utc-"), t => {
|
|
406
|
+
// range is -00:00 to -12:00 (https://en.wikipedia.org/wiki/List_of_UTC_offsets)
|
|
407
|
+
let offset = t.split("-").takeLast();
|
|
408
|
+
if (!offset.contains(":"))
|
|
409
|
+
offset = `${offset}:00`;
|
|
410
|
+
const [hour, minute] = offset.split(":").map(t => TypeHelper.parseNumber(t));
|
|
411
|
+
if (hour == null || minute == null)
|
|
412
|
+
return false;
|
|
413
|
+
return hour >= 0 && hour < 12 && minute >= 0 && minute < 60
|
|
414
|
+
|| (hour === 12 && minute === 0);
|
|
415
|
+
}, "Invalid UTC offset for zone");
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Gets the numeric value of this DateTime.
|
|
419
|
+
*
|
|
420
|
+
* @returns The milliseconds since the Unix epoch.
|
|
421
|
+
*/
|
|
422
|
+
valueOf() {
|
|
423
|
+
return this._dateTime.valueOf();
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Compares this DateTime with another for equality.
|
|
427
|
+
*
|
|
428
|
+
* @param value - The DateTime to compare with.
|
|
429
|
+
* @returns True if the DateTime instances are equal, false otherwise.
|
|
430
|
+
*/
|
|
431
|
+
equals(value) {
|
|
432
|
+
given(value, "value").ensureIsType(DateTime);
|
|
433
|
+
if (value == null)
|
|
434
|
+
return false;
|
|
435
|
+
if (value === this)
|
|
436
|
+
return true;
|
|
437
|
+
return value.value === this._value && value.zone === this._zone;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Returns the string representation of this DateTime.
|
|
441
|
+
*
|
|
442
|
+
* @returns The string representation in the format "YYYY-MM-DD HH:mm:ss zone".
|
|
443
|
+
*/
|
|
444
|
+
toString() {
|
|
445
|
+
return `${this._value} ${this._zone}`;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Returns the date and time string.
|
|
449
|
+
*
|
|
450
|
+
* @returns The string in the format "YYYY-MM-DD HH:mm:ss".
|
|
451
|
+
*/
|
|
452
|
+
toStringDateTime() {
|
|
453
|
+
return this._value;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Returns the ISO string representation.
|
|
457
|
+
*
|
|
458
|
+
* @returns The ISO 8601 string representation.
|
|
459
|
+
*/
|
|
460
|
+
toStringISO() {
|
|
461
|
+
return this._dateTime.toISO({ format: "extended", includeOffset: true });
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Formats a DateTime object to a string using the specified format
|
|
465
|
+
* @param dateTime The DateTime object to format
|
|
466
|
+
* @param format The format to use (defaults to yearMonthDayHourMinute: "yyyy-MM-dd HH:mm")
|
|
467
|
+
* @returns The formatted datetime string
|
|
468
|
+
*/
|
|
469
|
+
format(format = DateTimeFormat.yearMonthDayHourMinuteSecond) {
|
|
470
|
+
// For the default format, use the built-in toStringDateTime()
|
|
471
|
+
if (format === DateTimeFormat.yearMonthDayHourMinuteSecond)
|
|
472
|
+
return this.toStringDateTime();
|
|
473
|
+
// For other formats, parse and reformat
|
|
474
|
+
const value = this.toStringDateTime();
|
|
475
|
+
// Parse the value (format: "yyyy-MM-dd HH:mm:ss")
|
|
476
|
+
const [date, time] = value.split(" ");
|
|
477
|
+
const [year, month, day] = date.split("-");
|
|
478
|
+
const [hour, minute, _second] = time.split(":");
|
|
479
|
+
switch (format) {
|
|
480
|
+
case DateTimeFormat.yearMonthDayHourMinute:
|
|
481
|
+
return `${year}-${month}-${day} ${hour}:${minute}`;
|
|
482
|
+
case DateTimeFormat.yearMonthDayHour:
|
|
483
|
+
return `${year}-${month}-${day} ${hour}`;
|
|
484
|
+
case DateTimeFormat.yearMonthDay:
|
|
485
|
+
return `${year}-${month}-${day}`;
|
|
486
|
+
case DateTimeFormat.yearMonth:
|
|
487
|
+
return `${year}-${month}`;
|
|
488
|
+
case DateTimeFormat.year:
|
|
489
|
+
return year;
|
|
490
|
+
default:
|
|
491
|
+
return value;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Formats a DateTime object to a string using extended format capabilities.
|
|
496
|
+
* This method leverages Luxon's full formatting capabilities for more complex formatting needs.
|
|
497
|
+
*
|
|
498
|
+
* @param format - The format string to use. Can be a predefined DateTimeFormatExt or any custom Luxon format string.
|
|
499
|
+
* @returns The formatted datetime string.
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* ```typescript
|
|
503
|
+
* const dt = DateTime.now("America/New_York");
|
|
504
|
+
* dt.formatExt("DD HH:mm:ss"); // "Jul 2, 2023 15:30:20"
|
|
505
|
+
* dt.formatExt("MMMM d, yyyy"); // "July 2, 2023"
|
|
506
|
+
* dt.formatExt("EEEE DD"); // "Friday Jul 2, 2023"
|
|
507
|
+
*
|
|
508
|
+
* export type DateTimeFormatExt =
|
|
509
|
+
"DD HH:mm:ss" // Jul 2, 2023 15:30:20
|
|
510
|
+
| "MMMM d, HH:mm:ss" // Jul 2 15:30:20
|
|
511
|
+
| "DD HH:mm" // Jul 2, 2023 15:30
|
|
512
|
+
| "MMMM d, HH:mm" // Jul 2 15:30
|
|
513
|
+
| "yyyy/LL/dd" // 2023/07/21
|
|
514
|
+
| "yyyy/LL/dd HH:mm:ss"
|
|
515
|
+
| "yyyy/LL/dd HH:mm"
|
|
516
|
+
| "yyyy-MM-dd" // 2023-07-21
|
|
517
|
+
| "HH:mm:ss" // 15:30:20
|
|
518
|
+
| "HH:mm" // 15:30
|
|
519
|
+
| "DDD" // July 21, 2023
|
|
520
|
+
| "DD" // Jul 21, 2023
|
|
521
|
+
| "yyyy-MM" // 2023-07
|
|
522
|
+
| "MMMM yyyy" // July 2023
|
|
523
|
+
| "DDDD" // Sunday, July 9, 2023
|
|
524
|
+
| "EEEE DD" // Friday Aug 4, 2023
|
|
525
|
+
| "LLL yyyy" // Jul 2025
|
|
526
|
+
| "LLLL yyyy" // July 2025
|
|
527
|
+
| "MMMM d" // November 2
|
|
528
|
+
| "LLL d" // Nov 2
|
|
529
|
+
;
|
|
530
|
+
*
|
|
531
|
+
* ```
|
|
532
|
+
*/
|
|
533
|
+
formatExt(format) {
|
|
534
|
+
given(format, "format").ensureHasValue().ensureIsString();
|
|
535
|
+
return this._dateTime.toFormat(format);
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Checks if this DateTime represents the same instant as another.
|
|
539
|
+
*
|
|
540
|
+
* @param value - The DateTime to compare with.
|
|
541
|
+
* @returns True if the DateTime instances represent the same instant, false otherwise.
|
|
542
|
+
*/
|
|
543
|
+
isSame(value) {
|
|
544
|
+
given(value, "value").ensureHasValue().ensureIsType(DateTime);
|
|
545
|
+
return this.valueOf() === value.valueOf();
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Checks if this DateTime is before another.
|
|
549
|
+
*
|
|
550
|
+
* @param value - The DateTime to compare with.
|
|
551
|
+
* @returns True if this DateTime is before the other, false otherwise.
|
|
552
|
+
*/
|
|
553
|
+
isBefore(value) {
|
|
554
|
+
given(value, "value").ensureHasValue().ensureIsType(DateTime);
|
|
555
|
+
return this.valueOf() < value.valueOf();
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Checks if this DateTime is the same or before another.
|
|
559
|
+
*
|
|
560
|
+
* @param value - The DateTime to compare with.
|
|
561
|
+
* @returns True if this DateTime is the same or before the other, false otherwise.
|
|
562
|
+
*/
|
|
563
|
+
isSameOrBefore(value) {
|
|
564
|
+
given(value, "value").ensureHasValue().ensureIsType(DateTime);
|
|
565
|
+
return this.valueOf() <= value.valueOf();
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Checks if this DateTime is after another.
|
|
569
|
+
*
|
|
570
|
+
* @param value - The DateTime to compare with.
|
|
571
|
+
* @returns True if this DateTime is after the other, false otherwise.
|
|
572
|
+
*/
|
|
573
|
+
isAfter(value) {
|
|
574
|
+
given(value, "value").ensureHasValue().ensureIsType(DateTime);
|
|
575
|
+
return this.valueOf() > value.valueOf();
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Checks if this DateTime is the same or after another.
|
|
579
|
+
*
|
|
580
|
+
* @param value - The DateTime to compare with.
|
|
581
|
+
* @returns True if this DateTime is the same or after the other, false otherwise.
|
|
582
|
+
*/
|
|
583
|
+
isSameOrAfter(value) {
|
|
584
|
+
given(value, "value").ensureHasValue().ensureIsType(DateTime);
|
|
585
|
+
return this.valueOf() >= value.valueOf();
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Checks if this DateTime is between two others.
|
|
589
|
+
*
|
|
590
|
+
* @param start - The start DateTime.
|
|
591
|
+
* @param end - The end DateTime.
|
|
592
|
+
* @returns True if this DateTime is between start and end, false otherwise.
|
|
593
|
+
* @throws Error if end is before start.
|
|
594
|
+
*/
|
|
595
|
+
isBetween(start, end) {
|
|
596
|
+
given(start, "start").ensureHasValue().ensureIsType(DateTime);
|
|
597
|
+
given(end, "end").ensureHasValue().ensureIsType(DateTime)
|
|
598
|
+
.ensure(t => t.isSameOrAfter(start), "must be same or after start");
|
|
599
|
+
return this.isSameOrAfter(start) && this.isSameOrBefore(end);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Calculates the time difference between this DateTime and another.
|
|
603
|
+
*
|
|
604
|
+
* @param value - The DateTime to compare with.
|
|
605
|
+
* @returns A Duration representing the time difference.
|
|
606
|
+
*/
|
|
607
|
+
timeDiff(value) {
|
|
608
|
+
given(value, "value").ensureHasValue().ensureIsType(DateTime);
|
|
609
|
+
return Duration.fromMilliSeconds(Math.abs(this.valueOf() - value.valueOf()));
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Calculates the days difference between this DateTime and another.
|
|
613
|
+
*
|
|
614
|
+
* @param value - The DateTime to compare with.
|
|
615
|
+
* @returns The number of days difference.
|
|
616
|
+
*/
|
|
617
|
+
daysDiff(value) {
|
|
618
|
+
given(value, "value").ensureHasValue().ensureIsType(DateTime);
|
|
619
|
+
return Math.abs(Number.parseInt(this._dateTime.diff(value._dateTime, ["days"]).days.toString()));
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Checks if this DateTime is on the same day as another.
|
|
623
|
+
*
|
|
624
|
+
* @param value - The DateTime to compare with.
|
|
625
|
+
* @returns True if the DateTime instances are on the same day, false otherwise.
|
|
626
|
+
*/
|
|
627
|
+
isSameDay(value) {
|
|
628
|
+
given(value, "value").ensureHasValue().ensureIsType(DateTime);
|
|
629
|
+
const daysDiff = this._dateTime.diff(value._dateTime, ["days"]).days;
|
|
630
|
+
return Math.abs(daysDiff) < 1;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Adds a duration to this DateTime. this accounts for shift in DST
|
|
634
|
+
*
|
|
635
|
+
* @param time - The duration to add.
|
|
636
|
+
* @returns A new DateTime instance with the duration added.
|
|
637
|
+
*/
|
|
638
|
+
addTime(time) {
|
|
639
|
+
given(time, "time").ensureHasValue().ensureIsObject().ensureIsInstanceOf(Duration);
|
|
640
|
+
return new DateTime({
|
|
641
|
+
value: this._dateTime.plus({ milliseconds: time.toMilliSeconds() }).toFormat(DateTimeFormat_DEFAULT),
|
|
642
|
+
zone: this._zone
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Subtracts a duration from this DateTime. this accounts for shift in DST
|
|
647
|
+
*
|
|
648
|
+
* @param time - The duration to subtract.
|
|
649
|
+
* @returns A new DateTime instance with the duration subtracted.
|
|
650
|
+
*/
|
|
651
|
+
subtractTime(time) {
|
|
652
|
+
given(time, "time").ensureHasValue().ensureIsObject().ensureIsInstanceOf(Duration);
|
|
653
|
+
return new DateTime({
|
|
654
|
+
value: this._dateTime.minus({ milliseconds: time.toMilliSeconds() }).toFormat(DateTimeFormat_DEFAULT),
|
|
655
|
+
zone: this._zone
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Adds days to this DateTime. this doesn't change time based on DST
|
|
660
|
+
*
|
|
661
|
+
* @param days - The number of days to add.
|
|
662
|
+
* @returns A new DateTime instance with the days added.
|
|
663
|
+
* @throws Error if days is not a positive integer.
|
|
664
|
+
*/
|
|
665
|
+
addDays(days) {
|
|
666
|
+
given(days, "days").ensureHasValue().ensureIsNumber()
|
|
667
|
+
.ensure(t => t >= 0 && Number.isInteger(t), "days should be positive integer");
|
|
668
|
+
return new DateTime({
|
|
669
|
+
value: this._dateTime.plus({ days }).toFormat(DateTimeFormat_DEFAULT),
|
|
670
|
+
zone: this._zone
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Subtracts days from this DateTime. this doesn't change time based on DST
|
|
675
|
+
*
|
|
676
|
+
* @param days - The number of days to subtract.
|
|
677
|
+
* @returns A new DateTime instance with the days subtracted.
|
|
678
|
+
* @throws Error if days is not a positive integer.
|
|
679
|
+
*/
|
|
680
|
+
subtractDays(days) {
|
|
681
|
+
given(days, "days").ensureHasValue().ensureIsNumber()
|
|
682
|
+
.ensure(t => t >= 0 && Number.isInteger(t), "days should be positive integer");
|
|
683
|
+
return new DateTime({
|
|
684
|
+
value: this._dateTime.minus({ days }).toFormat(DateTimeFormat_DEFAULT),
|
|
685
|
+
zone: this._zone
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Gets an array of DateTime instances for each day of the month.
|
|
690
|
+
*
|
|
691
|
+
* @returns An array of DateTime instances, where:
|
|
692
|
+
* - First element is the start of the month (e.g., "2023-06-01 00:00")
|
|
693
|
+
* - Last element is the end of the month (e.g., "2023-06-30 23:59")
|
|
694
|
+
* - Elements in between represent the start of each day (e.g., "2023-06-11 00:00")
|
|
695
|
+
*/
|
|
696
|
+
getDaysOfMonth() {
|
|
697
|
+
const startOfMonth = this._dateTime.startOf("month");
|
|
698
|
+
const endOfMonth = this._dateTime.endOf("month");
|
|
699
|
+
const luxonDays = LuxonInterval.fromDateTimes(startOfMonth, endOfMonth).splitBy({ days: 1 })
|
|
700
|
+
.map((t) => t.start);
|
|
701
|
+
luxonDays[0] = startOfMonth;
|
|
702
|
+
luxonDays[luxonDays.length - 1] = endOfMonth;
|
|
703
|
+
return luxonDays.map(t => new DateTime({
|
|
704
|
+
value: t.toFormat(DateTimeFormat_DEFAULT),
|
|
705
|
+
zone: this._zone
|
|
706
|
+
}));
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Converts this DateTime to a different timezone.
|
|
710
|
+
*
|
|
711
|
+
* @param zone - The target timezone.
|
|
712
|
+
* @returns A new DateTime instance in the specified timezone.
|
|
713
|
+
* @throws Error if the timezone is invalid.
|
|
714
|
+
*/
|
|
715
|
+
convertToZone(zone) {
|
|
716
|
+
given(zone, "zone").ensureHasValue().ensureIsString()
|
|
717
|
+
.ensure(t => DateTime.validateTimeZone(t));
|
|
718
|
+
if (zone === this.zone)
|
|
719
|
+
return this;
|
|
720
|
+
const newDateTime = this._dateTime.setZone(zone).toFormat(DateTimeFormat_DEFAULT);
|
|
721
|
+
return new DateTime({
|
|
722
|
+
value: newDateTime,
|
|
723
|
+
zone
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Checks if this DateTime is within a time range.
|
|
728
|
+
*
|
|
729
|
+
* @param startTimeCode - The start time code in HHMM format.
|
|
730
|
+
* @param endTimeCode - The end time code in HHMM format.
|
|
731
|
+
* @returns True if this DateTime is within the time range, false otherwise.
|
|
732
|
+
* @throws Error if the time codes are invalid or if endTimeCode is before startTimeCode.
|
|
733
|
+
*/
|
|
734
|
+
isWithinTimeRange(startTimeCode, endTimeCode) {
|
|
735
|
+
given(startTimeCode, "startTimeCode").ensureHasValue().ensureIsString()
|
|
736
|
+
.ensure(t => t.matchesFormat("######"))
|
|
737
|
+
.ensure(t => Number.parseInt(t) >= 0 && Number.parseInt(t) <= 235959);
|
|
738
|
+
given(endTimeCode, "endTimeCode").ensureHasValue().ensureIsString()
|
|
739
|
+
.ensure(t => t.matchesFormat("######"))
|
|
740
|
+
.ensure(t => Number.parseInt(t) >= 0 && Number.parseInt(t) <= 235959)
|
|
741
|
+
.ensure(t => Number.parseInt(t) >= Number.parseInt(startTimeCode), "must be >= startTimeCode");
|
|
742
|
+
const startDateTime = DateTime.createFromCodes(this.dateCode, startTimeCode, this.zone);
|
|
743
|
+
const endDateTime = DateTime.createFromCodes(this.dateCode, endTimeCode, this.zone);
|
|
744
|
+
return this.isBetween(startDateTime, endDateTime);
|
|
745
|
+
}
|
|
746
|
+
static {
|
|
747
|
+
__runInitializers(_classThis, _classExtraInitializers);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
return DateTime = _classThis;
|
|
751
|
+
})();
|
|
752
|
+
export { DateTime };
|
|
753
|
+
//# sourceMappingURL=date-time.js.map
|