@naturalcycles/js-lib 14.231.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 +50 -46
- package/dist/datetime/localTime.js +187 -195
- 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 +183 -190
- package/dist-esm/datetime/timeInterval.js +5 -5
- package/dist-esm/env/buildInfo.js +2 -2
- package/package.json +4 -4
- package/src/datetime/dateInterval.ts +6 -7
- package/src/datetime/localDate.ts +307 -237
- package/src/datetime/localTime.ts +209 -210
- 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,115 +49,23 @@ 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
|
-
|
|
53
|
+
constructor(public $date: Date) {}
|
|
57
54
|
|
|
58
55
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
56
|
+
* Returns [cloned] LocalTime that is based on the same unixtimestamp, but in UTC timezone.
|
|
57
|
+
* Opposite of `.local()` method.
|
|
61
58
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
_assert(t !== null, `Cannot parse "${d}" into LocalTime`, {
|
|
66
|
-
input: d,
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
return t
|
|
59
|
+
utc(): LocalTime {
|
|
60
|
+
return new LocalTime(new Date(this.$date.toISOString()))
|
|
70
61
|
}
|
|
71
62
|
|
|
72
63
|
/**
|
|
73
|
-
*
|
|
64
|
+
* Returns [cloned] LocalTime that is based on the same unixtimestamp, but in local timezone.
|
|
65
|
+
* Opposite of `.utc()` method.
|
|
74
66
|
*/
|
|
75
|
-
|
|
76
|
-
return LocalTime
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Returns null if invalid
|
|
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
|
-
// if (utc) {
|
|
111
|
-
// date.setMinutes(date.getMinutes() + date.getTimezoneOffset())
|
|
112
|
-
// }
|
|
113
|
-
|
|
114
|
-
return new LocalTime(date)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
static parseToDate(d: LocalTimeInput): Date {
|
|
118
|
-
if (d instanceof LocalTime) return d.$date
|
|
119
|
-
if (d instanceof Date) return d
|
|
120
|
-
|
|
121
|
-
const date = typeof d === 'number' ? new Date(d * 1000) : new Date(d)
|
|
122
|
-
|
|
123
|
-
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to Date`, {
|
|
124
|
-
input: d,
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
return date
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
static parseToUnixTimestamp(d: LocalTimeInput): UnixTimestampNumber {
|
|
131
|
-
if (typeof d === 'number') return d
|
|
132
|
-
if (d instanceof LocalTime) return d.unix()
|
|
133
|
-
|
|
134
|
-
const date = d instanceof Date ? d : new Date(d)
|
|
135
|
-
|
|
136
|
-
_assert(!isNaN(date.getDate()), `Cannot parse "${d}" to UnixTimestamp`, {
|
|
137
|
-
input: d,
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
return date.valueOf() / 1000
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
static isValid(d: LocalTimeInput | undefined | null): boolean {
|
|
144
|
-
return this.parseOrNull(d) !== null
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
static now(): LocalTime {
|
|
148
|
-
return new LocalTime(new Date())
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
static fromComponents(
|
|
152
|
-
c: { year: number; month: number } & Partial<LocalTimeComponents>,
|
|
153
|
-
): LocalTime {
|
|
154
|
-
return new LocalTime(
|
|
155
|
-
new Date(c.year, c.month - 1, c.day || 1, c.hour || 0, c.minute || 0, c.second || 0),
|
|
156
|
-
)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
static fromDateUTC(d: Date): LocalTime {
|
|
160
|
-
return new LocalTime(new Date(d.toISOString()))
|
|
67
|
+
local(): LocalTime {
|
|
68
|
+
return new LocalTime(new Date(this.$date.getTime()))
|
|
161
69
|
}
|
|
162
70
|
|
|
163
71
|
get(unit: LocalTimeUnit): number {
|
|
@@ -291,7 +199,7 @@ export class LocalTime {
|
|
|
291
199
|
|
|
292
200
|
if (unit === 'year' || unit === 'month') {
|
|
293
201
|
const d = addMonths(this.$date, unit === 'month' ? num : num * 12, mutate)
|
|
294
|
-
return mutate ? this :
|
|
202
|
+
return mutate ? this : localTime.of(d)
|
|
295
203
|
}
|
|
296
204
|
|
|
297
205
|
return this.set(unit, this.get(unit) + num, mutate)
|
|
@@ -306,7 +214,7 @@ export class LocalTime {
|
|
|
306
214
|
}
|
|
307
215
|
|
|
308
216
|
diff(other: LocalTimeInput, unit: LocalTimeUnit): number {
|
|
309
|
-
const date2 =
|
|
217
|
+
const date2 = localTime.parseToDate(other)
|
|
310
218
|
|
|
311
219
|
const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000
|
|
312
220
|
if (!secDiff) return 0
|
|
@@ -314,9 +222,9 @@ export class LocalTime {
|
|
|
314
222
|
let r
|
|
315
223
|
|
|
316
224
|
if (unit === 'year') {
|
|
317
|
-
r = differenceInMonths(this
|
|
225
|
+
r = differenceInMonths(this.$date, date2) / 12
|
|
318
226
|
} else if (unit === 'month') {
|
|
319
|
-
r = differenceInMonths(this
|
|
227
|
+
r = differenceInMonths(this.$date, date2)
|
|
320
228
|
} else if (unit === 'day') {
|
|
321
229
|
r = secDiff / SECONDS_IN_DAY
|
|
322
230
|
} else if (unit === 'week') {
|
|
@@ -382,7 +290,7 @@ export class LocalTime {
|
|
|
382
290
|
endOfWeek(d, true)
|
|
383
291
|
} else {
|
|
384
292
|
// year or month
|
|
385
|
-
const lastDay =
|
|
293
|
+
const lastDay = localDate.getMonthLength(d.getFullYear(), d.getMonth() + 1)
|
|
386
294
|
d.setDate(lastDay)
|
|
387
295
|
}
|
|
388
296
|
}
|
|
@@ -397,41 +305,7 @@ export class LocalTime {
|
|
|
397
305
|
* E.g 31 for January.
|
|
398
306
|
*/
|
|
399
307
|
daysInMonth(): number {
|
|
400
|
-
return
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
static sort(items: LocalTime[], mutate = false, dir: SortDirection = 'asc'): LocalTime[] {
|
|
404
|
-
const mod = dir === 'desc' ? -1 : 1
|
|
405
|
-
return (mutate ? items : [...items]).sort((a, b) => {
|
|
406
|
-
const v1 = a.$date.valueOf()
|
|
407
|
-
const v2 = b.$date.valueOf()
|
|
408
|
-
if (v1 === v2) return 0
|
|
409
|
-
return (v1 < v2 ? -1 : 1) * mod
|
|
410
|
-
})
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
static earliestOrUndefined(items: LocalTimeInput[]): LocalTime | undefined {
|
|
414
|
-
return items.length ? LocalTime.earliest(items) : undefined
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
static earliest(items: LocalTimeInput[]): LocalTime {
|
|
418
|
-
_assert(items.length, 'LocalTime.earliest called on empty array')
|
|
419
|
-
|
|
420
|
-
return items
|
|
421
|
-
.map(i => LocalTime.of(i))
|
|
422
|
-
.reduce((min, item) => (min.isSameOrBefore(item) ? min : item))
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
static latestOrUndefined(items: LocalTimeInput[]): LocalTime | undefined {
|
|
426
|
-
return items.length ? LocalTime.latest(items) : undefined
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
static latest(items: LocalTimeInput[]): LocalTime {
|
|
430
|
-
_assert(items.length, 'LocalTime.latest called on empty array')
|
|
431
|
-
|
|
432
|
-
return items
|
|
433
|
-
.map(i => LocalTime.of(i))
|
|
434
|
-
.reduce((max, item) => (max.isSameOrAfter(item) ? max : item))
|
|
308
|
+
return localDate.getMonthLength(this.$date.getFullYear(), this.$date.getMonth() + 1)
|
|
435
309
|
}
|
|
436
310
|
|
|
437
311
|
isSame(d: LocalTimeInput): boolean {
|
|
@@ -475,14 +349,14 @@ export class LocalTime {
|
|
|
475
349
|
* Third argument allows to override "now".
|
|
476
350
|
*/
|
|
477
351
|
isOlderThan(n: number, unit: LocalTimeUnit, now?: LocalTimeInput): boolean {
|
|
478
|
-
return this.isBefore(
|
|
352
|
+
return this.isBefore(localTime.of(now ?? new Date()).plus(-n, unit))
|
|
479
353
|
}
|
|
480
354
|
|
|
481
355
|
/**
|
|
482
356
|
* Checks if this localTime is same or older (<=) than "now" by X units.
|
|
483
357
|
*/
|
|
484
358
|
isSameOrOlderThan(n: number, unit: LocalTimeUnit, now?: LocalTimeInput): boolean {
|
|
485
|
-
return this.isSameOrBefore(
|
|
359
|
+
return this.isSameOrBefore(localTime.of(now ?? new Date()).plus(-n, unit))
|
|
486
360
|
}
|
|
487
361
|
|
|
488
362
|
/**
|
|
@@ -495,14 +369,14 @@ export class LocalTime {
|
|
|
495
369
|
* Third argument allows to override "now".
|
|
496
370
|
*/
|
|
497
371
|
isYoungerThan(n: number, unit: LocalTimeUnit, now?: LocalTimeInput): boolean {
|
|
498
|
-
return this.isAfter(
|
|
372
|
+
return this.isAfter(localTime.of(now ?? new Date()).plus(-n, unit))
|
|
499
373
|
}
|
|
500
374
|
|
|
501
375
|
/**
|
|
502
376
|
* Checks if this localTime is same or younger (>=) than "now" by X units.
|
|
503
377
|
*/
|
|
504
378
|
isSameOrYoungerThan(n: number, unit: LocalTimeUnit, now?: LocalTimeInput): boolean {
|
|
505
|
-
return this.isSameOrAfter(
|
|
379
|
+
return this.isSameOrAfter(localTime.of(now ?? new Date()).plus(-n, unit))
|
|
506
380
|
}
|
|
507
381
|
|
|
508
382
|
/**
|
|
@@ -512,19 +386,15 @@ export class LocalTime {
|
|
|
512
386
|
*/
|
|
513
387
|
cmp(d: LocalTimeInput): -1 | 0 | 1 {
|
|
514
388
|
const t1 = this.$date.valueOf()
|
|
515
|
-
const t2 =
|
|
389
|
+
const t2 = localTime.parseToDate(d).valueOf()
|
|
516
390
|
if (t1 === t2) return 0
|
|
517
391
|
return t1 < t2 ? -1 : 1
|
|
518
392
|
}
|
|
519
393
|
|
|
520
394
|
components(): LocalTimeComponents {
|
|
521
395
|
return {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
day: this.$date.getDate(),
|
|
525
|
-
hour: this.$date.getHours(),
|
|
526
|
-
minute: this.$date.getMinutes(),
|
|
527
|
-
second: this.$date.getSeconds(),
|
|
396
|
+
...this.dateComponents(),
|
|
397
|
+
...this.timeComponents(),
|
|
528
398
|
}
|
|
529
399
|
}
|
|
530
400
|
|
|
@@ -545,7 +415,7 @@ export class LocalTime {
|
|
|
545
415
|
}
|
|
546
416
|
|
|
547
417
|
fromNow(now: LocalTimeInput = new Date()): string {
|
|
548
|
-
const msDiff =
|
|
418
|
+
const msDiff = localTime.parseToDate(now).valueOf() - this.$date.valueOf()
|
|
549
419
|
|
|
550
420
|
if (msDiff === 0) return 'now'
|
|
551
421
|
|
|
@@ -577,7 +447,7 @@ export class LocalTime {
|
|
|
577
447
|
}
|
|
578
448
|
|
|
579
449
|
toLocalDate(): LocalDate {
|
|
580
|
-
return
|
|
450
|
+
return localDate.fromDate(this.$date)
|
|
581
451
|
}
|
|
582
452
|
|
|
583
453
|
/**
|
|
@@ -673,66 +543,151 @@ export class LocalTime {
|
|
|
673
543
|
}
|
|
674
544
|
}
|
|
675
545
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
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)
|
|
682
553
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
export function localTimeNow(): LocalTime {
|
|
687
|
-
return LocalTime.now()
|
|
688
|
-
}
|
|
554
|
+
_assert(t !== null, `Cannot parse "${d}" into LocalTime`, {
|
|
555
|
+
input: d,
|
|
556
|
+
})
|
|
689
557
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
*
|
|
693
|
-
* `localTime` function will instead return LocalTime of `now` for falsy input.
|
|
694
|
-
*/
|
|
695
|
-
export function localTimeOrUndefined(d?: LocalTimeInput | null): LocalTime | undefined {
|
|
696
|
-
return d ? LocalTime.of(d) : undefined
|
|
697
|
-
}
|
|
558
|
+
return t
|
|
559
|
+
}
|
|
698
560
|
|
|
699
|
-
/**
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
}
|
|
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
|
+
}
|
|
705
567
|
|
|
706
|
-
/**
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}
|
|
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
|
|
713
574
|
|
|
714
|
-
|
|
715
|
-
* UTC offset is the opposite of "timezone offset" - it's the number of minutes to add
|
|
716
|
-
* to the local time to get UTC time.
|
|
717
|
-
*
|
|
718
|
-
* E.g utcOffset for CEST is -120,
|
|
719
|
-
* which means that you need to add -120 minutes to the local time to get UTC time.
|
|
720
|
-
*
|
|
721
|
-
* Instead of -0 it returns 0, for the peace of mind and less weird test/snapshot differences.
|
|
722
|
-
*/
|
|
723
|
-
export function getUTCOffsetMinutes(): NumberOfMinutes {
|
|
724
|
-
return -new Date().getTimezoneOffset() || 0
|
|
725
|
-
}
|
|
575
|
+
let date
|
|
726
576
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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
|
+
}
|
|
736
691
|
}
|
|
737
692
|
|
|
738
693
|
// based on: https://github.com/date-fns/date-fns/blob/master/src/getISOWeek/index.ts
|
|
@@ -824,7 +779,7 @@ function addMonths(d: Date, num: number, mutate = false): Date {
|
|
|
824
779
|
month += 12
|
|
825
780
|
}
|
|
826
781
|
|
|
827
|
-
const monthLen =
|
|
782
|
+
const monthLen = localDate.getMonthLength(year, month)
|
|
828
783
|
if (day > monthLen) day = monthLen
|
|
829
784
|
|
|
830
785
|
d.setFullYear(year, month - 1, day)
|
|
@@ -839,3 +794,47 @@ function differenceInMonths(a: Date, b: Date): number {
|
|
|
839
794
|
const anchor2 = addMonths(a, wholeMonthDiff + sign).getTime()
|
|
840
795
|
return -(wholeMonthDiff + ((b.getTime() - anchor) / (anchor2 - anchor)) * sign)
|
|
841
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'
|