@naturalcycles/js-lib 14.232.0 → 14.233.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/dist/datetime/dateInterval.d.ts +1 -2
- package/dist/datetime/dateInterval.js +5 -5
- package/dist/datetime/localDate.d.ts +125 -52
- package/dist/datetime/localDate.js +259 -201
- package/dist/datetime/localTime.d.ts +46 -51
- package/dist/datetime/localTime.js +181 -197
- package/dist/datetime/timeInterval.d.ts +1 -2
- package/dist/datetime/timeInterval.js +4 -4
- package/dist/env/buildInfo.js +1 -1
- package/dist-esm/datetime/dateInterval.js +6 -6
- package/dist-esm/datetime/localDate.js +259 -195
- package/dist-esm/datetime/localTime.js +177 -192
- package/dist-esm/datetime/timeInterval.js +5 -5
- package/dist-esm/env/buildInfo.js +2 -2
- package/package.json +2 -2
- package/src/datetime/dateInterval.ts +6 -7
- package/src/datetime/localDate.ts +307 -237
- package/src/datetime/localTime.ts +203 -212
- package/src/datetime/timeInterval.ts +6 -7
- package/src/env/buildInfo.ts +2 -2
|
@@ -11,7 +11,7 @@ import type {
|
|
|
11
11
|
UnixTimestampMillisNumber,
|
|
12
12
|
UnixTimestampNumber,
|
|
13
13
|
} from '../types'
|
|
14
|
-
import { LocalDate } from './localDate'
|
|
14
|
+
import { localDate, LocalDate } from './localDate'
|
|
15
15
|
|
|
16
16
|
export type LocalTimeUnit = 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second'
|
|
17
17
|
|
|
@@ -49,111 +49,11 @@ const SECONDS_IN_DAY = 86400
|
|
|
49
49
|
// const MILLISECONDS_IN_MINUTE = 60000
|
|
50
50
|
const VALID_DAYS_OF_WEEK = new Set([1, 2, 3, 4, 5, 6, 7])
|
|
51
51
|
|
|
52
|
-
/**
|
|
53
|
-
* @experimental
|
|
54
|
-
*/
|
|
55
52
|
export class LocalTime {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Parses input String into LocalDate.
|
|
60
|
-
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
61
|
-
*/
|
|
62
|
-
static of(d: LocalTimeInput): LocalTime {
|
|
63
|
-
const t = this.parseOrNull(d)
|
|
64
|
-
|
|
65
|
-
_assert(t !== null, `Cannot parse "${d}" into LocalTime`, {
|
|
66
|
-
input: d,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
return t
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Create LocalTime from unixTimestamp in milliseconds (not in seconds).
|
|
74
|
-
*/
|
|
75
|
-
static ofMillis(millis: UnixTimestampMillisNumber): LocalTime {
|
|
76
|
-
return LocalTime.of(new Date(millis))
|
|
77
|
-
}
|
|
53
|
+
constructor(public $date: Date) {}
|
|
78
54
|
|
|
79
55
|
/**
|
|
80
|
-
* Returns
|
|
81
|
-
*/
|
|
82
|
-
static parseOrNull(d: LocalTimeInput | undefined | null): LocalTime | null {
|
|
83
|
-
if (!d) return null
|
|
84
|
-
if (d instanceof LocalTime) return d
|
|
85
|
-
|
|
86
|
-
let date
|
|
87
|
-
|
|
88
|
-
if (d instanceof Date) {
|
|
89
|
-
date = d
|
|
90
|
-
} else if (typeof d === 'number') {
|
|
91
|
-
date = new Date(d * 1000)
|
|
92
|
-
} else if (typeof (d as any) !== 'string') {
|
|
93
|
-
// unexpected type, e.g Function or something
|
|
94
|
-
return null
|
|
95
|
-
} else {
|
|
96
|
-
// Slicing removes the "timezone component", and makes the date "local"
|
|
97
|
-
// e.g 2022-04-06T23:15:00+09:00
|
|
98
|
-
// becomes 2022-04-06T23:15:00
|
|
99
|
-
date = new Date(d.slice(0, 19))
|
|
100
|
-
// We used to slice to remove the timezone information, now we don't
|
|
101
|
-
// date = new Date(d)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// validation
|
|
105
|
-
if (isNaN(date.getDate())) {
|
|
106
|
-
// throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
107
|
-
return null
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return new LocalTime(date)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
static parseToDate(d: LocalTimeInput): Date {
|
|
114
|
-
if (d instanceof LocalTime) return d.$date
|
|
115
|
-
if (d instanceof Date) return d
|
|
116
|
-
|
|
117
|
-
const date = typeof d === 'number' ? new Date(d * 1000) : new Date(d)
|
|
118
|
-
|
|
119
|
-
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to Date`, {
|
|
120
|
-
input: d,
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
return date
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
static parseToUnixTimestamp(d: LocalTimeInput): UnixTimestampNumber {
|
|
127
|
-
if (typeof d === 'number') return d
|
|
128
|
-
if (d instanceof LocalTime) return d.unix()
|
|
129
|
-
|
|
130
|
-
const date = d instanceof Date ? d : new Date(d)
|
|
131
|
-
|
|
132
|
-
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to UnixTimestamp`, {
|
|
133
|
-
input: d,
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
return date.valueOf() / 1000
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
static isValid(d: LocalTimeInput | undefined | null): boolean {
|
|
140
|
-
return this.parseOrNull(d) !== null
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
static now(): LocalTime {
|
|
144
|
-
return new LocalTime(new Date())
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
static fromComponents(
|
|
148
|
-
c: { year: number; month: number } & Partial<LocalTimeComponents>,
|
|
149
|
-
): LocalTime {
|
|
150
|
-
return new LocalTime(
|
|
151
|
-
new Date(c.year, c.month - 1, c.day || 1, c.hour || 0, c.minute || 0, c.second || 0),
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Returns LocalTime that is based on the same unixtimestamp, but in UTC timezone.
|
|
56
|
+
* Returns [cloned] LocalTime that is based on the same unixtimestamp, but in UTC timezone.
|
|
157
57
|
* Opposite of `.local()` method.
|
|
158
58
|
*/
|
|
159
59
|
utc(): LocalTime {
|
|
@@ -161,7 +61,7 @@ export class LocalTime {
|
|
|
161
61
|
}
|
|
162
62
|
|
|
163
63
|
/**
|
|
164
|
-
* Returns LocalTime that is based on the same unixtimestamp, but in local timezone.
|
|
64
|
+
* Returns [cloned] LocalTime that is based on the same unixtimestamp, but in local timezone.
|
|
165
65
|
* Opposite of `.utc()` method.
|
|
166
66
|
*/
|
|
167
67
|
local(): LocalTime {
|
|
@@ -299,7 +199,7 @@ export class LocalTime {
|
|
|
299
199
|
|
|
300
200
|
if (unit === 'year' || unit === 'month') {
|
|
301
201
|
const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate)
|
|
302
|
-
return mutate ? this :
|
|
202
|
+
return mutate ? this : localTime.of(d)
|
|
303
203
|
}
|
|
304
204
|
|
|
305
205
|
return this.set(unit, this.get(unit) + num, mutate)
|
|
@@ -314,7 +214,7 @@ export class LocalTime {
|
|
|
314
214
|
}
|
|
315
215
|
|
|
316
216
|
diff(other: LocalTimeInput, unit: LocalTimeUnit): number {
|
|
317
|
-
const date2 =
|
|
217
|
+
const date2 = localTime.parseToDate(other)
|
|
318
218
|
|
|
319
219
|
const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000
|
|
320
220
|
if (!secDiff) return 0
|
|
@@ -322,9 +222,9 @@ export class LocalTime {
|
|
|
322
222
|
let r
|
|
323
223
|
|
|
324
224
|
if (unit === 'year') {
|
|
325
|
-
r = differenceInMonths(this
|
|
225
|
+
r = differenceInMonths(this.$date, date2) / 12
|
|
326
226
|
} else if (unit === 'month') {
|
|
327
|
-
r = differenceInMonths(this
|
|
227
|
+
r = differenceInMonths(this.$date, date2)
|
|
328
228
|
} else if (unit === 'day') {
|
|
329
229
|
r = secDiff / SECONDS_IN_DAY
|
|
330
230
|
} else if (unit === 'week') {
|
|
@@ -390,7 +290,7 @@ export class LocalTime {
|
|
|
390
290
|
endOfWeek(d, true)
|
|
391
291
|
} else {
|
|
392
292
|
// year or month
|
|
393
|
-
const lastDay =
|
|
293
|
+
const lastDay = localDate.getMonthLength(d.getFullYear(), d.getMonth() + 1)
|
|
394
294
|
d.setDate(lastDay)
|
|
395
295
|
}
|
|
396
296
|
}
|
|
@@ -405,41 +305,7 @@ export class LocalTime {
|
|
|
405
305
|
* E.g 31 for January.
|
|
406
306
|
*/
|
|
407
307
|
daysInMonth(): number {
|
|
408
|
-
return
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
static sort(items: LocalTime[], mutate = false, dir: SortDirection = 'asc'): LocalTime[] {
|
|
412
|
-
const mod = dir === 'desc' ? -1 : 1
|
|
413
|
-
return (mutate ? items : [...items]).sort((a, b) => {
|
|
414
|
-
const v1 = a.$date.valueOf()
|
|
415
|
-
const v2 = b.$date.valueOf()
|
|
416
|
-
if (v1 === v2) return 0
|
|
417
|
-
return (v1 < v2 ? -1 : 1) * mod
|
|
418
|
-
})
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
static earliestOrUndefined(items: LocalTimeInput[]): LocalTime | undefined {
|
|
422
|
-
return items.length ? LocalTime.earliest(items) : undefined
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
static earliest(items: LocalTimeInput[]): LocalTime {
|
|
426
|
-
_assert(items.length, 'LocalTime.earliest called on empty array')
|
|
427
|
-
|
|
428
|
-
return items
|
|
429
|
-
.map(i => LocalTime.of(i))
|
|
430
|
-
.reduce((min, item) => (min.isSameOrBefore(item) ? min : item))
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
static latestOrUndefined(items: LocalTimeInput[]): LocalTime | undefined {
|
|
434
|
-
return items.length ? LocalTime.latest(items) : undefined
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
static latest(items: LocalTimeInput[]): LocalTime {
|
|
438
|
-
_assert(items.length, 'LocalTime.latest called on empty array')
|
|
439
|
-
|
|
440
|
-
return items
|
|
441
|
-
.map(i => LocalTime.of(i))
|
|
442
|
-
.reduce((max, item) => (max.isSameOrAfter(item) ? max : item))
|
|
308
|
+
return localDate.getMonthLength(this.$date.getFullYear(), this.$date.getMonth() + 1)
|
|
443
309
|
}
|
|
444
310
|
|
|
445
311
|
isSame(d: LocalTimeInput): boolean {
|
|
@@ -483,14 +349,14 @@ export class LocalTime {
|
|
|
483
349
|
* Third argument allows to override "now".
|
|
484
350
|
*/
|
|
485
351
|
isOlderThan(n: number, unit: LocalTimeUnit, now?: LocalTimeInput): boolean {
|
|
486
|
-
return this.isBefore(
|
|
352
|
+
return this.isBefore(localTime.of(now ?? new Date()).plus(-n, unit))
|
|
487
353
|
}
|
|
488
354
|
|
|
489
355
|
/**
|
|
490
356
|
* Checks if this localTime is same or older (<=) than "now" by X units.
|
|
491
357
|
*/
|
|
492
358
|
isSameOrOlderThan(n: number, unit: LocalTimeUnit, now?: LocalTimeInput): boolean {
|
|
493
|
-
return this.isSameOrBefore(
|
|
359
|
+
return this.isSameOrBefore(localTime.of(now ?? new Date()).plus(-n, unit))
|
|
494
360
|
}
|
|
495
361
|
|
|
496
362
|
/**
|
|
@@ -503,14 +369,14 @@ export class LocalTime {
|
|
|
503
369
|
* Third argument allows to override "now".
|
|
504
370
|
*/
|
|
505
371
|
isYoungerThan(n: number, unit: LocalTimeUnit, now?: LocalTimeInput): boolean {
|
|
506
|
-
return this.isAfter(
|
|
372
|
+
return this.isAfter(localTime.of(now ?? new Date()).plus(-n, unit))
|
|
507
373
|
}
|
|
508
374
|
|
|
509
375
|
/**
|
|
510
376
|
* Checks if this localTime is same or younger (>=) than "now" by X units.
|
|
511
377
|
*/
|
|
512
378
|
isSameOrYoungerThan(n: number, unit: LocalTimeUnit, now?: LocalTimeInput): boolean {
|
|
513
|
-
return this.isSameOrAfter(
|
|
379
|
+
return this.isSameOrAfter(localTime.of(now ?? new Date()).plus(-n, unit))
|
|
514
380
|
}
|
|
515
381
|
|
|
516
382
|
/**
|
|
@@ -520,19 +386,15 @@ export class LocalTime {
|
|
|
520
386
|
*/
|
|
521
387
|
cmp(d: LocalTimeInput): -1 | 0 | 1 {
|
|
522
388
|
const t1 = this.$date.valueOf()
|
|
523
|
-
const t2 =
|
|
389
|
+
const t2 = localTime.parseToDate(d).valueOf()
|
|
524
390
|
if (t1 === t2) return 0
|
|
525
391
|
return t1 < t2 ? -1 : 1
|
|
526
392
|
}
|
|
527
393
|
|
|
528
394
|
components(): LocalTimeComponents {
|
|
529
395
|
return {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
day: this.$date.getDate(),
|
|
533
|
-
hour: this.$date.getHours(),
|
|
534
|
-
minute: this.$date.getMinutes(),
|
|
535
|
-
second: this.$date.getSeconds(),
|
|
396
|
+
...this.dateComponents(),
|
|
397
|
+
...this.timeComponents(),
|
|
536
398
|
}
|
|
537
399
|
}
|
|
538
400
|
|
|
@@ -553,7 +415,7 @@ export class LocalTime {
|
|
|
553
415
|
}
|
|
554
416
|
|
|
555
417
|
fromNow(now: LocalTimeInput = new Date()): string {
|
|
556
|
-
const msDiff =
|
|
418
|
+
const msDiff = localTime.parseToDate(now).valueOf() - this.$date.valueOf()
|
|
557
419
|
|
|
558
420
|
if (msDiff === 0) return 'now'
|
|
559
421
|
|
|
@@ -585,7 +447,7 @@ export class LocalTime {
|
|
|
585
447
|
}
|
|
586
448
|
|
|
587
449
|
toLocalDate(): LocalDate {
|
|
588
|
-
return
|
|
450
|
+
return localDate.fromDate(this.$date)
|
|
589
451
|
}
|
|
590
452
|
|
|
591
453
|
/**
|
|
@@ -681,66 +543,151 @@ export class LocalTime {
|
|
|
681
543
|
}
|
|
682
544
|
}
|
|
683
545
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
546
|
+
class LocalTimeFactory {
|
|
547
|
+
/**
|
|
548
|
+
* Parses input String into LocalDate.
|
|
549
|
+
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
550
|
+
*/
|
|
551
|
+
of(d: LocalTimeInput): LocalTime {
|
|
552
|
+
const t = this.parseOrNull(d)
|
|
690
553
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
export function localTimeNow(): LocalTime {
|
|
695
|
-
return LocalTime.now()
|
|
696
|
-
}
|
|
554
|
+
_assert(t !== null, `Cannot parse "${d}" into LocalTime`, {
|
|
555
|
+
input: d,
|
|
556
|
+
})
|
|
697
557
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
*
|
|
701
|
-
* `localTime` function will instead return LocalTime of `now` for falsy input.
|
|
702
|
-
*/
|
|
703
|
-
export function localTimeOrUndefined(d?: LocalTimeInput | null): LocalTime | undefined {
|
|
704
|
-
return d ? LocalTime.of(d) : undefined
|
|
705
|
-
}
|
|
558
|
+
return t
|
|
559
|
+
}
|
|
706
560
|
|
|
707
|
-
/**
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
561
|
+
/**
|
|
562
|
+
* Create LocalTime from unixTimestamp in milliseconds (not in seconds).
|
|
563
|
+
*/
|
|
564
|
+
ofMillis(millis: UnixTimestampMillisNumber): LocalTime {
|
|
565
|
+
return this.of(new Date(millis))
|
|
566
|
+
}
|
|
713
567
|
|
|
714
|
-
/**
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
}
|
|
568
|
+
/**
|
|
569
|
+
* Returns null if invalid
|
|
570
|
+
*/
|
|
571
|
+
parseOrNull(d: LocalTimeInput | undefined | null): LocalTime | null {
|
|
572
|
+
if (!d) return null
|
|
573
|
+
if (d instanceof LocalTime) return d
|
|
721
574
|
|
|
722
|
-
|
|
723
|
-
* UTC offset is the opposite of "timezone offset" - it's the number of minutes to add
|
|
724
|
-
* to the local time to get UTC time.
|
|
725
|
-
*
|
|
726
|
-
* E.g utcOffset for CEST is -120,
|
|
727
|
-
* which means that you need to add -120 minutes to the local time to get UTC time.
|
|
728
|
-
*
|
|
729
|
-
* Instead of -0 it returns 0, for the peace of mind and less weird test/snapshot differences.
|
|
730
|
-
*/
|
|
731
|
-
export function getUTCOffsetMinutes(): NumberOfMinutes {
|
|
732
|
-
return -new Date().getTimezoneOffset() || 0
|
|
733
|
-
}
|
|
575
|
+
let date
|
|
734
576
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
577
|
+
if (d instanceof Date) {
|
|
578
|
+
date = d
|
|
579
|
+
} else if (typeof d === 'number') {
|
|
580
|
+
date = new Date(d * 1000)
|
|
581
|
+
} else if (typeof (d as any) !== 'string') {
|
|
582
|
+
// unexpected type, e.g Function or something
|
|
583
|
+
return null
|
|
584
|
+
} else {
|
|
585
|
+
// Slicing removes the "timezone component", and makes the date "local"
|
|
586
|
+
// e.g 2022-04-06T23:15:00+09:00
|
|
587
|
+
// becomes 2022-04-06T23:15:00
|
|
588
|
+
date = new Date(d.slice(0, 19))
|
|
589
|
+
// We used to slice to remove the timezone information, now we don't
|
|
590
|
+
// date = new Date(d)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// validation
|
|
594
|
+
if (isNaN(date.getDate())) {
|
|
595
|
+
// throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
596
|
+
return null
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return new LocalTime(date)
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
parseToDate(d: LocalTimeInput): Date {
|
|
603
|
+
if (d instanceof LocalTime) return d.$date
|
|
604
|
+
if (d instanceof Date) return d
|
|
605
|
+
|
|
606
|
+
const date = typeof d === 'number' ? new Date(d * 1000) : new Date(d)
|
|
607
|
+
|
|
608
|
+
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to Date`, {
|
|
609
|
+
input: d,
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
return date
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
parseToUnixTimestamp(d: LocalTimeInput): UnixTimestampNumber {
|
|
616
|
+
if (typeof d === 'number') return d
|
|
617
|
+
if (d instanceof LocalTime) return d.unix()
|
|
618
|
+
|
|
619
|
+
const date = d instanceof Date ? d : new Date(d)
|
|
620
|
+
|
|
621
|
+
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to UnixTimestamp`, {
|
|
622
|
+
input: d,
|
|
623
|
+
})
|
|
624
|
+
|
|
625
|
+
return date.valueOf() / 1000
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
isValid(d: LocalTimeInput | undefined | null): boolean {
|
|
629
|
+
return this.parseOrNull(d) !== null
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
now(): LocalTime {
|
|
633
|
+
return new LocalTime(new Date())
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/**
|
|
637
|
+
* Creates a LocalTime from the input, unless it's falsy - then returns undefined.
|
|
638
|
+
*
|
|
639
|
+
* `localTime` function will instead return LocalTime of `now` for falsy input.
|
|
640
|
+
*/
|
|
641
|
+
orUndefined(d?: LocalTimeInput | null): LocalTime | undefined {
|
|
642
|
+
return d ? this.of(d) : undefined
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Creates a LocalTime from the input, unless it's falsy - then returns LocalTime.now
|
|
647
|
+
*/
|
|
648
|
+
orNow(d?: LocalTimeInput | null): LocalTime {
|
|
649
|
+
return d ? this.of(d) : this.now()
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
fromComponents(c: { year: number; month: number } & Partial<LocalTimeComponents>): LocalTime {
|
|
653
|
+
return new LocalTime(
|
|
654
|
+
new Date(c.year, c.month - 1, c.day || 1, c.hour || 0, c.minute || 0, c.second || 0),
|
|
655
|
+
)
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
sort(items: LocalTime[], dir: SortDirection = 'asc', mutate = false): LocalTime[] {
|
|
659
|
+
const mod = dir === 'desc' ? -1 : 1
|
|
660
|
+
return (mutate ? items : [...items]).sort((a, b) => {
|
|
661
|
+
const v1 = a.$date.valueOf()
|
|
662
|
+
const v2 = b.$date.valueOf()
|
|
663
|
+
if (v1 === v2) return 0
|
|
664
|
+
return (v1 < v2 ? -1 : 1) * mod
|
|
665
|
+
})
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
minOrUndefined(items: LocalTimeInput[]): LocalTime | undefined {
|
|
669
|
+
return items.length ? this.min(items) : undefined
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
min(items: LocalTimeInput[]): LocalTime {
|
|
673
|
+
_assert(items.length, 'localTime.min called on empty array')
|
|
674
|
+
|
|
675
|
+
return items
|
|
676
|
+
.map(i => this.of(i))
|
|
677
|
+
.reduce((min, item) => (min.$date.valueOf() <= item.$date.valueOf() ? min : item))
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
maxOrUndefined(items: LocalTimeInput[]): LocalTime | undefined {
|
|
681
|
+
return items.length ? this.max(items) : undefined
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
max(items: LocalTimeInput[]): LocalTime {
|
|
685
|
+
_assert(items.length, 'localTime.max called on empty array')
|
|
686
|
+
|
|
687
|
+
return items
|
|
688
|
+
.map(i => this.of(i))
|
|
689
|
+
.reduce((max, item) => (max.$date.valueOf() >= item.$date.valueOf() ? max : item))
|
|
690
|
+
}
|
|
744
691
|
}
|
|
745
692
|
|
|
746
693
|
// based on: https://github.com/date-fns/date-fns/blob/master/src/getISOWeek/index.ts
|
|
@@ -832,7 +779,7 @@ function addMonths(d: Date, num: number, mutate = false): Date {
|
|
|
832
779
|
month += 12
|
|
833
780
|
}
|
|
834
781
|
|
|
835
|
-
const monthLen =
|
|
782
|
+
const monthLen = localDate.getMonthLength(year, month)
|
|
836
783
|
if (day > monthLen) day = monthLen
|
|
837
784
|
|
|
838
785
|
d.setFullYear(year, month - 1, day)
|
|
@@ -847,3 +794,47 @@ function differenceInMonths(a: Date, b: Date): number {
|
|
|
847
794
|
const anchor2 = addMonths(a, wholeMonthDiff + sign).getTime()
|
|
848
795
|
return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign)
|
|
849
796
|
}
|
|
797
|
+
|
|
798
|
+
interface LocalTimeFn extends LocalTimeFactory {
|
|
799
|
+
(d: LocalTimeInput): LocalTime
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
const localTimeFactory = new LocalTimeFactory()
|
|
803
|
+
|
|
804
|
+
export const localTime = localTimeFactory.of.bind(localTimeFactory) as LocalTimeFn
|
|
805
|
+
|
|
806
|
+
// The line below is the blackest of black magic I have ever written in 2024.
|
|
807
|
+
// And probably 2023 as well.
|
|
808
|
+
Object.setPrototypeOf(localTime, localTimeFactory)
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
Convenience function to return current Unix timestamp in seconds.
|
|
812
|
+
Like Date.now(), but in seconds.
|
|
813
|
+
*/
|
|
814
|
+
export function nowUnix(): UnixTimestampNumber {
|
|
815
|
+
return Math.floor(Date.now() / 1000)
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* UTC offset is the opposite of "timezone offset" - it's the number of minutes to add
|
|
820
|
+
* to the local time to get UTC time.
|
|
821
|
+
*
|
|
822
|
+
* E.g utcOffset for CEST is -120,
|
|
823
|
+
* which means that you need to add -120 minutes to the local time to get UTC time.
|
|
824
|
+
*
|
|
825
|
+
* Instead of -0 it returns 0, for the peace of mind and less weird test/snapshot differences.
|
|
826
|
+
*/
|
|
827
|
+
export function getUTCOffsetMinutes(): NumberOfMinutes {
|
|
828
|
+
return -new Date().getTimezoneOffset() || 0
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Same as getUTCOffsetMinutes, but rounded to hours.
|
|
833
|
+
*
|
|
834
|
+
* E.g for CEST it is -2.
|
|
835
|
+
*
|
|
836
|
+
* Instead of -0 it returns 0, for the peace of mind and less weird test/snapshot differences.
|
|
837
|
+
*/
|
|
838
|
+
export function getUTCOffsetHours(): NumberOfHours {
|
|
839
|
+
return Math.round(getUTCOffsetMinutes() / 60)
|
|
840
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { UnixTimestampNumber, Inclusiveness } from '../types'
|
|
2
|
-
import
|
|
3
|
-
import { LocalTime } from './localTime'
|
|
2
|
+
import { localTime, LocalTimeInput, LocalTime } from './localTime'
|
|
4
3
|
|
|
5
4
|
export type TimeIntervalConfig = TimeInterval | TimeIntervalString
|
|
6
5
|
export type TimeIntervalString = string
|
|
@@ -19,8 +18,8 @@ export class TimeInterval {
|
|
|
19
18
|
|
|
20
19
|
static of(start: LocalTimeInput, end: LocalTimeInput): TimeInterval {
|
|
21
20
|
return new TimeInterval(
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
localTime.parseToUnixTimestamp(start),
|
|
22
|
+
localTime.parseToUnixTimestamp(end),
|
|
24
23
|
)
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -33,11 +32,11 @@ export class TimeInterval {
|
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
get startTime(): LocalTime {
|
|
36
|
-
return
|
|
35
|
+
return localTime(this.$start)
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
get endTime(): LocalTime {
|
|
40
|
-
return
|
|
39
|
+
return localTime(this.$end)
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
/**
|
|
@@ -78,7 +77,7 @@ export class TimeInterval {
|
|
|
78
77
|
}
|
|
79
78
|
|
|
80
79
|
includes(d: LocalTimeInput, incl: Inclusiveness = '[)'): boolean {
|
|
81
|
-
d =
|
|
80
|
+
d = localTime.parseToUnixTimestamp(d)
|
|
82
81
|
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
|
|
83
82
|
if (d < this.$start || (d === this.$start && incl[0] === '(')) return false
|
|
84
83
|
if (d > this.$end || (d === this.$end && incl[1] === ')')) return false
|
package/src/env/buildInfo.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { localTime } from '../datetime/localTime'
|
|
2
2
|
import type { UnixTimestampNumber } from '../types'
|
|
3
3
|
|
|
4
4
|
export interface BuildInfo {
|
|
@@ -43,7 +43,7 @@ export interface BuildInfo {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function generateBuildInfoDev(): BuildInfo {
|
|
46
|
-
const now =
|
|
46
|
+
const now = localTime.now()
|
|
47
47
|
const ts = now.unix()
|
|
48
48
|
const rev = 'devRev'
|
|
49
49
|
const branchName = 'devBranch'
|