@naturalcycles/js-lib 14.243.1 → 14.245.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.
Files changed (42) hide show
  1. package/dist/datetime/dateInterval.d.ts +1 -1
  2. package/dist/datetime/dateInterval.js +1 -1
  3. package/dist/datetime/localDate.d.ts +75 -51
  4. package/dist/datetime/localDate.js +256 -200
  5. package/dist/datetime/localTime.d.ts +88 -56
  6. package/dist/datetime/localTime.js +277 -176
  7. package/dist/datetime/timeInterval.d.ts +2 -2
  8. package/dist/datetime/timeInterval.js +2 -2
  9. package/dist/env/buildInfo.js +2 -2
  10. package/dist/error/error.util.d.ts +1 -1
  11. package/dist/index.d.ts +37 -37
  12. package/dist/index.js +37 -37
  13. package/dist/json-schema/from-data/generateJsonSchemaFromData.d.ts +1 -1
  14. package/dist/json-schema/jsonSchemaBuilder.d.ts +1 -1
  15. package/dist/object/object.util.d.ts +1 -1
  16. package/dist/time/time.util.js +4 -2
  17. package/dist/zod/zod.util.d.ts +1 -1
  18. package/dist-esm/datetime/dateInterval.js +1 -1
  19. package/dist-esm/datetime/localDate.js +256 -199
  20. package/dist-esm/datetime/localTime.js +277 -175
  21. package/dist-esm/datetime/timeInterval.js +2 -2
  22. package/dist-esm/decorators/logMethod.decorator.js +1 -1
  23. package/dist-esm/env/buildInfo.js +2 -2
  24. package/dist-esm/index.js +37 -37
  25. package/dist-esm/json-schema/jsonSchemaBuilder.js +1 -1
  26. package/dist-esm/time/time.util.js +4 -2
  27. package/package.json +1 -1
  28. package/src/datetime/dateInterval.ts +2 -2
  29. package/src/datetime/localDate.ts +259 -215
  30. package/src/datetime/localTime.ts +277 -209
  31. package/src/datetime/timeInterval.ts +4 -4
  32. package/src/decorators/logMethod.decorator.ts +1 -1
  33. package/src/define.ts +1 -1
  34. package/src/env/buildInfo.ts +2 -2
  35. package/src/error/error.util.ts +3 -3
  36. package/src/http/fetcher.ts +1 -1
  37. package/src/index.ts +37 -37
  38. package/src/json-schema/from-data/generateJsonSchemaFromData.ts +1 -1
  39. package/src/json-schema/jsonSchemaBuilder.ts +2 -2
  40. package/src/object/object.util.ts +1 -1
  41. package/src/time/time.util.ts +4 -1
  42. package/src/zod/zod.util.ts +1 -1
@@ -19,10 +19,22 @@ const SECONDS_IN_DAY = 86400;
19
19
  // const MILLISECONDS_IN_DAY = 86400000
20
20
  // const MILLISECONDS_IN_MINUTE = 60000
21
21
  const VALID_DAYS_OF_WEEK = new Set([1, 2, 3, 4, 5, 6, 7]);
22
- // It supports 2 forms:
23
- // 1. 2023-03-03
24
- // 2. 2023-03-03T05:10:02
25
- const DATE_TIME_REGEX = /^(\d{4})-(\d{2})-(\d{2})([T\s](\d{2}):(\d{2}):(\d{2}))?/;
22
+ /**
23
+ * It supports 2 forms:
24
+ * 1. 2023-03-03
25
+ * 2. 2023-03-03T05:10:02
26
+ * // todo: make it even looser, like Day.js
27
+ */
28
+ const DATE_TIME_REGEX_LOOSE = /^(\d{4})-(\d{2})-(\d{2})([Tt\s](\d{2}):?(\d{2})?:?(\d{2})?)?/;
29
+ /**
30
+ * Supports 2 forms:
31
+ * 1. 2023-03-03
32
+ * 2. 2023-03-03T05:10:02
33
+ * Ok, now it allows arbitrary stuff after `:ss`, to allow millis/timezone info,
34
+ * but it will not take it into account.
35
+ */
36
+ const DATE_TIME_REGEX_STRICT = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/;
37
+ const DATE_REGEX_STRICT = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
26
38
  export class LocalTime {
27
39
  constructor($date) {
28
40
  this.$date = $date;
@@ -31,14 +43,14 @@ export class LocalTime {
31
43
  * Returns [cloned] LocalTime that is based on the same unixtimestamp, but in UTC timezone.
32
44
  * Opposite of `.local()` method.
33
45
  */
34
- utc() {
46
+ toUTC() {
35
47
  return new LocalTime(new Date(this.$date.toISOString()));
36
48
  }
37
49
  /**
38
50
  * Returns [cloned] LocalTime that is based on the same unixtimestamp, but in local timezone.
39
51
  * Opposite of `.utc()` method.
40
52
  */
41
- local() {
53
+ toLocal() {
42
54
  return new LocalTime(new Date(this.$date.getTime()));
43
55
  }
44
56
  /**
@@ -160,34 +172,58 @@ export class LocalTime {
160
172
  }
161
173
  return t;
162
174
  }
163
- year(v) {
164
- return v === undefined ? this.get('year') : this.set('year', v);
175
+ get year() {
176
+ return this.$date.getFullYear();
165
177
  }
166
- month(v) {
167
- return v === undefined ? this.get('month') : this.set('month', v);
178
+ setYear(v) {
179
+ return this.set('year', v);
168
180
  }
169
- week(v) {
170
- return v === undefined ? getWeek(this.$date) : this.set('week', v);
181
+ get month() {
182
+ return this.$date.getMonth() + 1;
171
183
  }
172
- day(v) {
173
- return v === undefined ? this.get('day') : this.set('day', v);
184
+ setMonth(v) {
185
+ return this.set('month', v);
174
186
  }
175
- dayOfWeek(v) {
176
- const dow = (this.$date.getDay() || 7);
177
- if (v === undefined) {
178
- return dow;
179
- }
180
- _assert(VALID_DAYS_OF_WEEK.has(v), `Invalid dayOfWeek: ${v}`);
181
- return this.plus(v - dow, 'day');
187
+ get week() {
188
+ return getWeek(this.$date);
189
+ }
190
+ setWeek(v) {
191
+ return this.set('week', v);
192
+ }
193
+ get day() {
194
+ return this.$date.getDate();
195
+ }
196
+ setDay(v) {
197
+ return this.set('day', v);
198
+ }
199
+ get hour() {
200
+ return this.$date.getHours();
182
201
  }
183
- hour(v) {
184
- return v === undefined ? this.get('hour') : this.set('hour', v);
202
+ setHour(v) {
203
+ return this.set('hour', v);
185
204
  }
186
- minute(v) {
187
- return v === undefined ? this.get('minute') : this.set('minute', v);
205
+ get minute() {
206
+ return this.$date.getMinutes();
188
207
  }
189
- second(v) {
190
- return v === undefined ? this.get('second') : this.set('second', v);
208
+ setMinute(v) {
209
+ return this.set('minute', v);
210
+ }
211
+ get second() {
212
+ return this.$date.getSeconds();
213
+ }
214
+ setSecond(v) {
215
+ return this.set('second', v);
216
+ }
217
+ /**
218
+ * Based on ISO: 1-7 is Mon-Sun.
219
+ */
220
+ get dayOfWeek() {
221
+ return (this.$date.getDay() || 7);
222
+ }
223
+ setDayOfWeek(v) {
224
+ _assert(VALID_DAYS_OF_WEEK.has(v), `Invalid dayOfWeek: ${v}`);
225
+ const dow = this.$date.getDay() || 7;
226
+ return this.plus(v - dow, 'day');
191
227
  }
192
228
  setComponents(c, mutate = false) {
193
229
  const d = mutate ? this.$date : new Date(this.$date);
@@ -255,7 +291,7 @@ export class LocalTime {
255
291
  }
256
292
  if (unit === 'year' || unit === 'month') {
257
293
  const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate);
258
- return mutate ? this : localTime.of(d);
294
+ return mutate ? this : localTime.fromInput(d);
259
295
  }
260
296
  return this.set(unit, this.get(unit) + num, mutate);
261
297
  }
@@ -266,7 +302,7 @@ export class LocalTime {
266
302
  return Math.abs(this.diff(other, unit));
267
303
  }
268
304
  diff(other, unit) {
269
- const date2 = localTime.of(other).$date;
305
+ const date2 = localTime.fromInput(other).$date;
270
306
  const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000;
271
307
  if (!secDiff)
272
308
  return 0;
@@ -354,32 +390,32 @@ export class LocalTime {
354
390
  * Returns how many days are in the current month.
355
391
  * E.g 31 for January.
356
392
  */
357
- daysInMonth() {
393
+ get daysInMonth() {
358
394
  return localDate.getMonthLength(this.$date.getFullYear(), this.$date.getMonth() + 1);
359
395
  }
360
396
  isSame(d) {
361
- return this.cmp(d) === 0;
397
+ return this.compare(d) === 0;
362
398
  }
363
399
  isBefore(d, inclusive = false) {
364
- const r = this.cmp(d);
400
+ const r = this.compare(d);
365
401
  return r === -1 || (r === 0 && inclusive);
366
402
  }
367
403
  isSameOrBefore(d) {
368
- return this.cmp(d) <= 0;
404
+ return this.compare(d) <= 0;
369
405
  }
370
406
  isAfter(d, inclusive = false) {
371
- const r = this.cmp(d);
407
+ const r = this.compare(d);
372
408
  return r === 1 || (r === 0 && inclusive);
373
409
  }
374
410
  isSameOrAfter(d) {
375
- return this.cmp(d) >= 0;
411
+ return this.compare(d) >= 0;
376
412
  }
377
413
  isBetween(min, max, incl = '[)') {
378
- let r = this.cmp(min);
414
+ let r = this.compare(min);
379
415
  // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
380
416
  if (r < 0 || (r === 0 && incl[0] === '('))
381
417
  return false;
382
- r = this.cmp(max);
418
+ r = this.compare(max);
383
419
  if (r > 0 || (r === 0 && incl[1] === ')'))
384
420
  return false;
385
421
  return true;
@@ -394,13 +430,13 @@ export class LocalTime {
394
430
  * Third argument allows to override "now".
395
431
  */
396
432
  isOlderThan(n, unit, now) {
397
- return this.isBefore(localTime.of(now ?? new Date()).plus(-n, unit));
433
+ return this.isBefore(localTime.fromInput(now ?? new Date()).plus(-n, unit));
398
434
  }
399
435
  /**
400
436
  * Checks if this localTime is same or older (<=) than "now" by X units.
401
437
  */
402
438
  isSameOrOlderThan(n, unit, now) {
403
- return this.isSameOrBefore(localTime.of(now ?? new Date()).plus(-n, unit));
439
+ return this.isSameOrBefore(localTime.fromInput(now ?? new Date()).plus(-n, unit));
404
440
  }
405
441
  /**
406
442
  * Checks if this localTime is younger (>) than "now" by X units.
@@ -412,13 +448,13 @@ export class LocalTime {
412
448
  * Third argument allows to override "now".
413
449
  */
414
450
  isYoungerThan(n, unit, now) {
415
- return this.isAfter(localTime.of(now ?? new Date()).plus(-n, unit));
451
+ return this.isAfter(localTime.fromInput(now ?? new Date()).plus(-n, unit));
416
452
  }
417
453
  /**
418
454
  * Checks if this localTime is same or younger (>=) than "now" by X units.
419
455
  */
420
456
  isSameOrYoungerThan(n, unit, now) {
421
- return this.isSameOrAfter(localTime.of(now ?? new Date()).plus(-n, unit));
457
+ return this.isSameOrAfter(localTime.fromInput(now ?? new Date()).plus(-n, unit));
422
458
  }
423
459
  getAgeInYears(now) {
424
460
  return this.getAgeIn('year', now);
@@ -439,42 +475,42 @@ export class LocalTime {
439
475
  return this.getAgeIn('second', now);
440
476
  }
441
477
  getAgeIn(unit, now) {
442
- return localTime.of(now ?? new Date()).diff(this, unit);
478
+ return localTime.fromInput(now ?? new Date()).diff(this, unit);
443
479
  }
444
480
  /**
445
481
  * Returns 1 if this > d
446
482
  * returns 0 if they are equal
447
483
  * returns -1 if this < d
448
484
  */
449
- cmp(d) {
485
+ compare(d) {
450
486
  const t1 = this.$date.valueOf();
451
- const t2 = localTime.of(d).$date.valueOf();
487
+ const t2 = localTime.fromInput(d).$date.valueOf();
452
488
  if (t1 === t2)
453
489
  return 0;
454
490
  return t1 < t2 ? -1 : 1;
455
491
  }
456
- getDateTimeObject() {
492
+ toDateTimeObject() {
457
493
  return {
458
- ...this.getDateObject(),
459
- ...this.getTimeObject(),
494
+ ...this.toDateObject(),
495
+ ...this.toTimeObject(),
460
496
  };
461
497
  }
462
- getDateObject() {
498
+ toDateObject() {
463
499
  return {
464
500
  year: this.$date.getFullYear(),
465
501
  month: this.$date.getMonth() + 1,
466
502
  day: this.$date.getDate(),
467
503
  };
468
504
  }
469
- getTimeObject() {
505
+ toTimeObject() {
470
506
  return {
471
507
  hour: this.$date.getHours(),
472
508
  minute: this.$date.getMinutes(),
473
509
  second: this.$date.getSeconds(),
474
510
  };
475
511
  }
476
- fromNow(now = new Date()) {
477
- const msDiff = localTime.of(now).$date.valueOf() - this.$date.valueOf();
512
+ toFromNowString(now = new Date()) {
513
+ const msDiff = localTime.fromInput(now).$date.valueOf() - this.$date.valueOf();
478
514
  if (msDiff === 0)
479
515
  return 'now';
480
516
  if (msDiff >= 0) {
@@ -482,16 +518,16 @@ export class LocalTime {
482
518
  }
483
519
  return `in ${_ms(msDiff * -1)}`;
484
520
  }
485
- getDate() {
521
+ toDate() {
486
522
  return this.$date;
487
523
  }
488
524
  clone() {
489
525
  return new LocalTime(new Date(this.$date));
490
526
  }
491
- unix() {
527
+ get unix() {
492
528
  return Math.floor(this.$date.valueOf() / 1000);
493
529
  }
494
- unixMillis() {
530
+ get unixMillis() {
495
531
  return this.$date.valueOf();
496
532
  }
497
533
  valueOf() {
@@ -523,7 +559,7 @@ export class LocalTime {
523
559
  * Returns e.g: `1984-06-21`, only the date part of DateTime
524
560
  */
525
561
  toISODate() {
526
- const { year, month, day } = this.getDateObject();
562
+ const { year, month, day } = this.toDateObject();
527
563
  return [
528
564
  String(year).padStart(4, '0'),
529
565
  String(month).padStart(2, '0'),
@@ -536,7 +572,7 @@ export class LocalTime {
536
572
  * Returns e.g: `17:03:15` (or `17:03` with seconds=false)
537
573
  */
538
574
  toISOTime(seconds = true) {
539
- const { hour, minute, second } = this.getTimeObject();
575
+ const { hour, minute, second } = this.toTimeObject();
540
576
  return [
541
577
  String(hour).padStart(2, '0'),
542
578
  String(minute).padStart(2, '0'),
@@ -551,7 +587,7 @@ export class LocalTime {
551
587
  * Returns e.g: `19840621_1705`
552
588
  */
553
589
  toStringCompact(seconds = false) {
554
- const { year, month, day, hour, minute, second } = this.getDateTimeObject();
590
+ const { year, month, day, hour, minute, second } = this.toDateTimeObject();
555
591
  return [
556
592
  String(year).padStart(4, '0'),
557
593
  String(month).padStart(2, '0'),
@@ -566,7 +602,7 @@ export class LocalTime {
566
602
  return this.toISODateTime();
567
603
  }
568
604
  toJSON() {
569
- return this.unix();
605
+ return this.unix;
570
606
  }
571
607
  toMonthId() {
572
608
  return this.toISODate().slice(0, 7);
@@ -580,105 +616,198 @@ export class LocalTime {
580
616
  }
581
617
  class LocalTimeFactory {
582
618
  /**
583
- * Parses input String into LocalDate.
584
- * Input can already be a LocalDate - it is returned as-is in that case.
619
+ * Creates a LocalTime from the input, unless it's falsy - then returns undefined.
620
+ *
621
+ * `localTime` function will instead return LocalTime of `now` for falsy input.
585
622
  */
586
- of(d) {
587
- const t = this.parseOrNull(d);
588
- _assert(t !== null, `Cannot parse "${d}" into LocalTime`, {
589
- input: d,
590
- });
591
- return t;
623
+ orUndefined(input) {
624
+ return input || input === 0 ? this.fromInput(input) : undefined;
592
625
  }
593
626
  /**
594
- * Create LocalTime from unixTimestamp in milliseconds (not in seconds).
627
+ * Creates a LocalTime from the input, unless it's falsy - then returns LocalTime.now
595
628
  */
596
- ofMillis(millis) {
597
- return this.of(new Date(millis));
629
+ orNow(input) {
630
+ return input || input === 0 ? this.fromInput(input) : this.now();
631
+ }
632
+ now() {
633
+ return new LocalTime(new Date());
598
634
  }
599
635
  /**
600
- * Returns null if invalid
636
+ Convenience function to return current Unix timestamp in seconds.
637
+ Like Date.now(), but in seconds.
601
638
  */
602
- parseOrNull(d) {
603
- if (d instanceof LocalTime)
604
- return d;
605
- let date;
606
- if (d instanceof Date) {
607
- date = d;
608
- }
609
- else if (typeof d === 'number') {
610
- date = new Date(d * 1000);
611
- }
612
- else if (!d) {
613
- // This check is after checking the number, to support `0`
614
- return null;
615
- }
616
- else if (typeof d !== 'string') {
617
- // unexpected type, e.g Function or something
618
- return null;
639
+ nowUnix() {
640
+ return Math.floor(Date.now() / 1000);
641
+ }
642
+ /**
643
+ * Create LocalTime from LocalTimeInput.
644
+ * Input can already be a LocalTime - it is returned as-is in that case.
645
+ * Date - will be used as-is.
646
+ * String - will be parsed as strict `yyyy-mm-ddThh:mm:ss`.
647
+ * Number - will be treated as unix timestamp in seconds.
648
+ */
649
+ fromInput(input) {
650
+ if (input instanceof LocalTime)
651
+ return input;
652
+ if (input instanceof Date) {
653
+ return this.fromDate(input);
619
654
  }
620
- else {
621
- date = this.parseStringToDateOrNull(d);
622
- if (date === null)
623
- return null;
655
+ if (typeof input === 'number') {
656
+ return this.fromUnix(input);
624
657
  }
658
+ // It means it's a string
659
+ // Will parse it STRICTLY
660
+ return this.fromIsoDateTimeString(input);
661
+ }
662
+ /**
663
+ * Returns true if input is valid to create LocalTime.
664
+ */
665
+ isValid(input) {
666
+ if (!input)
667
+ return false;
668
+ if (input instanceof LocalTime)
669
+ return true;
670
+ if (input instanceof Date)
671
+ return !isNaN(input.getDate());
672
+ // We currently don't validate Unixtimestamp input, treat it as always valid
673
+ if (typeof input === 'number')
674
+ return true;
675
+ return this.isValidString(input);
676
+ }
677
+ /**
678
+ * Returns true if isoString is a valid iso8601 string like `yyyy-mm-ddThh:mm:dd`.
679
+ */
680
+ isValidString(isoString) {
681
+ return !!this.parseStrictlyOrUndefined(isoString);
682
+ }
683
+ /**
684
+ * Tries to convert/parse the input into LocalTime.
685
+ * Uses LOOSE parsing.
686
+ * If invalid - doesn't throw, but returns undefined instead.
687
+ */
688
+ try(input) {
689
+ if (input instanceof LocalTime)
690
+ return input;
691
+ if (input instanceof Date) {
692
+ if (isNaN(input.getDate()))
693
+ return;
694
+ return new LocalTime(input);
695
+ }
696
+ if (typeof input === 'number') {
697
+ return this.fromUnix(input);
698
+ }
699
+ if (!input)
700
+ return;
701
+ const date = this.parseLooselyOrUndefined(input);
702
+ return date ? new LocalTime(date) : undefined;
703
+ }
704
+ /**
705
+ * Performs STRICT parsing.
706
+ * Only allows IsoDateTimeString or IsoDateString input, nothing else.
707
+ */
708
+ // eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
709
+ fromIsoDateTimeString(s) {
710
+ const d = this.parseStrictlyOrUndefined(s);
711
+ _assert(d, `Cannot parse "${s}" into LocalTime`);
712
+ return new LocalTime(d);
713
+ }
714
+ /**
715
+ * Performs LOOSE parsing.
716
+ * Tries to coerce imprefect/incorrect string input into IsoDateTimeString.
717
+ * Use with caution.
718
+ * Allows to input IsoDateString, will set h:m:s to zeros.
719
+ */
720
+ parse(s) {
721
+ const d = this.parseLooselyOrUndefined(String(s));
722
+ _assert(d, `Cannot parse "${s}" into LocalTime`);
723
+ return new LocalTime(d);
724
+ }
725
+ parseStrictlyOrUndefined(s) {
726
+ if (!s || typeof s !== 'string')
727
+ return;
728
+ let m = DATE_TIME_REGEX_STRICT.exec(s);
729
+ if (!m) {
730
+ // DateTime regex didn't match, try just-Date regex
731
+ m = DATE_REGEX_STRICT.exec(s);
732
+ if (!m)
733
+ return;
734
+ }
735
+ const o = {
736
+ year: Number(m[1]),
737
+ month: Number(m[2]),
738
+ day: Number(m[3]),
739
+ hour: Number(m[4]) || 0,
740
+ minute: Number(m[5]) || 0,
741
+ second: Number(m[6]) || 0,
742
+ };
743
+ if (!this.isDateTimeObjectValid(o))
744
+ return;
745
+ return this.createDateFromDateTimeObject(o);
746
+ }
747
+ parseLooselyOrUndefined(s) {
748
+ if (!s || typeof s !== 'string')
749
+ return;
750
+ const m = DATE_TIME_REGEX_LOOSE.exec(s);
751
+ if (!m) {
752
+ if (s.length < 8)
753
+ return;
754
+ // Attempt to parse with Date constructor
755
+ const d = new Date(s);
756
+ return isNaN(d.getDate()) ? undefined : d;
757
+ }
758
+ const o = {
759
+ year: Number(m[1]),
760
+ month: Number(m[2]),
761
+ day: Number(m[3]) || 1,
762
+ // [4] is skipped due to extra regex parentheses group
763
+ hour: Number(m[5]) || 0,
764
+ minute: Number(m[6]) || 0,
765
+ second: Number(m[7]) || 0,
766
+ };
767
+ if (!this.isDateTimeObjectValid(o))
768
+ return;
769
+ return this.createDateFromDateTimeObject(o);
770
+ }
771
+ /**
772
+ * Throws on invalid value.
773
+ */
774
+ validateDateTimeObject(o) {
775
+ _assert(this.isDateTimeObjectValid(o), `Cannot construct LocalTime from: ${o.year}-${o.month}-${o.day} ${o.hour}:${o.minute}:${o.second}`);
776
+ }
777
+ isDateTimeObjectValid(o) {
778
+ return localDate.isDateObjectValid(o) && this.isTimeObjectValid(o);
779
+ }
780
+ isTimeObjectValid({ hour, minute, second }) {
781
+ return hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 && second >= 0 && second <= 59;
782
+ }
783
+ fromDate(date) {
784
+ _assert(!isNaN(date.getDate()), `localTime.fromDate is called on Date object that is invalid`);
625
785
  return new LocalTime(date);
626
786
  }
627
- parseStringToDateOrNull(s) {
628
- // Slicing removes the "timezone component", and makes the date "local"
629
- // e.g 2022-04-06T23:15:00+09:00
630
- // becomes 2022-04-06T23:15:00
631
- // date = new Date(d.slice(0, 19))
632
- // Parsing is inspired by how Day.js does it
633
- // Specifically, it ensures that `localTime('2023-03-03')` returns the expected Date, and not a day before
634
- // Because `new Date('2023-03-03')` in NewYork gives you '2023-03-02 19:00:00 GMT-0500'
635
- const m = s.match(DATE_TIME_REGEX);
636
- // Validate in 3 ways:
637
- // 1. Should match Regex.
638
- // In some ways it's stricter than Date constructor, e.g it doesn't allow 2023/05/05
639
- // In other ways it's looser, e.g it allows `2023-05-05T`, while Date constructor doesn't.
640
- // 2. Date constructor (of Node/v8 implementation, which we know is different from e.g WebKit/Safari)
641
- // should not return `Invalid Date`.
642
- // 3. Year, month and day should be valid, e.g 2023-01-32 should not be allowed.
643
- // UPD: Actually, 3 can be skipped, because 2 is catching it already
644
- // UPD: 2 is skipped, 1 and 3 are kept
645
- // if (!m || isNaN(new Date(s).getDate())) return null
646
- if (!m)
647
- return null;
648
- const year = Number(m[1]);
649
- const month = Number(m[2]);
650
- const day = Number(m[3]);
651
- const hour = Number(m[5]);
652
- const minute = Number(m[6]);
653
- const second = Number(m[7]);
654
- // Validation for just the Date part
655
- if (!year ||
656
- !month ||
657
- month < 1 ||
658
- month > 12 ||
659
- !day ||
660
- day < 1 ||
661
- day > localDate.getMonthLength(year, month)) {
662
- return null;
663
- }
664
- // Validation for Date+Time string, since the string is longer than YYYY-MM-DD
665
- if (s.length > 10 &&
666
- (isNaN(hour) ||
667
- isNaN(minute) ||
668
- isNaN(second) ||
669
- hour < 0 ||
670
- hour > 23 ||
671
- minute < 0 ||
672
- minute > 59 ||
673
- second < 0 ||
674
- second > 59)) {
675
- return null;
676
- }
677
- return new Date(year, month - 1, day, hour || 0, minute || 0, second || 0, 0);
678
- }
679
- isValid(d) {
680
- return this.parseOrNull(d) !== null;
787
+ fromUnix(ts) {
788
+ return new LocalTime(new Date(ts * 1000));
681
789
  }
790
+ /**
791
+ * Create LocalTime from unixTimestamp in milliseconds (not in seconds).
792
+ */
793
+ fromMillis(millis) {
794
+ return new LocalTime(new Date(millis));
795
+ }
796
+ fromDateTimeObject(o) {
797
+ // todo: validate?
798
+ return new LocalTime(this.createDateFromDateTimeObject(o));
799
+ }
800
+ createDateFromDateTimeObject(o) {
801
+ return new Date(o.year, o.month - 1, o.day || 1, o.hour || 0, o.minute || 0, o.second || 0);
802
+ }
803
+ // private assertNotNull(
804
+ // lt: LocalTime | null,
805
+ // input: LocalTimeInputNullable,
806
+ // ): asserts lt is LocalTime {
807
+ // _assert(lt !== null, `Cannot parse "${input}" into LocalTime`, {
808
+ // input,
809
+ // })
810
+ // }
682
811
  /**
683
812
  * Returns the IANA timezone e.g `Europe/Stockholm`.
684
813
  * https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
@@ -696,26 +825,6 @@ class LocalTimeFactory {
696
825
  isTimezoneValid(tz) {
697
826
  return Intl.supportedValuesOf('timeZone').includes(tz);
698
827
  }
699
- now() {
700
- return new LocalTime(new Date());
701
- }
702
- /**
703
- * Creates a LocalTime from the input, unless it's falsy - then returns undefined.
704
- *
705
- * `localTime` function will instead return LocalTime of `now` for falsy input.
706
- */
707
- orUndefined(d) {
708
- return d ? this.of(d) : undefined;
709
- }
710
- /**
711
- * Creates a LocalTime from the input, unless it's falsy - then returns LocalTime.now
712
- */
713
- orNow(d) {
714
- return d ? this.of(d) : this.now();
715
- }
716
- fromComponents(c) {
717
- return new LocalTime(new Date(c.year, c.month - 1, c.day || 1, c.hour || 0, c.minute || 0, c.second || 0));
718
- }
719
828
  sort(items, dir = 'asc', mutate = false) {
720
829
  const mod = dir === 'desc' ? -1 : 1;
721
830
  return (mutate ? items : [...items]).sort((a, b) => {
@@ -733,7 +842,7 @@ class LocalTimeFactory {
733
842
  const items2 = items.filter(_isTruthy);
734
843
  _assert(items2.length, 'localTime.min called on empty array');
735
844
  return items2
736
- .map(i => this.of(i))
845
+ .map(i => this.fromInput(i))
737
846
  .reduce((min, item) => (min.$date.valueOf() <= item.$date.valueOf() ? min : item));
738
847
  }
739
848
  maxOrUndefined(items) {
@@ -743,7 +852,7 @@ class LocalTimeFactory {
743
852
  const items2 = items.filter(_isTruthy);
744
853
  _assert(items2.length, 'localTime.max called on empty array');
745
854
  return items2
746
- .map(i => this.of(i))
855
+ .map(i => this.fromInput(i))
747
856
  .reduce((max, item) => (max.$date.valueOf() >= item.$date.valueOf() ? max : item));
748
857
  }
749
858
  }
@@ -836,14 +945,7 @@ function differenceInMonths(a, b) {
836
945
  return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign);
837
946
  }
838
947
  const localTimeFactory = new LocalTimeFactory();
839
- export const localTime = localTimeFactory.of.bind(localTimeFactory);
948
+ export const localTime = localTimeFactory.fromInput.bind(localTimeFactory);
840
949
  // The line below is the blackest of black magic I have ever written in 2024.
841
950
  // And probably 2023 as well.
842
951
  Object.setPrototypeOf(localTime, localTimeFactory);
843
- /**
844
- Convenience function to return current Unix timestamp in seconds.
845
- Like Date.now(), but in seconds.
846
- */
847
- export function nowUnix() {
848
- return Math.floor(Date.now() / 1000);
849
- }
@@ -11,7 +11,7 @@ export class TimeInterval {
11
11
  this.$end = $end;
12
12
  }
13
13
  static of(start, end) {
14
- return new TimeInterval(localTime.of(start).unix(), localTime.of(end).unix());
14
+ return new TimeInterval(localTime.fromInput(start).unix, localTime.fromInput(end).unix);
15
15
  }
16
16
  get start() {
17
17
  return this.$start;
@@ -55,7 +55,7 @@ export class TimeInterval {
55
55
  return this.cmp(d) >= 0;
56
56
  }
57
57
  includes(d, incl = '[)') {
58
- d = localTime.of(d).unix();
58
+ d = localTime.fromInput(d).unix;
59
59
  // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
60
60
  if (d < this.$start || (d === this.$start && incl[0] === '('))
61
61
  return false;
@@ -1,4 +1,4 @@
1
- import { SimpleMovingAverage, _stringify, _assert } from '..';
1
+ import { _assert, _stringify, SimpleMovingAverage } from '..';
2
2
  import { _ms } from '../time/time.util';
3
3
  import { _getArgsSignature, _getMethodSignature } from './decorator.util';
4
4
  /**