@naturalcycles/js-lib 14.232.0 → 14.233.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.
@@ -1,4 +1,4 @@
1
- import { LocalDate, localDateRange } from './localDate';
1
+ import { localDate } from './localDate';
2
2
  /**
3
3
  * Class that supports ISO8601 "Time interval" standard that looks like `2022-02-24/2022-03-30`.
4
4
  *
@@ -10,7 +10,7 @@ export class DateInterval {
10
10
  this.end = end;
11
11
  }
12
12
  static of(start, end) {
13
- return new DateInterval(LocalDate.of(start), LocalDate.of(end));
13
+ return new DateInterval(localDate(start), localDate(end));
14
14
  }
15
15
  /**
16
16
  * Parses string like `2022-02-24/2023-03-30` into a DateInterval.
@@ -22,7 +22,7 @@ export class DateInterval {
22
22
  if (!end || !start) {
23
23
  throw new Error(`Cannot parse "${d}" into DateInterval`);
24
24
  }
25
- return new DateInterval(LocalDate.of(start), LocalDate.of(end));
25
+ return new DateInterval(localDate(start), localDate(end));
26
26
  }
27
27
  isSame(d) {
28
28
  return this.cmp(d) === 0;
@@ -45,7 +45,7 @@ export class DateInterval {
45
45
  * Ranges of DateInterval (start, end) are INCLUSIVE.
46
46
  */
47
47
  includes(d, incl = '[]') {
48
- d = LocalDate.of(d);
48
+ d = localDate(d);
49
49
  // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
50
50
  return d.isAfter(this.start, incl[0] === '[') && d.isBefore(this.end, incl[1] === ']');
51
51
  }
@@ -63,13 +63,13 @@ export class DateInterval {
63
63
  return this.start.cmp(d.start) || this.end.cmp(d.end);
64
64
  }
65
65
  getDays(incl = '[]') {
66
- return localDateRange(this.start, this.end, incl, 1, 'day');
66
+ return localDate.range(this.start, this.end, incl, 1, 'day');
67
67
  }
68
68
  /**
69
69
  * Returns an array of LocalDates that are included in the interval.
70
70
  */
71
71
  range(incl = '[]', step = 1, stepUnit = 'day') {
72
- return localDateRange(this.start, this.end, incl, step, stepUnit);
72
+ return localDate.range(this.start, this.end, incl, step, stepUnit);
73
73
  }
74
74
  toString() {
75
75
  return [this.start, this.end].join('/');
@@ -1,10 +1,11 @@
1
1
  import { _assert } from '../error/assert';
2
2
  import { Iterable2 } from '../iter/iterable2';
3
- import { LocalTime } from './localTime';
3
+ import { localTime } from './localTime';
4
4
  const MDAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
5
5
  const DATE_REGEX = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
6
6
  /**
7
- * @experimental
7
+ * LocalDate represents a date without time.
8
+ * It is timezone-independent.
8
9
  */
9
10
  export class LocalDate {
10
11
  constructor($year, $month, $day) {
@@ -12,90 +13,6 @@ export class LocalDate {
12
13
  this.$month = $month;
13
14
  this.$day = $day;
14
15
  }
15
- static create(year, month, day) {
16
- return new LocalDate(year, month, day);
17
- }
18
- /**
19
- * Parses input into LocalDate.
20
- * Input can already be a LocalDate - it is returned as-is in that case.
21
- */
22
- static of(d) {
23
- const t = this.parseOrNull(d);
24
- _assert(t !== null, `Cannot parse "${d}" into LocalDate`, {
25
- input: d,
26
- });
27
- return t;
28
- }
29
- static parseCompact(d) {
30
- const [year, month, day] = [d.slice(0, 4), d.slice(4, 2), d.slice(6, 2)].map(Number);
31
- _assert(day && month && year, `Cannot parse "${d}" into LocalDate`);
32
- return new LocalDate(year, month, day);
33
- }
34
- static fromDate(d) {
35
- return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate());
36
- }
37
- static fromDateUTC(d) {
38
- return new LocalDate(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
39
- }
40
- /**
41
- * Returns null if invalid.
42
- */
43
- static parseOrNull(d) {
44
- if (!d)
45
- return null;
46
- if (d instanceof LocalDate)
47
- return d;
48
- if (d instanceof Date) {
49
- return this.fromDate(d);
50
- }
51
- const matches = typeof d === 'string' && DATE_REGEX.exec(d.slice(0, 10));
52
- if (!matches)
53
- return null;
54
- const year = Number(matches[1]);
55
- const month = Number(matches[2]);
56
- const day = Number(matches[3]);
57
- if (!year ||
58
- !month ||
59
- month < 1 ||
60
- month > 12 ||
61
- !day ||
62
- day < 1 ||
63
- day > this.getMonthLength(year, month)) {
64
- return null;
65
- }
66
- return new LocalDate(year, month, day);
67
- }
68
- static isValid(iso) {
69
- return this.parseOrNull(iso) !== null;
70
- }
71
- static today() {
72
- return this.fromDate(new Date());
73
- }
74
- static todayUTC() {
75
- return this.fromDateUTC(new Date());
76
- }
77
- static sort(items, mutate = false, dir = 'asc') {
78
- const mod = dir === 'desc' ? -1 : 1;
79
- return (mutate ? items : [...items]).sort((a, b) => a.cmp(b) * mod);
80
- }
81
- static earliestOrUndefined(items) {
82
- return items.length ? LocalDate.earliest(items) : undefined;
83
- }
84
- static earliest(items) {
85
- _assert(items.length, 'LocalDate.earliest called on empty array');
86
- return items
87
- .map(i => LocalDate.of(i))
88
- .reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
89
- }
90
- static latestOrUndefined(items) {
91
- return items.length ? LocalDate.latest(items) : undefined;
92
- }
93
- static latest(items) {
94
- _assert(items.length, 'LocalDate.latest called on empty array');
95
- return items
96
- .map(i => LocalDate.of(i))
97
- .reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
98
- }
99
16
  get(unit) {
100
17
  return unit === 'year' ? this.$year : unit === 'month' ? this.$month : this.$day;
101
18
  }
@@ -125,7 +42,7 @@ export class LocalDate {
125
42
  return (this.toDate().getDay() || 7);
126
43
  }
127
44
  isSame(d) {
128
- d = LocalDate.of(d);
45
+ d = localDate.of(d);
129
46
  return this.$day === d.$day && this.$month === d.$month && this.$year === d.$year;
130
47
  }
131
48
  isBefore(d, inclusive = false) {
@@ -162,13 +79,13 @@ export class LocalDate {
162
79
  * Third argument allows to override "today".
163
80
  */
164
81
  isOlderThan(n, unit, today) {
165
- return this.isBefore(LocalDate.of(today || new Date()).plus(-n, unit));
82
+ return this.isBefore(localDate.of(today || new Date()).plus(-n, unit));
166
83
  }
167
84
  /**
168
85
  * Checks if this localDate is same or older (<=) than "today" by X units.
169
86
  */
170
87
  isSameOrOlderThan(n, unit, today) {
171
- return this.isSameOrBefore(LocalDate.of(today || new Date()).plus(-n, unit));
88
+ return this.isSameOrBefore(localDate.of(today || new Date()).plus(-n, unit));
172
89
  }
173
90
  /**
174
91
  * Checks if this localDate is younger (>) than "today" by X units.
@@ -180,13 +97,13 @@ export class LocalDate {
180
97
  * Third argument allows to override "today".
181
98
  */
182
99
  isYoungerThan(n, unit, today) {
183
- return this.isAfter(LocalDate.of(today || new Date()).plus(-n, unit));
100
+ return this.isAfter(localDate.of(today || new Date()).plus(-n, unit));
184
101
  }
185
102
  /**
186
103
  * Checks if this localDate is same or younger (>=) than "today" by X units.
187
104
  */
188
105
  isSameOrYoungerThan(n, unit, today) {
189
- return this.isSameOrAfter(LocalDate.of(today || new Date()).plus(-n, unit));
106
+ return this.isSameOrAfter(localDate.of(today || new Date()).plus(-n, unit));
190
107
  }
191
108
  /**
192
109
  * Returns 1 if this > d
@@ -194,7 +111,7 @@ export class LocalDate {
194
111
  * returns -1 if this < d
195
112
  */
196
113
  cmp(d) {
197
- d = LocalDate.of(d);
114
+ d = localDate.of(d);
198
115
  if (this.$year < d.$year)
199
116
  return -1;
200
117
  if (this.$year > d.$year)
@@ -221,7 +138,7 @@ export class LocalDate {
221
138
  * a.diff(b) means "a minus b"
222
139
  */
223
140
  diff(d, unit) {
224
- d = LocalDate.of(d);
141
+ d = localDate.of(d);
225
142
  const sign = this.cmp(d);
226
143
  if (!sign)
227
144
  return 0;
@@ -232,8 +149,8 @@ export class LocalDate {
232
149
  if (big.$month < small.$month ||
233
150
  (big.$month === small.$month &&
234
151
  big.$day < small.$day &&
235
- !(big.$day === LocalDate.getMonthLength(big.$year, big.$month) &&
236
- small.$day === LocalDate.getMonthLength(small.$year, small.$month)))) {
152
+ !(big.$day === localDate.getMonthLength(big.$year, big.$month) &&
153
+ small.$day === localDate.getMonthLength(small.$year, small.$month)))) {
237
154
  years--;
238
155
  }
239
156
  return years * sign || 0;
@@ -241,7 +158,7 @@ export class LocalDate {
241
158
  if (unit === 'month') {
242
159
  let months = (big.$year - small.$year) * 12 + (big.$month - small.$month);
243
160
  if (big.$day < small.$day) {
244
- const bigMonthLen = LocalDate.getMonthLength(big.$year, big.$month);
161
+ const bigMonthLen = localDate.getMonthLength(big.$year, big.$month);
245
162
  if (big.$day !== bigMonthLen || small.$day < bigMonthLen) {
246
163
  months--;
247
164
  }
@@ -253,16 +170,16 @@ export class LocalDate {
253
170
  // If small date is after 1st of March - next year's "leapness" should be used
254
171
  const offsetYear = small.$month >= 3 ? 1 : 0;
255
172
  for (let year = small.$year; year < big.$year; year++) {
256
- days += LocalDate.getYearLength(year + offsetYear);
173
+ days += localDate.getYearLength(year + offsetYear);
257
174
  }
258
175
  if (small.$month < big.$month) {
259
176
  for (let month = small.$month; month < big.$month; month++) {
260
- days += LocalDate.getMonthLength(big.$year, month);
177
+ days += localDate.getMonthLength(big.$year, month);
261
178
  }
262
179
  }
263
180
  else if (big.$month < small.$month) {
264
181
  for (let month = big.$month; month < small.$month; month++) {
265
- days -= LocalDate.getMonthLength(big.$year, month);
182
+ days -= localDate.getMonthLength(big.$year, month);
266
183
  }
267
184
  }
268
185
  if (unit === 'week') {
@@ -303,11 +220,11 @@ export class LocalDate {
303
220
  $year -= 1;
304
221
  $month += 12;
305
222
  }
306
- $day += LocalDate.getMonthLength($year, $month);
223
+ $day += localDate.getMonthLength($year, $month);
307
224
  }
308
225
  }
309
226
  else {
310
- let monLen = LocalDate.getMonthLength($year, $month);
227
+ let monLen = localDate.getMonthLength($year, $month);
311
228
  if (unit !== 'day') {
312
229
  if ($day > monLen) {
313
230
  // Case of 2022-05-31 plus 1 month should be 2022-06-30, not 31
@@ -322,7 +239,7 @@ export class LocalDate {
322
239
  $year += 1;
323
240
  $month -= 12;
324
241
  }
325
- monLen = LocalDate.getMonthLength($year, $month);
242
+ monLen = localDate.getMonthLength($year, $month);
326
243
  }
327
244
  }
328
245
  }
@@ -341,35 +258,24 @@ export class LocalDate {
341
258
  if (unit === 'day')
342
259
  return this;
343
260
  if (unit === 'month')
344
- return LocalDate.create(this.$year, this.$month, 1);
261
+ return new LocalDate(this.$year, this.$month, 1);
345
262
  // year
346
- return LocalDate.create(this.$year, 1, 1);
263
+ return new LocalDate(this.$year, 1, 1);
347
264
  }
348
265
  endOf(unit) {
349
266
  if (unit === 'day')
350
267
  return this;
351
268
  if (unit === 'month')
352
- return LocalDate.create(this.$year, this.$month, LocalDate.getMonthLength(this.$year, this.$month));
269
+ return new LocalDate(this.$year, this.$month, localDate.getMonthLength(this.$year, this.$month));
353
270
  // year
354
- return LocalDate.create(this.$year, 12, 31);
271
+ return new LocalDate(this.$year, 12, 31);
355
272
  }
356
273
  /**
357
274
  * Returns how many days are in the current month.
358
275
  * E.g 31 for January.
359
276
  */
360
277
  daysInMonth() {
361
- return LocalDate.getMonthLength(this.$year, this.$month);
362
- }
363
- static getYearLength(year) {
364
- return this.isLeapYear(year) ? 366 : 365;
365
- }
366
- static getMonthLength(year, month) {
367
- if (month === 2)
368
- return this.isLeapYear(year) ? 29 : 28;
369
- return MDAYS[month];
370
- }
371
- static isLeapYear(year) {
372
- return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
278
+ return localDate.getMonthLength(this.$year, this.$month);
373
279
  }
374
280
  clone() {
375
281
  return new LocalDate(this.$year, this.$month, this.$day);
@@ -388,33 +294,45 @@ export class LocalDate {
388
294
  * Unlike normal `.toDate` that uses browser's timezone by default.
389
295
  */
390
296
  toDateInUTC() {
391
- return new Date(this.toISODateTimeUTC());
297
+ return new Date(this.toISODateTimeInUTC());
392
298
  }
299
+ /**
300
+ * Converts LocalDate to LocalTime with 0 hours, 0 minutes, 0 seconds.
301
+ * LocalTime's Date will be in local timezone.
302
+ */
393
303
  toLocalTime() {
394
- return LocalTime.of(this.toDate());
304
+ return localTime.of(this.toDate());
395
305
  }
306
+ /**
307
+ * Returns e.g: `1984-06-21`
308
+ */
396
309
  toISODate() {
397
- return this.toString();
310
+ return [
311
+ String(this.$year).padStart(4, '0'),
312
+ String(this.$month).padStart(2, '0'),
313
+ String(this.$day).padStart(2, '0'),
314
+ ].join('-');
398
315
  }
399
316
  /**
400
- * Returns e.g: `1984-06-21T17:56:21`
317
+ * Returns e.g: `1984-06-21T00:00:00`
318
+ * Hours, minutes and seconds are 0.
401
319
  */
402
320
  toISODateTime() {
403
- return this.toString() + 'T00:00:00';
321
+ return this.toISODate() + 'T00:00:00';
404
322
  }
405
323
  /**
406
- * Returns e.g: `1984-06-21T17:56:21Z` (notice the Z at the end, which indicates UTC)
324
+ * Returns e.g: `1984-06-21T00:00:00Z` (notice the Z at the end, which indicates UTC).
325
+ * Hours, minutes and seconds are 0.
407
326
  */
408
- toISODateTimeUTC() {
327
+ toISODateTimeInUTC() {
409
328
  return this.toISODateTime() + 'Z';
410
329
  }
411
330
  toString() {
412
- return [
413
- String(this.$year).padStart(4, '0'),
414
- String(this.$month).padStart(2, '0'),
415
- String(this.$day).padStart(2, '0'),
416
- ].join('-');
331
+ return this.toISODate();
417
332
  }
333
+ /**
334
+ * Returns e.g: `19840621`
335
+ */
418
336
  toStringCompact() {
419
337
  return [
420
338
  String(this.$year).padStart(4, '0'),
@@ -422,18 +340,26 @@ export class LocalDate {
422
340
  String(this.$day).padStart(2, '0'),
423
341
  ].join('');
424
342
  }
343
+ /**
344
+ * Returns e.g: `1984-06`
345
+ */
425
346
  toMonthId() {
426
- return this.toString().slice(0, 7);
347
+ return this.toISODate().slice(0, 7);
427
348
  }
428
- // May be not optimal, as LocalTime better suits it
349
+ /**
350
+ * Returns unix timestamp of 00:00:00 of that date (in UTC, because unix timestamp always reflects UTC).
351
+ */
429
352
  unix() {
430
353
  return Math.floor(this.toDate().valueOf() / 1000);
431
354
  }
355
+ /**
356
+ * Same as .unix(), but in milliseconds.
357
+ */
432
358
  unixMillis() {
433
359
  return this.toDate().valueOf();
434
360
  }
435
361
  toJSON() {
436
- return this.toString();
362
+ return this.toISODate();
437
363
  }
438
364
  format(fmt) {
439
365
  if (fmt instanceof Intl.DateTimeFormat) {
@@ -442,71 +368,209 @@ export class LocalDate {
442
368
  return fmt(this);
443
369
  }
444
370
  }
445
- export function localDateRange(min, max, incl = '[)', step = 1, stepUnit = 'day') {
446
- return localDateRangeIterable(min, max, incl, step, stepUnit).toArray();
447
- }
448
- /**
449
- * Experimental, returns the range as Iterable2.
450
- */
451
- export function localDateRangeIterable(min, max, incl = '[)', step = 1, stepUnit = 'day') {
452
- if (stepUnit === 'week') {
453
- step *= 7;
454
- stepUnit = 'day';
455
- }
456
- const $min = LocalDate.of(min).startOf(stepUnit);
457
- const $max = LocalDate.of(max).startOf(stepUnit);
458
- let value = $min;
459
- // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
460
- if (value.isAfter($min, incl[0] === '[')) {
461
- // ok
462
- }
463
- else {
464
- value.plus(1, stepUnit, true);
465
- }
466
- const rightInclusive = incl[1] === ']';
467
- return Iterable2.of({
468
- *[Symbol.iterator]() {
469
- while (value.isBefore($max, rightInclusive)) {
470
- yield value;
471
- // We don't mutate, because we already returned `current`
472
- // in the previous iteration
473
- value = value.plus(step, stepUnit);
474
- }
475
- },
476
- });
477
- }
478
- /**
479
- * Convenience wrapper around `LocalDate.of`
480
- */
481
- export function localDate(d) {
482
- return LocalDate.of(d);
483
- }
484
- /**
485
- * Convenience wrapper around `LocalDate.today`
486
- */
487
- export function localDateToday() {
488
- return LocalDate.today();
489
- }
490
- /**
491
- * Creates a LocalDate from the input, unless it's falsy - then returns undefined.
492
- *
493
- * `localDate` function will instead return LocalDate of today for falsy input.
494
- */
495
- export function localDateOrUndefined(d) {
496
- return d ? LocalDate.of(d) : undefined;
497
- }
498
- /**
499
- * Creates a LocalDate from the input, unless it's falsy - then returns LocalDate.today.
500
- */
501
- export function localDateOrToday(d) {
502
- return d ? LocalDate.of(d) : LocalDate.today();
371
+ class LocalDateFactory {
372
+ /**
373
+ * Create LocalDate from year, month and day components.
374
+ */
375
+ create(year, month, day) {
376
+ return new LocalDate(year, month, day);
377
+ }
378
+ /**
379
+ * Create LocalDate from LocalDateInput.
380
+ * Input can already be a LocalDate - it is returned as-is in that case.
381
+ * String - will be parsed as yyyy-mm-dd.
382
+ * Date - will be converted to LocalDate (as-is, in whatever timezone it is - local or UTC).
383
+ * No other formats are supported.
384
+ *
385
+ * Will throw if it fails to parse/construct LocalDate.
386
+ */
387
+ of(d) {
388
+ const t = this.parseOrNull(d);
389
+ _assert(t !== null, `Cannot parse "${d}" into LocalDate`, {
390
+ input: d,
391
+ });
392
+ return t;
393
+ }
394
+ /**
395
+ * Tries to construct LocalDate from LocalDateInput, returns null otherwise.
396
+ * Does not throw (returns null instead).
397
+ */
398
+ parseOrNull(d) {
399
+ if (!d)
400
+ return null;
401
+ if (d instanceof LocalDate)
402
+ return d;
403
+ if (d instanceof Date) {
404
+ return this.fromDate(d);
405
+ }
406
+ const matches = typeof d === 'string' && DATE_REGEX.exec(d.slice(0, 10));
407
+ if (!matches)
408
+ return null;
409
+ const year = Number(matches[1]);
410
+ const month = Number(matches[2]);
411
+ const day = Number(matches[3]);
412
+ if (!year ||
413
+ !month ||
414
+ month < 1 ||
415
+ month > 12 ||
416
+ !day ||
417
+ day < 1 ||
418
+ day > this.getMonthLength(year, month)) {
419
+ return null;
420
+ }
421
+ return new LocalDate(year, month, day);
422
+ }
423
+ /**
424
+ * Parses "compact iso8601 format", e.g `19840621` into LocalDate.
425
+ * Throws if it fails to do so.
426
+ */
427
+ parseCompact(d) {
428
+ const [year, month, day] = [d.slice(0, 4), d.slice(4, 2), d.slice(6, 2)].map(Number);
429
+ _assert(day && month && year, `Cannot parse "${d}" into LocalDate`);
430
+ return new LocalDate(year, month, day);
431
+ }
432
+ getYearLength(year) {
433
+ return this.isLeapYear(year) ? 366 : 365;
434
+ }
435
+ getMonthLength(year, month) {
436
+ if (month === 2)
437
+ return this.isLeapYear(year) ? 29 : 28;
438
+ return MDAYS[month];
439
+ }
440
+ isLeapYear(year) {
441
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
442
+ }
443
+ /**
444
+ * Constructs LocalDate from Date.
445
+ * Takes Date as-is, in its timezone - local or UTC.
446
+ */
447
+ fromDate(d) {
448
+ return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate());
449
+ }
450
+ /**
451
+ * Constructs LocalDate from Date.
452
+ * Takes Date's year/month/day components in UTC, using getUTCFullYear, getUTCMonth, getUTCDate.
453
+ */
454
+ fromDateInUTC(d) {
455
+ return new LocalDate(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
456
+ }
457
+ /**
458
+ * Returns true if isoString is a valid iso8601 string like `yyyy-mm-dd`.
459
+ */
460
+ isValid(isoString) {
461
+ return this.parseOrNull(isoString) !== null;
462
+ }
463
+ /**
464
+ * Creates LocalDate that represents `today` (in local timezone).
465
+ */
466
+ today() {
467
+ return this.fromDate(new Date());
468
+ }
469
+ /**
470
+ * Creates LocalDate that represents `today` in UTC.
471
+ */
472
+ todayInUTC() {
473
+ return this.fromDateInUTC(new Date());
474
+ }
475
+ /**
476
+ * Sorts an array of LocalDates in `dir` order (ascending by default).
477
+ */
478
+ sort(items, dir = 'asc', mutate = false) {
479
+ const mod = dir === 'desc' ? -1 : 1;
480
+ return (mutate ? items : [...items]).sort((a, b) => a.cmp(b) * mod);
481
+ }
482
+ /**
483
+ * Returns the earliest (min) LocalDate from the array, or undefined if the array is empty.
484
+ */
485
+ minOrUndefined(items) {
486
+ return items.length ? this.min(items) : undefined;
487
+ }
488
+ /**
489
+ * Returns the earliest LocalDate from the array.
490
+ * Throws if the array is empty.
491
+ */
492
+ min(items) {
493
+ _assert(items.length, 'localDate.min called on empty array');
494
+ return items.map(i => this.of(i)).reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
495
+ }
496
+ /**
497
+ * Returns the latest (max) LocalDate from the array, or undefined if the array is empty.
498
+ */
499
+ maxOrUndefined(items) {
500
+ return items.length ? this.max(items) : undefined;
501
+ }
502
+ /**
503
+ * Returns the latest LocalDate from the array.
504
+ * Throws if the array is empty.
505
+ */
506
+ max(items) {
507
+ _assert(items.length, 'localDate.max called on empty array');
508
+ return items.map(i => this.of(i)).reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
509
+ }
510
+ /**
511
+ * Returns the range (array) of LocalDates between min and max.
512
+ * By default, min is included, max is excluded.
513
+ */
514
+ range(min, max, incl = '[)', step = 1, stepUnit = 'day') {
515
+ return this.rangeIterable(min, max, incl, step, stepUnit).toArray();
516
+ }
517
+ /**
518
+ * Returns the Iterable2 of LocalDates between min and max.
519
+ * By default, min is included, max is excluded.
520
+ */
521
+ rangeIterable(min, max, incl = '[)', step = 1, stepUnit = 'day') {
522
+ if (stepUnit === 'week') {
523
+ step *= 7;
524
+ stepUnit = 'day';
525
+ }
526
+ const $min = this.of(min).startOf(stepUnit);
527
+ const $max = this.of(max).startOf(stepUnit);
528
+ let value = $min;
529
+ // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
530
+ if (value.isAfter($min, incl[0] === '[')) {
531
+ // ok
532
+ }
533
+ else {
534
+ value.plus(1, stepUnit, true);
535
+ }
536
+ const rightInclusive = incl[1] === ']';
537
+ return Iterable2.of({
538
+ *[Symbol.iterator]() {
539
+ while (value.isBefore($max, rightInclusive)) {
540
+ yield value;
541
+ // We don't mutate, because we already returned `current`
542
+ // in the previous iteration
543
+ value = value.plus(step, stepUnit);
544
+ }
545
+ },
546
+ });
547
+ }
548
+ /**
549
+ * Creates a LocalDate from the input, unless it's falsy - then returns undefined.
550
+ *
551
+ * Similar to `localDate.orToday`, but that will instead return Today on falsy input.
552
+ */
553
+ orUndefined(d) {
554
+ return d ? this.of(d) : undefined;
555
+ }
556
+ /**
557
+ * Creates a LocalDate from the input, unless it's falsy - then returns localDate.today.
558
+ */
559
+ orToday(d) {
560
+ return d ? this.of(d) : this.today();
561
+ }
503
562
  }
563
+ const localDateFactory = new LocalDateFactory();
564
+ // export const localDate = Object.assign((d: LocalDateInput) => {
565
+ // return localDateFactory.of(d)
566
+ // }, localDateFactory) as LocalDateFn
567
+ export const localDate = localDateFactory.of.bind(localDateFactory);
568
+ // The line below is the blackest of black magic I have ever written in 2024.
569
+ // And probably 2023 as well.
570
+ Object.setPrototypeOf(localDate, localDateFactory);
504
571
  /**
505
572
  Convenience function to return current today's IsoDateString representation, e.g `2024-06-21`
506
573
  */
507
574
  export function todayString() {
508
- // It was benchmarked to be faster than by concatenating individual Date components
509
- // return new Date().toISOString().slice(0, 10)
510
- // But, toISOString always returns the date in UTC, so in the Browser it would give unexpected result!
511
- return LocalDate.fromDate(new Date()).toString();
575
+ return localDate.fromDate(new Date()).toISODate();
512
576
  }