@naturalcycles/js-lib 14.91.1 → 14.93.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.
@@ -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);
@@ -283,6 +289,9 @@ export class LocalDate {
283
289
  toLocalTime() {
284
290
  return LocalTime.of(this.toDate());
285
291
  }
292
+ toISODate() {
293
+ return this.toString();
294
+ }
286
295
  toString() {
287
296
  return [
288
297
  String(this.year).padStart(4, '0'),
@@ -297,6 +306,13 @@ export class LocalDate {
297
306
  String(this.day).padStart(2, '0'),
298
307
  ].join('');
299
308
  }
309
+ // May be not optimal, as LocalTime better suits it
310
+ unix() {
311
+ return Math.floor(this.toDate().valueOf() / 1000);
312
+ }
313
+ unixMillis() {
314
+ return this.toDate().valueOf();
315
+ }
300
316
  toJSON() {
301
317
  return this.toString();
302
318
  }
@@ -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
- return new LocalTime(date);
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 this.of(new Date());
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.$date.getFullYear() : this.set('year', v);
123
+ return v === undefined ? this.get('year') : this.set('year', v);
109
124
  }
110
125
  month(v) {
111
- return v === undefined ? this.$date.getMonth() + 1 : this.set('month', v);
126
+ return v === undefined ? this.get('month') : this.set('month', v);
112
127
  }
113
128
  date(v) {
114
- return v === undefined ? this.$date.getDate() : this.set('day', v);
129
+ return v === undefined ? this.get('day') : this.set('day', v);
115
130
  }
116
131
  hour(v) {
117
- return v === undefined ? this.$date.getHours() : this.set('hour', v);
132
+ return v === undefined ? this.get('hour') : this.set('hour', v);
118
133
  }
119
134
  minute(v) {
120
- return v === undefined ? this.$date.getMinutes() : this.set('minute', v);
135
+ return v === undefined ? this.get('minute') : this.set('minute', v);
121
136
  }
122
137
  second(v) {
123
- return v === undefined ? this.$date.getSeconds() : this.set('second', v);
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
- return mutate ? this : new LocalTime(d);
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(-num, unit, mutate);
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
- if (mutate) {
190
- const d = this.$date;
191
- d.setSeconds(0);
192
- if (unit === 'minute')
193
- return this;
194
- d.setMinutes(0);
195
- if (unit === 'hour')
196
- return this;
197
- d.setHours(0);
198
- if (unit === 'day')
199
- return this;
200
- d.setDate(0);
201
- if (unit === 'month')
202
- return this;
203
- d.setMonth(0);
204
- return this;
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;
@@ -274,6 +274,16 @@ export class LocalTime {
274
274
  }
275
275
  // todo: endOf
276
276
  components() {
277
+ if (this.utcMode) {
278
+ return {
279
+ year: this.$date.getUTCFullYear(),
280
+ month: this.$date.getUTCMonth() + 1,
281
+ day: this.$date.getUTCDate(),
282
+ hour: this.$date.getUTCHours(),
283
+ minute: this.$date.getUTCMinutes(),
284
+ second: this.$date.getSeconds(),
285
+ };
286
+ }
277
287
  return {
278
288
  year: this.$date.getFullYear(),
279
289
  month: this.$date.getMonth() + 1,
@@ -283,27 +293,56 @@ export class LocalTime {
283
293
  second: this.$date.getSeconds(),
284
294
  };
285
295
  }
296
+ fromNow(now = LocalTime.now()) {
297
+ const msDiff = LocalTime.of(now).unixMillis() - this.unixMillis();
298
+ if (msDiff === 0)
299
+ return 'now';
300
+ if (msDiff >= 0) {
301
+ return `${_ms(msDiff)} ago`;
302
+ }
303
+ return `in ${_ms(msDiff * -1)}`;
304
+ }
286
305
  getDate() {
287
306
  return this.$date;
288
307
  }
289
308
  clone() {
290
- return new LocalTime(new Date(this.$date));
309
+ return new LocalTime(new Date(this.$date), this.utcMode);
291
310
  }
292
311
  unix() {
293
312
  return Math.floor(this.$date.valueOf() / 1000);
294
313
  }
314
+ unixMillis() {
315
+ return this.$date.valueOf();
316
+ }
295
317
  valueOf() {
296
318
  return Math.floor(this.$date.valueOf() / 1000);
297
319
  }
298
320
  toLocalDate() {
321
+ if (this.utcMode) {
322
+ return LocalDate.create(this.$date.getUTCFullYear(), this.$date.getUTCMonth() + 1, this.$date.getUTCDate());
323
+ }
299
324
  return LocalDate.create(this.$date.getFullYear(), this.$date.getMonth() + 1, this.$date.getDate());
300
325
  }
301
326
  toPretty(seconds = true) {
302
- return this.$date
303
- .toISOString()
304
- .slice(0, seconds ? 19 : 16)
305
- .split('T')
306
- .join(' ');
327
+ const { year, month, day, hour, minute, second } = this.components();
328
+ return ([
329
+ String(year).padStart(4, '0'),
330
+ String(month).padStart(2, '0'),
331
+ String(day).padStart(2, '0'),
332
+ ].join('-') +
333
+ ' ' +
334
+ [
335
+ String(hour).padStart(2, '0'),
336
+ String(minute).padStart(2, '0'),
337
+ seconds && String(second).padStart(2, '0'),
338
+ ]
339
+ .filter(Boolean)
340
+ .join(':'));
341
+ // return this.$date
342
+ // .toISOString()
343
+ // .slice(0, seconds ? 19 : 16)
344
+ // .split('T')
345
+ // .join(' ')
307
346
  }
308
347
  /**
309
348
  * Returns e.g: `1984-06-21T17:56:21`, only the date part of DateTime
@@ -315,20 +354,41 @@ export class LocalTime {
315
354
  * Returns e.g: `1984-06-21`, only the date part of DateTime
316
355
  */
317
356
  toISODate() {
318
- return this.$date.toISOString().slice(0, 10);
357
+ const { year, month, day } = this.components();
358
+ return [
359
+ String(year).padStart(4, '0'),
360
+ String(month).padStart(2, '0'),
361
+ String(day).padStart(2, '0'),
362
+ ].join('-');
363
+ // return this.$date.toISOString().slice(0, 10)
364
+ }
365
+ /**
366
+ * Returns e.g: `17:03:15` (or `17:03` with seconds=false)
367
+ */
368
+ toISOTime(seconds = true) {
369
+ // return this.$date.toISOString().slice(11, seconds ? 19 : 16)
370
+ const { hour, minute, second } = this.components();
371
+ return [
372
+ String(hour).padStart(2, '0'),
373
+ String(minute).padStart(2, '0'),
374
+ seconds && String(second).padStart(2, '0'),
375
+ ]
376
+ .filter(Boolean)
377
+ .join(':');
319
378
  }
320
379
  /**
321
380
  * Returns e.g: `19840621_1705`
322
381
  */
323
382
  toStringCompact(seconds = false) {
383
+ const { year, month, day, hour, minute, second } = this.components();
324
384
  return [
325
- String(this.$date.getFullYear()).padStart(4, '0'),
326
- String(this.$date.getMonth() + 1).padStart(2, '0'),
327
- String(this.$date.getDate()).padStart(2, '0'),
385
+ String(year).padStart(4, '0'),
386
+ String(month).padStart(2, '0'),
387
+ String(day).padStart(2, '0'),
328
388
  '_',
329
- String(this.$date.getHours()).padStart(2, '0'),
330
- String(this.$date.getMinutes()).padStart(2, '0'),
331
- seconds ? String(this.$date.getSeconds()).padStart(2, '0') : '',
389
+ String(hour).padStart(2, '0'),
390
+ String(minute).padStart(2, '0'),
391
+ seconds ? String(second).padStart(2, '0') : '',
332
392
  ].join('');
333
393
  }
334
394
  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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.91.1",
3
+ "version": "14.93.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -0,0 +1,68 @@
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
+ * DateIntervals compare by start date.
54
+ * If it's the same - then by end date.
55
+ */
56
+ cmp(d: DateIntervalConfig): -1 | 0 | 1 {
57
+ d = DateInterval.parse(d)
58
+ return this.start.cmp(d.start) || this.end.cmp(d.end)
59
+ }
60
+
61
+ toString(): string {
62
+ return [this.start, this.end].join('/')
63
+ }
64
+
65
+ private toJSON(): string {
66
+ return this.toString()
67
+ }
68
+ }
@@ -1,6 +1,6 @@
1
1
  import { _assert } from '../error/assert'
2
2
  import { Sequence } from '../seq/seq'
3
- import { END, IsoDate } from '../types'
3
+ import { END, IsoDate, UnixTimestamp } from '../types'
4
4
  import { LocalTime } from './localTime'
5
5
 
6
6
  export type LocalDateUnit = 'year' | 'month' | 'day'
@@ -47,6 +47,10 @@ export class LocalDate {
47
47
  return new LocalDate(d.getFullYear(), d.getMonth() + 1, d.getDate())
48
48
  }
49
49
 
50
+ static fromDateUTC(d: Date): LocalDate {
51
+ return new LocalDate(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate())
52
+ }
53
+
50
54
  /**
51
55
  * Returns null if invalid.
52
56
  */
@@ -79,6 +83,10 @@ export class LocalDate {
79
83
  return this.fromDate(new Date())
80
84
  }
81
85
 
86
+ static todayUTC(): LocalDate {
87
+ return this.fromDateUTC(new Date())
88
+ }
89
+
82
90
  static sort(items: LocalDate[], mutate = false, descending = false): LocalDate[] {
83
91
  const mod = descending ? -1 : 1
84
92
  return (mutate ? items : [...items]).sort((a, b) => a.cmp(b) * mod)
@@ -361,6 +369,10 @@ export class LocalDate {
361
369
  return LocalTime.of(this.toDate())
362
370
  }
363
371
 
372
+ toISODate(): IsoDate {
373
+ return this.toString()
374
+ }
375
+
364
376
  toString(): IsoDate {
365
377
  return [
366
378
  String(this.year).padStart(4, '0'),
@@ -377,6 +389,15 @@ export class LocalDate {
377
389
  ].join('')
378
390
  }
379
391
 
392
+ // May be not optimal, as LocalTime better suits it
393
+ unix(): UnixTimestamp {
394
+ return Math.floor(this.toDate().valueOf() / 1000)
395
+ }
396
+
397
+ unixMillis(): number {
398
+ return this.toDate().valueOf()
399
+ }
400
+
380
401
  toJSON(): IsoDate {
381
402
  return this.toString()
382
403
  }