@naturalcycles/js-lib 14.91.2 → 14.94.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 +38 -0
- package/dist/datetime/dateInterval.js +80 -0
- package/dist/datetime/localDate.d.ts +4 -0
- package/dist/datetime/localDate.js +15 -0
- package/dist/datetime/localTime.d.ts +10 -2
- package/dist/datetime/localTime.js +146 -80
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -0
- package/dist-esm/datetime/dateInterval.js +76 -0
- package/dist-esm/datetime/localDate.js +15 -0
- package/dist-esm/datetime/localTime.js +146 -80
- package/dist-esm/index.js +1 -0
- package/package.json +1 -1
- package/src/datetime/dateInterval.ts +91 -0
- package/src/datetime/localDate.ts +17 -0
- package/src/datetime/localTime.ts +165 -75
- package/src/index.ts +5 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { LocalDate } from './localDate';
|
|
2
|
+
/**
|
|
3
|
+
* Class that supports ISO8601 "Time interval" standard that looks like `2022-02-24/2022-03-30`.
|
|
4
|
+
*
|
|
5
|
+
* @experimental
|
|
6
|
+
*/
|
|
7
|
+
export class DateInterval {
|
|
8
|
+
constructor(start, end) {
|
|
9
|
+
this.start = start;
|
|
10
|
+
this.end = end;
|
|
11
|
+
}
|
|
12
|
+
static of(start, end) {
|
|
13
|
+
return new DateInterval(LocalDate.of(start), LocalDate.of(end));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parses string like `2022-02-24/2023-03-30` into a DateInterval.
|
|
17
|
+
*/
|
|
18
|
+
static parse(d) {
|
|
19
|
+
if (d instanceof DateInterval)
|
|
20
|
+
return d;
|
|
21
|
+
const [start, end] = d.split('/');
|
|
22
|
+
if (!end || !start) {
|
|
23
|
+
throw new Error(`Cannot parse "${d}" into DateInterval`);
|
|
24
|
+
}
|
|
25
|
+
return new DateInterval(LocalDate.of(start), LocalDate.of(end));
|
|
26
|
+
}
|
|
27
|
+
isSame(d) {
|
|
28
|
+
return this.cmp(d) === 0;
|
|
29
|
+
}
|
|
30
|
+
isBefore(d) {
|
|
31
|
+
return this.cmp(d) === -1;
|
|
32
|
+
}
|
|
33
|
+
isSameOrBefore(d) {
|
|
34
|
+
return this.cmp(d) <= 0;
|
|
35
|
+
}
|
|
36
|
+
isAfter(d) {
|
|
37
|
+
return this.cmp(d) === 1;
|
|
38
|
+
}
|
|
39
|
+
isSameOrAfter(d) {
|
|
40
|
+
return this.cmp(d) >= 0;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Ranges of DateInterval (start, end) are INCLUSIVE.
|
|
44
|
+
*/
|
|
45
|
+
includes(d) {
|
|
46
|
+
d = LocalDate.of(d);
|
|
47
|
+
return d.isSameOrAfter(this.start) && d.isSameOrBefore(this.end);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* DateIntervals compare by start date.
|
|
51
|
+
* If it's the same - then by end date.
|
|
52
|
+
*/
|
|
53
|
+
cmp(d) {
|
|
54
|
+
d = DateInterval.parse(d);
|
|
55
|
+
return this.start.cmp(d.start) || this.end.cmp(d.end);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Returns an array of LocalDates that are included in the interval.
|
|
59
|
+
* Ranges are INCLUSIVE.
|
|
60
|
+
*/
|
|
61
|
+
getDays() {
|
|
62
|
+
const days = [];
|
|
63
|
+
let current = this.start;
|
|
64
|
+
do {
|
|
65
|
+
days.push(current);
|
|
66
|
+
current = current.add(1, 'day');
|
|
67
|
+
} while (current.isSameOrBefore(this.end));
|
|
68
|
+
return days;
|
|
69
|
+
}
|
|
70
|
+
toString() {
|
|
71
|
+
return [this.start, this.end].join('/');
|
|
72
|
+
}
|
|
73
|
+
toJSON() {
|
|
74
|
+
return this.toString();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -36,6 +36,9 @@ export class LocalDate {
|
|
|
36
36
|
static fromDate(d) {
|
|
37
37
|
return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate());
|
|
38
38
|
}
|
|
39
|
+
static fromDateUTC(d) {
|
|
40
|
+
return new LocalDate(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate());
|
|
41
|
+
}
|
|
39
42
|
/**
|
|
40
43
|
* Returns null if invalid.
|
|
41
44
|
*/
|
|
@@ -61,6 +64,9 @@ export class LocalDate {
|
|
|
61
64
|
static today() {
|
|
62
65
|
return this.fromDate(new Date());
|
|
63
66
|
}
|
|
67
|
+
static todayUTC() {
|
|
68
|
+
return this.fromDateUTC(new Date());
|
|
69
|
+
}
|
|
64
70
|
static sort(items, mutate = false, descending = false) {
|
|
65
71
|
const mod = descending ? -1 : 1;
|
|
66
72
|
return (mutate ? items : [...items]).sort((a, b) => a.cmp(b) * mod);
|
|
@@ -122,6 +128,15 @@ export class LocalDate {
|
|
|
122
128
|
isSameOrAfter(d) {
|
|
123
129
|
return this.cmp(d) >= 0;
|
|
124
130
|
}
|
|
131
|
+
isBetween(min, max, incl = '[)') {
|
|
132
|
+
let r = this.cmp(min);
|
|
133
|
+
if (r < 0 || (r === 0 && incl[0] === '('))
|
|
134
|
+
return false;
|
|
135
|
+
r = this.cmp(max);
|
|
136
|
+
if (r > 0 || (r === 0 && incl[1] === ')'))
|
|
137
|
+
return false;
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
125
140
|
/**
|
|
126
141
|
* Returns 1 if this > d
|
|
127
142
|
* returns 0 if they are equal
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { _assert } from '../error/assert';
|
|
2
|
+
import { _ms } from '../time/time.util';
|
|
2
3
|
import { LocalDate } from './localDate';
|
|
3
4
|
/* eslint-disable no-dupe-class-members */
|
|
4
5
|
// Design choices:
|
|
@@ -10,12 +11,16 @@ import { LocalDate } from './localDate';
|
|
|
10
11
|
// .valueOf returns unix timestamp (no millis)
|
|
11
12
|
// Prevents dayjs(undefined) being dayjs.now()
|
|
12
13
|
// Validates on parse, throws if invalid. Doesn't allow invalid objects
|
|
14
|
+
// No arbitrary .format('') (which is slow to parse) vs well-defined (opinionated) formats
|
|
15
|
+
// Separate LocalTime, powered by native js Date, and LocalDate - a smaller functionality class when
|
|
16
|
+
// you only need "whole dates", no time, no timezone worry
|
|
13
17
|
/**
|
|
14
18
|
* @experimental
|
|
15
19
|
*/
|
|
16
20
|
export class LocalTime {
|
|
17
|
-
constructor($date) {
|
|
21
|
+
constructor($date, utcMode) {
|
|
18
22
|
this.$date = $date;
|
|
23
|
+
this.utcMode = utcMode;
|
|
19
24
|
}
|
|
20
25
|
/**
|
|
21
26
|
* Parses input String into LocalDate.
|
|
@@ -28,6 +33,14 @@ export class LocalTime {
|
|
|
28
33
|
}
|
|
29
34
|
return t;
|
|
30
35
|
}
|
|
36
|
+
utc() {
|
|
37
|
+
this.utcMode = true;
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
local() {
|
|
41
|
+
this.utcMode = false;
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
31
44
|
/**
|
|
32
45
|
* Returns null if invalid
|
|
33
46
|
*/
|
|
@@ -49,106 +62,110 @@ export class LocalTime {
|
|
|
49
62
|
// throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
50
63
|
return null;
|
|
51
64
|
}
|
|
52
|
-
|
|
65
|
+
// if (utc) {
|
|
66
|
+
// date.setMinutes(date.getMinutes() + date.getTimezoneOffset())
|
|
67
|
+
// }
|
|
68
|
+
return new LocalTime(date, false);
|
|
53
69
|
}
|
|
54
70
|
static isValid(d) {
|
|
55
71
|
return this.parseOrNull(d) !== null;
|
|
56
72
|
}
|
|
57
|
-
static unix(ts) {
|
|
58
|
-
return new LocalTime(new Date(ts * 1000));
|
|
59
|
-
}
|
|
60
73
|
static now() {
|
|
61
|
-
return
|
|
74
|
+
return new LocalTime(new Date(), false);
|
|
62
75
|
}
|
|
63
76
|
static fromComponents(c) {
|
|
64
|
-
return new LocalTime(new Date(c.year, c.month - 1, c.day, c.hour, c.minute, c.second));
|
|
77
|
+
return new LocalTime(new Date(c.year, c.month - 1, c.day, c.hour, c.minute, c.second), false);
|
|
65
78
|
}
|
|
66
79
|
get(unit) {
|
|
67
80
|
if (unit === 'year') {
|
|
68
|
-
return this.$date.getFullYear();
|
|
81
|
+
return this.utcMode ? this.$date.getUTCFullYear() : this.$date.getFullYear();
|
|
69
82
|
}
|
|
70
83
|
if (unit === 'month') {
|
|
71
|
-
return this.$date.getMonth() + 1;
|
|
84
|
+
return (this.utcMode ? this.$date.getUTCMonth() : this.$date.getMonth()) + 1;
|
|
72
85
|
}
|
|
73
86
|
if (unit === 'day') {
|
|
74
|
-
return this.$date.getDate();
|
|
87
|
+
return this.utcMode ? this.$date.getUTCDate() : this.$date.getDate();
|
|
75
88
|
}
|
|
76
89
|
if (unit === 'hour') {
|
|
77
|
-
return this.$date.getHours();
|
|
90
|
+
return this.utcMode ? this.$date.getUTCHours() : this.$date.getHours();
|
|
78
91
|
}
|
|
79
92
|
if (unit === 'minute') {
|
|
80
|
-
return this.$date.getMinutes();
|
|
93
|
+
return this.utcMode ? this.$date.getUTCMinutes() : this.$date.getMinutes();
|
|
81
94
|
}
|
|
82
95
|
// second
|
|
83
|
-
return this.$date.getSeconds();
|
|
96
|
+
return this.utcMode ? this.$date.getUTCSeconds() : this.$date.getSeconds();
|
|
84
97
|
}
|
|
85
98
|
set(unit, v, mutate = false) {
|
|
86
99
|
const t = mutate ? this : this.clone();
|
|
100
|
+
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
|
87
101
|
if (unit === 'year') {
|
|
88
|
-
t.$date.setFullYear(v);
|
|
102
|
+
this.utcMode ? t.$date.setUTCFullYear(v) : t.$date.setFullYear(v);
|
|
89
103
|
}
|
|
90
104
|
else if (unit === 'month') {
|
|
91
|
-
t.$date.setMonth(v - 1);
|
|
105
|
+
this.utcMode ? t.$date.setUTCMonth(v - 1) : t.$date.setMonth(v - 1);
|
|
92
106
|
}
|
|
93
107
|
else if (unit === 'day') {
|
|
94
|
-
t.$date.setDate(v);
|
|
108
|
+
this.utcMode ? t.$date.setUTCDate(v) : t.$date.setDate(v);
|
|
95
109
|
}
|
|
96
110
|
else if (unit === 'hour') {
|
|
97
|
-
t.$date.setHours(v);
|
|
111
|
+
this.utcMode ? t.$date.setUTCHours(v) : t.$date.setHours(v);
|
|
98
112
|
}
|
|
99
113
|
else if (unit === 'minute') {
|
|
100
|
-
t.$date.setMinutes(v);
|
|
114
|
+
this.utcMode ? t.$date.setUTCMinutes(v) : t.$date.setMinutes(v);
|
|
101
115
|
}
|
|
102
116
|
else if (unit === 'second') {
|
|
103
|
-
t.$date.setSeconds(v);
|
|
117
|
+
this.utcMode ? t.$date.setUTCSeconds(v) : t.$date.setSeconds(v);
|
|
104
118
|
}
|
|
119
|
+
/* eslint-enable @typescript-eslint/no-unused-expressions */
|
|
105
120
|
return t;
|
|
106
121
|
}
|
|
107
122
|
year(v) {
|
|
108
|
-
return v === undefined ? this
|
|
123
|
+
return v === undefined ? this.get('year') : this.set('year', v);
|
|
109
124
|
}
|
|
110
125
|
month(v) {
|
|
111
|
-
return v === undefined ? this
|
|
126
|
+
return v === undefined ? this.get('month') : this.set('month', v);
|
|
112
127
|
}
|
|
113
128
|
date(v) {
|
|
114
|
-
return v === undefined ? this
|
|
129
|
+
return v === undefined ? this.get('day') : this.set('day', v);
|
|
115
130
|
}
|
|
116
131
|
hour(v) {
|
|
117
|
-
return v === undefined ? this
|
|
132
|
+
return v === undefined ? this.get('hour') : this.set('hour', v);
|
|
118
133
|
}
|
|
119
134
|
minute(v) {
|
|
120
|
-
return v === undefined ? this
|
|
135
|
+
return v === undefined ? this.get('minute') : this.set('minute', v);
|
|
121
136
|
}
|
|
122
137
|
second(v) {
|
|
123
|
-
return v === undefined ? this
|
|
138
|
+
return v === undefined ? this.get('second') : this.set('second', v);
|
|
124
139
|
}
|
|
125
140
|
setComponents(c, mutate = false) {
|
|
126
141
|
const d = mutate ? this.$date : new Date(this.$date);
|
|
142
|
+
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
|
127
143
|
if (c.year) {
|
|
128
|
-
d.setFullYear(c.year);
|
|
144
|
+
this.utcMode ? d.setUTCFullYear(c.year) : d.setFullYear(c.year);
|
|
129
145
|
}
|
|
130
146
|
if (c.month) {
|
|
131
|
-
d.setMonth(c.month - 1);
|
|
147
|
+
this.utcMode ? d.setUTCMonth(c.month - 1) : d.setMonth(c.month - 1);
|
|
132
148
|
}
|
|
133
149
|
if (c.day) {
|
|
134
|
-
d.setDate(c.day);
|
|
150
|
+
this.utcMode ? d.setUTCDate(c.day) : d.setDate(c.day);
|
|
135
151
|
}
|
|
136
152
|
if (c.hour !== undefined) {
|
|
137
|
-
d.setHours(c.hour);
|
|
153
|
+
this.utcMode ? d.setUTCHours(c.hour) : d.setHours(c.hour);
|
|
138
154
|
}
|
|
139
155
|
if (c.minute !== undefined) {
|
|
140
|
-
d.setMinutes(c.minute);
|
|
156
|
+
this.utcMode ? d.setUTCMinutes(c.minute) : d.setMinutes(c.minute);
|
|
141
157
|
}
|
|
142
158
|
if (c.second !== undefined) {
|
|
143
|
-
d.setSeconds(c.second);
|
|
159
|
+
this.utcMode ? d.setUTCSeconds(c.second) : d.setSeconds(c.second);
|
|
144
160
|
}
|
|
145
|
-
|
|
161
|
+
/* eslint-enable @typescript-eslint/no-unused-expressions */
|
|
162
|
+
return mutate ? this : new LocalTime(d, this.utcMode);
|
|
146
163
|
}
|
|
147
164
|
add(num, unit, mutate = false) {
|
|
148
165
|
return this.set(unit, this.get(unit) + num, mutate);
|
|
149
166
|
}
|
|
150
167
|
subtract(num, unit, mutate = false) {
|
|
151
|
-
return this.add(-
|
|
168
|
+
return this.add(num * -1, unit, mutate);
|
|
152
169
|
}
|
|
153
170
|
absDiff(other, unit) {
|
|
154
171
|
return Math.abs(this.diff(other, unit));
|
|
@@ -186,40 +203,23 @@ export class LocalTime {
|
|
|
186
203
|
startOf(unit, mutate = false) {
|
|
187
204
|
if (unit === 'second')
|
|
188
205
|
return this;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const c = this.components();
|
|
207
|
-
c.second = 0;
|
|
208
|
-
if (unit === 'year') {
|
|
209
|
-
c.month = c.day = 1;
|
|
210
|
-
c.hour = c.minute = 0;
|
|
211
|
-
}
|
|
212
|
-
else if (unit === 'month') {
|
|
213
|
-
c.day = 1;
|
|
214
|
-
c.hour = c.minute = 0;
|
|
215
|
-
}
|
|
216
|
-
else if (unit === 'day') {
|
|
217
|
-
c.hour = c.minute = 0;
|
|
218
|
-
}
|
|
219
|
-
else if (unit === 'hour') {
|
|
220
|
-
c.minute = 0;
|
|
221
|
-
}
|
|
222
|
-
return LocalTime.fromComponents(c);
|
|
206
|
+
const d = mutate ? this.$date : new Date(this.$date);
|
|
207
|
+
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
|
208
|
+
this.utcMode ? d.setUTCSeconds(0) : d.setSeconds(0);
|
|
209
|
+
if (unit !== 'minute') {
|
|
210
|
+
this.utcMode ? d.setUTCMinutes(0) : d.setMinutes(0);
|
|
211
|
+
if (unit !== 'hour') {
|
|
212
|
+
this.utcMode ? d.setUTCHours(0) : d.setHours(0);
|
|
213
|
+
if (unit !== 'day') {
|
|
214
|
+
this.utcMode ? d.setUTCDate(0) : d.setDate(0);
|
|
215
|
+
if (unit !== 'month') {
|
|
216
|
+
this.utcMode ? d.setUTCMonth(0) : d.setMonth(0);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/* eslint-enable @typescript-eslint/no-unused-expressions */
|
|
222
|
+
return mutate ? this : new LocalTime(d, this.utcMode);
|
|
223
223
|
}
|
|
224
224
|
static sort(items, mutate = false, descending = false) {
|
|
225
225
|
const mod = descending ? -1 : 1;
|
|
@@ -260,6 +260,15 @@ export class LocalTime {
|
|
|
260
260
|
isSameOrAfter(d) {
|
|
261
261
|
return this.cmp(d) >= 0;
|
|
262
262
|
}
|
|
263
|
+
isBetween(min, max, incl = '[)') {
|
|
264
|
+
let r = this.cmp(min);
|
|
265
|
+
if (r < 0 || (r === 0 && incl[0] === '('))
|
|
266
|
+
return false;
|
|
267
|
+
r = this.cmp(max);
|
|
268
|
+
if (r > 0 || (r === 0 && incl[1] === ')'))
|
|
269
|
+
return false;
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
263
272
|
/**
|
|
264
273
|
* Returns 1 if this > d
|
|
265
274
|
* returns 0 if they are equal
|
|
@@ -274,6 +283,16 @@ export class LocalTime {
|
|
|
274
283
|
}
|
|
275
284
|
// todo: endOf
|
|
276
285
|
components() {
|
|
286
|
+
if (this.utcMode) {
|
|
287
|
+
return {
|
|
288
|
+
year: this.$date.getUTCFullYear(),
|
|
289
|
+
month: this.$date.getUTCMonth() + 1,
|
|
290
|
+
day: this.$date.getUTCDate(),
|
|
291
|
+
hour: this.$date.getUTCHours(),
|
|
292
|
+
minute: this.$date.getUTCMinutes(),
|
|
293
|
+
second: this.$date.getSeconds(),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
277
296
|
return {
|
|
278
297
|
year: this.$date.getFullYear(),
|
|
279
298
|
month: this.$date.getMonth() + 1,
|
|
@@ -283,11 +302,20 @@ export class LocalTime {
|
|
|
283
302
|
second: this.$date.getSeconds(),
|
|
284
303
|
};
|
|
285
304
|
}
|
|
305
|
+
fromNow(now = LocalTime.now()) {
|
|
306
|
+
const msDiff = LocalTime.of(now).unixMillis() - this.unixMillis();
|
|
307
|
+
if (msDiff === 0)
|
|
308
|
+
return 'now';
|
|
309
|
+
if (msDiff >= 0) {
|
|
310
|
+
return `${_ms(msDiff)} ago`;
|
|
311
|
+
}
|
|
312
|
+
return `in ${_ms(msDiff * -1)}`;
|
|
313
|
+
}
|
|
286
314
|
getDate() {
|
|
287
315
|
return this.$date;
|
|
288
316
|
}
|
|
289
317
|
clone() {
|
|
290
|
-
return new LocalTime(new Date(this.$date));
|
|
318
|
+
return new LocalTime(new Date(this.$date), this.utcMode);
|
|
291
319
|
}
|
|
292
320
|
unix() {
|
|
293
321
|
return Math.floor(this.$date.valueOf() / 1000);
|
|
@@ -299,14 +327,31 @@ export class LocalTime {
|
|
|
299
327
|
return Math.floor(this.$date.valueOf() / 1000);
|
|
300
328
|
}
|
|
301
329
|
toLocalDate() {
|
|
330
|
+
if (this.utcMode) {
|
|
331
|
+
return LocalDate.create(this.$date.getUTCFullYear(), this.$date.getUTCMonth() + 1, this.$date.getUTCDate());
|
|
332
|
+
}
|
|
302
333
|
return LocalDate.create(this.$date.getFullYear(), this.$date.getMonth() + 1, this.$date.getDate());
|
|
303
334
|
}
|
|
304
335
|
toPretty(seconds = true) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
.
|
|
308
|
-
.
|
|
309
|
-
.
|
|
336
|
+
const { year, month, day, hour, minute, second } = this.components();
|
|
337
|
+
return ([
|
|
338
|
+
String(year).padStart(4, '0'),
|
|
339
|
+
String(month).padStart(2, '0'),
|
|
340
|
+
String(day).padStart(2, '0'),
|
|
341
|
+
].join('-') +
|
|
342
|
+
' ' +
|
|
343
|
+
[
|
|
344
|
+
String(hour).padStart(2, '0'),
|
|
345
|
+
String(minute).padStart(2, '0'),
|
|
346
|
+
seconds && String(second).padStart(2, '0'),
|
|
347
|
+
]
|
|
348
|
+
.filter(Boolean)
|
|
349
|
+
.join(':'));
|
|
350
|
+
// return this.$date
|
|
351
|
+
// .toISOString()
|
|
352
|
+
// .slice(0, seconds ? 19 : 16)
|
|
353
|
+
// .split('T')
|
|
354
|
+
// .join(' ')
|
|
310
355
|
}
|
|
311
356
|
/**
|
|
312
357
|
* Returns e.g: `1984-06-21T17:56:21`, only the date part of DateTime
|
|
@@ -318,20 +363,41 @@ export class LocalTime {
|
|
|
318
363
|
* Returns e.g: `1984-06-21`, only the date part of DateTime
|
|
319
364
|
*/
|
|
320
365
|
toISODate() {
|
|
321
|
-
|
|
366
|
+
const { year, month, day } = this.components();
|
|
367
|
+
return [
|
|
368
|
+
String(year).padStart(4, '0'),
|
|
369
|
+
String(month).padStart(2, '0'),
|
|
370
|
+
String(day).padStart(2, '0'),
|
|
371
|
+
].join('-');
|
|
372
|
+
// return this.$date.toISOString().slice(0, 10)
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Returns e.g: `17:03:15` (or `17:03` with seconds=false)
|
|
376
|
+
*/
|
|
377
|
+
toISOTime(seconds = true) {
|
|
378
|
+
// return this.$date.toISOString().slice(11, seconds ? 19 : 16)
|
|
379
|
+
const { hour, minute, second } = this.components();
|
|
380
|
+
return [
|
|
381
|
+
String(hour).padStart(2, '0'),
|
|
382
|
+
String(minute).padStart(2, '0'),
|
|
383
|
+
seconds && String(second).padStart(2, '0'),
|
|
384
|
+
]
|
|
385
|
+
.filter(Boolean)
|
|
386
|
+
.join(':');
|
|
322
387
|
}
|
|
323
388
|
/**
|
|
324
389
|
* Returns e.g: `19840621_1705`
|
|
325
390
|
*/
|
|
326
391
|
toStringCompact(seconds = false) {
|
|
392
|
+
const { year, month, day, hour, minute, second } = this.components();
|
|
327
393
|
return [
|
|
328
|
-
String(
|
|
329
|
-
String(
|
|
330
|
-
String(
|
|
394
|
+
String(year).padStart(4, '0'),
|
|
395
|
+
String(month).padStart(2, '0'),
|
|
396
|
+
String(day).padStart(2, '0'),
|
|
331
397
|
'_',
|
|
332
|
-
String(
|
|
333
|
-
String(
|
|
334
|
-
seconds ? String(
|
|
398
|
+
String(hour).padStart(2, '0'),
|
|
399
|
+
String(minute).padStart(2, '0'),
|
|
400
|
+
seconds ? String(second).padStart(2, '0') : '',
|
|
335
401
|
].join('');
|
|
336
402
|
}
|
|
337
403
|
toString() {
|
package/dist-esm/index.js
CHANGED
|
@@ -59,4 +59,5 @@ export * from './math/stack.util';
|
|
|
59
59
|
export * from './string/leven';
|
|
60
60
|
export * from './datetime/localDate';
|
|
61
61
|
export * from './datetime/localTime';
|
|
62
|
+
export * from './datetime/dateInterval';
|
|
62
63
|
export { is, _createPromiseDecorator, _stringMapValues, _stringMapEntries, _objectKeys, pMap, _passthroughMapper, _passUndefinedMapper, _passthroughPredicate, _passNothingPredicate, _noop, ErrorMode, pDefer, AggregatedError, pRetry, pRetryFn, pTimeout, pTimeoutFn, _tryCatch, _TryCatch, _stringifyAny, jsonSchema, JsonSchemaAnyBuilder, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, commonLoggerCreate, PQueue, END, SKIP, };
|
package/package.json
CHANGED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { LocalDate, LocalDateConfig } from './localDate'
|
|
2
|
+
|
|
3
|
+
export type DateIntervalConfig = DateInterval | string
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Class that supports ISO8601 "Time interval" standard that looks like `2022-02-24/2022-03-30`.
|
|
7
|
+
*
|
|
8
|
+
* @experimental
|
|
9
|
+
*/
|
|
10
|
+
export class DateInterval {
|
|
11
|
+
private constructor(public start: LocalDate, public end: LocalDate) {}
|
|
12
|
+
|
|
13
|
+
static of(start: LocalDateConfig, end: LocalDateConfig): DateInterval {
|
|
14
|
+
return new DateInterval(LocalDate.of(start), LocalDate.of(end))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parses string like `2022-02-24/2023-03-30` into a DateInterval.
|
|
19
|
+
*/
|
|
20
|
+
static parse(d: DateIntervalConfig): DateInterval {
|
|
21
|
+
if (d instanceof DateInterval) return d
|
|
22
|
+
|
|
23
|
+
const [start, end] = d.split('/')
|
|
24
|
+
|
|
25
|
+
if (!end || !start) {
|
|
26
|
+
throw new Error(`Cannot parse "${d}" into DateInterval`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return new DateInterval(LocalDate.of(start), LocalDate.of(end))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
isSame(d: DateIntervalConfig): boolean {
|
|
33
|
+
return this.cmp(d) === 0
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
isBefore(d: DateIntervalConfig): boolean {
|
|
37
|
+
return this.cmp(d) === -1
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
isSameOrBefore(d: DateIntervalConfig): boolean {
|
|
41
|
+
return this.cmp(d) <= 0
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
isAfter(d: DateIntervalConfig): boolean {
|
|
45
|
+
return this.cmp(d) === 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
isSameOrAfter(d: DateIntervalConfig): boolean {
|
|
49
|
+
return this.cmp(d) >= 0
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Ranges of DateInterval (start, end) are INCLUSIVE.
|
|
54
|
+
*/
|
|
55
|
+
includes(d: LocalDateConfig): boolean {
|
|
56
|
+
d = LocalDate.of(d)
|
|
57
|
+
return d.isSameOrAfter(this.start) && d.isSameOrBefore(this.end)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* DateIntervals compare by start date.
|
|
62
|
+
* If it's the same - then by end date.
|
|
63
|
+
*/
|
|
64
|
+
cmp(d: DateIntervalConfig): -1 | 0 | 1 {
|
|
65
|
+
d = DateInterval.parse(d)
|
|
66
|
+
return this.start.cmp(d.start) || this.end.cmp(d.end)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Returns an array of LocalDates that are included in the interval.
|
|
71
|
+
* Ranges are INCLUSIVE.
|
|
72
|
+
*/
|
|
73
|
+
getDays(): LocalDate[] {
|
|
74
|
+
const days: LocalDate[] = []
|
|
75
|
+
let current = this.start
|
|
76
|
+
do {
|
|
77
|
+
days.push(current)
|
|
78
|
+
current = current.add(1, 'day')
|
|
79
|
+
} while (current.isSameOrBefore(this.end))
|
|
80
|
+
|
|
81
|
+
return days
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
toString(): string {
|
|
85
|
+
return [this.start, this.end].join('/')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
toJSON(): string {
|
|
89
|
+
return this.toString()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -4,6 +4,7 @@ import { END, IsoDate, UnixTimestamp } from '../types'
|
|
|
4
4
|
import { LocalTime } from './localTime'
|
|
5
5
|
|
|
6
6
|
export type LocalDateUnit = 'year' | 'month' | 'day'
|
|
7
|
+
export type Inclusiveness = '()' | '[]' | '[)' | '(]'
|
|
7
8
|
|
|
8
9
|
const m31 = new Set<number>([1, 3, 5, 7, 8, 10, 12])
|
|
9
10
|
|
|
@@ -47,6 +48,10 @@ export class LocalDate {
|
|
|
47
48
|
return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate())
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
static fromDateUTC(d: Date): LocalDate {
|
|
52
|
+
return new LocalDate(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate())
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
/**
|
|
51
56
|
* Returns null if invalid.
|
|
52
57
|
*/
|
|
@@ -79,6 +84,10 @@ export class LocalDate {
|
|
|
79
84
|
return this.fromDate(new Date())
|
|
80
85
|
}
|
|
81
86
|
|
|
87
|
+
static todayUTC(): LocalDate {
|
|
88
|
+
return this.fromDateUTC(new Date())
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
static sort(items: LocalDate[], mutate = false, descending = false): LocalDate[] {
|
|
83
92
|
const mod = descending ? -1 : 1
|
|
84
93
|
return (mutate ? items : [...items]).sort((a, b) => a.cmp(b) * mod)
|
|
@@ -186,6 +195,14 @@ export class LocalDate {
|
|
|
186
195
|
return this.cmp(d) >= 0
|
|
187
196
|
}
|
|
188
197
|
|
|
198
|
+
isBetween(min: LocalDateConfig, max: LocalDateConfig, incl: Inclusiveness = '[)'): boolean {
|
|
199
|
+
let r = this.cmp(min)
|
|
200
|
+
if (r < 0 || (r === 0 && incl[0] === '(')) return false
|
|
201
|
+
r = this.cmp(max)
|
|
202
|
+
if (r > 0 || (r === 0 && incl[1] === ')')) return false
|
|
203
|
+
return true
|
|
204
|
+
}
|
|
205
|
+
|
|
189
206
|
/**
|
|
190
207
|
* Returns 1 if this > d
|
|
191
208
|
* returns 0 if they are equal
|