@naturalcycles/js-lib 14.98.2 → 14.99.1

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/index.d.ts CHANGED
@@ -64,9 +64,9 @@ export * from './datetime/localDate';
64
64
  export * from './datetime/localTime';
65
65
  export * from './datetime/dateInterval';
66
66
  export * from './datetime/timeInterval';
67
- import { LocalDateConfig, LocalDateUnit, Inclusiveness } from './datetime/localDate';
68
- import { LocalTimeConfig, LocalTimeUnit, LocalTimeComponents } from './datetime/localTime';
67
+ import { LocalDateConfig, LocalDateFormatter, LocalDateUnit, LocalDateUnitStrict, Inclusiveness } from './datetime/localDate';
68
+ import { LocalTimeConfig, LocalTimeFormatter, LocalTimeUnit, LocalTimeComponents, ISODayOfWeek } from './datetime/localTime';
69
69
  import { DateIntervalConfig, DateIntervalString } from './datetime/dateInterval';
70
70
  import { TimeIntervalConfig, TimeIntervalString } from './datetime/timeInterval';
71
- export type { DateIntervalConfig, DateIntervalString, TimeIntervalConfig, TimeIntervalString, LocalDateConfig, LocalDateUnit, Inclusiveness, LocalTimeConfig, LocalTimeUnit, LocalTimeComponents, AbortableMapper, AbortablePredicate, AbortableAsyncPredicate, AbortableAsyncMapper, PQueueCfg, MemoCache, AsyncMemoCache, PromiseDecoratorCfg, PromiseDecoratorResp, ErrorData, ErrorObject, HttpErrorData, HttpErrorResponse, Admin401ErrorData, Admin403ErrorData, StringMap, PromiseMap, AnyObject, AnyFunction, ValuesOf, ValueOf, KeyValueTuple, ObjectMapper, ObjectPredicate, InstanceId, IsoDate, IsoDateString, IsoDateTimeString, Reviver, PMapOptions, Mapper, AsyncMapper, Predicate, AsyncPredicate, BatchResult, DeferredPromise, PRetryOptions, PTimeoutOptions, TryCatchOptions, StringifyAnyOptions, JsonStringifyFunction, Merge, ReadonlyDeep, Promisable, Simplify, ConditionalPick, ConditionalExcept, Class, UnixTimestampNumber, UnixTimestamp, Integer, BaseDBEntity, SavedDBEntity, Saved, Unsaved, UnsavedId, CreatedUpdated, CreatedUpdatedId, ObjectWithId, AnyObjectWithId, JsonSchema, JsonSchemaAny, JsonSchemaOneOf, JsonSchemaAllOf, JsonSchemaAnyOf, JsonSchemaNot, JsonSchemaRef, JsonSchemaConst, JsonSchemaEnum, JsonSchemaString, JsonSchemaNumber, JsonSchemaBoolean, JsonSchemaNull, JsonSchemaRootObject, JsonSchemaObject, JsonSchemaArray, JsonSchemaTuple, JsonSchemaBuilder, CommonLogLevel, CommonLogWithLevelFunction, CommonLogFunction, CommonLogger, };
71
+ export type { DateIntervalConfig, DateIntervalString, TimeIntervalConfig, TimeIntervalString, LocalDateConfig, LocalDateFormatter, LocalDateUnit, LocalDateUnitStrict, Inclusiveness, LocalTimeConfig, LocalTimeFormatter, LocalTimeUnit, ISODayOfWeek, LocalTimeComponents, AbortableMapper, AbortablePredicate, AbortableAsyncPredicate, AbortableAsyncMapper, PQueueCfg, MemoCache, AsyncMemoCache, PromiseDecoratorCfg, PromiseDecoratorResp, ErrorData, ErrorObject, HttpErrorData, HttpErrorResponse, Admin401ErrorData, Admin403ErrorData, StringMap, PromiseMap, AnyObject, AnyFunction, ValuesOf, ValueOf, KeyValueTuple, ObjectMapper, ObjectPredicate, InstanceId, IsoDate, IsoDateString, IsoDateTimeString, Reviver, PMapOptions, Mapper, AsyncMapper, Predicate, AsyncPredicate, BatchResult, DeferredPromise, PRetryOptions, PTimeoutOptions, TryCatchOptions, StringifyAnyOptions, JsonStringifyFunction, Merge, ReadonlyDeep, Promisable, Simplify, ConditionalPick, ConditionalExcept, Class, UnixTimestampNumber, UnixTimestamp, Integer, BaseDBEntity, SavedDBEntity, Saved, Unsaved, UnsavedId, CreatedUpdated, CreatedUpdatedId, ObjectWithId, AnyObjectWithId, JsonSchema, JsonSchemaAny, JsonSchemaOneOf, JsonSchemaAllOf, JsonSchemaAnyOf, JsonSchemaNot, JsonSchemaRef, JsonSchemaConst, JsonSchemaEnum, JsonSchemaString, JsonSchemaNumber, JsonSchemaBoolean, JsonSchemaNull, JsonSchemaRootObject, JsonSchemaObject, JsonSchemaArray, JsonSchemaTuple, JsonSchemaBuilder, CommonLogLevel, CommonLogWithLevelFunction, CommonLogFunction, CommonLogger, };
72
72
  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/dist/index.js CHANGED
@@ -96,3 +96,4 @@ tslib_1.__exportStar(require("./datetime/localDate"), exports);
96
96
  tslib_1.__exportStar(require("./datetime/localTime"), exports);
97
97
  tslib_1.__exportStar(require("./datetime/dateInterval"), exports);
98
98
  tslib_1.__exportStar(require("./datetime/timeInterval"), exports);
99
+ const localTime_1 = require("./datetime/localTime");
@@ -1,4 +1,6 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
2
4
  /// <reference lib="es2018" />
3
5
  /// <reference lib="dom" />
4
6
  import { Class, ObservableLike, Primitive, TypedArray } from '../typeFest';
@@ -237,8 +237,37 @@ export function _shuffle(array, mutate = false) {
237
237
  return a;
238
238
  }
239
239
  /**
240
- * Returns last item of the array (or undefined if array is empty).
240
+ * Returns last item of non-empty array.
241
+ * Throws if array is empty.
241
242
  */
242
243
  export function _last(array) {
244
+ if (!array.length)
245
+ throw new Error('_last called on empty array');
246
+ return array[array.length - 1];
247
+ }
248
+ /**
249
+ * Returns last item of the array (or undefined if array is empty).
250
+ */
251
+ export function _lastOrUndefined(array) {
243
252
  return array[array.length - 1];
244
253
  }
254
+ export function _minOrUndefined(array) {
255
+ if (!array.length)
256
+ return;
257
+ return _min(array);
258
+ }
259
+ export function _min(array) {
260
+ if (!array.length)
261
+ throw new Error('_min called on empty array');
262
+ return array.reduce((min, item) => (min <= item ? min : item));
263
+ }
264
+ export function _maxOrUndefined(array) {
265
+ if (!array.length)
266
+ return;
267
+ return _max(array);
268
+ }
269
+ export function _max(array) {
270
+ if (!array.length)
271
+ throw new Error('_max called on empty array');
272
+ return array.reduce((max, item) => (max >= item ? max : item));
273
+ }
@@ -87,16 +87,24 @@ export class LocalDate {
87
87
  }
88
88
  static earliest(items) {
89
89
  _assert(items.length, 'LocalDate.earliest called on empty array');
90
- return items.reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
90
+ return items
91
+ .map(i => LocalDate.of(i))
92
+ .reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
91
93
  }
92
94
  static latestOrUndefined(items) {
93
95
  return items.length ? LocalDate.latest(items) : undefined;
94
96
  }
95
97
  static latest(items) {
96
98
  _assert(items.length, 'LocalDate.latest called on empty array');
97
- return items.reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
99
+ return items
100
+ .map(i => LocalDate.of(i))
101
+ .reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
98
102
  }
99
103
  static range(min, max, incl = '[)', step = 1, stepUnit = 'day') {
104
+ if (stepUnit === 'week') {
105
+ step *= 7;
106
+ stepUnit = 'day';
107
+ }
100
108
  const dates = [];
101
109
  const $min = LocalDate.of(min);
102
110
  const $max = LocalDate.of(max).startOf(stepUnit);
@@ -194,44 +202,58 @@ export class LocalDate {
194
202
  return Math.abs(this.diff(d, unit));
195
203
  }
196
204
  /**
197
- * Returns the number of **full** units difference (aka `Math.ceil`).
205
+ * Returns the number of **full** units difference (aka `Math.floor`).
198
206
  *
199
207
  * a.diff(b) means "a minus b"
200
208
  */
201
209
  diff(d, unit) {
202
210
  d = LocalDate.of(d);
211
+ const sign = this.cmp(d);
212
+ if (!sign)
213
+ return 0;
214
+ // Put items in descending order: "big minus small"
215
+ const [big, small] = sign === 1 ? [this, d] : [d, this];
203
216
  if (unit === 'year') {
204
- return this.$year - d.$year;
217
+ let years = big.$year - small.$year;
218
+ if (big.$month < small.$month || (big.$month === small.$month && big.$day < small.$day)) {
219
+ years--;
220
+ }
221
+ return years * sign || 0;
205
222
  }
206
223
  if (unit === 'month') {
207
- return (this.$year - d.$year) * 12 + (this.$month - d.$month);
224
+ let months = (big.$year - small.$year) * 12 + (big.$month - small.$month);
225
+ if (big.$day < small.$day)
226
+ months--;
227
+ return months * sign || 0;
208
228
  }
209
- // unit is 'day'
210
- let days = this.$day - d.$day;
211
- if (d.$year < this.$year) {
212
- for (let year = d.$year; year < this.$year; year++) {
213
- days += LocalDate.getYearLength(year);
214
- }
229
+ // unit is 'day' or 'week'
230
+ let days = big.$day - small.$day;
231
+ // If small date is after 1st of March - next year's "leapness" should be used
232
+ const offsetYear = small.$month >= 3 ? 1 : 0;
233
+ for (let year = small.$year; year < big.$year; year++) {
234
+ days += LocalDate.getYearLength(year + offsetYear);
215
235
  }
216
- else if (this.$year < d.$year) {
217
- for (let year = this.$year; year < d.$year; year++) {
218
- days -= LocalDate.getYearLength(year);
236
+ if (small.$month < big.$month) {
237
+ for (let month = small.$month; month < big.$month; month++) {
238
+ days += LocalDate.getMonthLength(big.$year, month);
219
239
  }
220
240
  }
221
- if (d.$month < this.$month) {
222
- for (let month = d.$month; month < this.$month; month++) {
223
- days += LocalDate.getMonthLength(this.$year, month);
241
+ else if (big.$month < small.$month) {
242
+ for (let month = big.$month; month < small.$month; month++) {
243
+ days -= LocalDate.getMonthLength(big.$year, month);
224
244
  }
225
245
  }
226
- else if (this.$month < d.$month) {
227
- for (let month = this.$month; month < d.$month; month++) {
228
- days -= LocalDate.getMonthLength(d.$year, month);
229
- }
246
+ if (unit === 'week') {
247
+ return Math.trunc(days / 7) * sign || 0;
230
248
  }
231
- return days;
249
+ return days * sign || 0;
232
250
  }
233
251
  add(num, unit, mutate = false) {
234
252
  let { $day, $month, $year } = this;
253
+ if (unit === 'week') {
254
+ num *= 7;
255
+ unit = 'day';
256
+ }
235
257
  if (unit === 'day') {
236
258
  $day += num;
237
259
  }
@@ -331,6 +353,12 @@ export class LocalDate {
331
353
  toISODate() {
332
354
  return this.toString();
333
355
  }
356
+ /**
357
+ * Returns e.g: `1984-06-21T17:56:21`
358
+ */
359
+ toISODateTime() {
360
+ return this.toString() + 'T00:00:00';
361
+ }
334
362
  toString() {
335
363
  return [
336
364
  String(this.$year).padStart(4, '0'),
@@ -355,6 +383,9 @@ export class LocalDate {
355
383
  toJSON() {
356
384
  return this.toString();
357
385
  }
386
+ format(fmt) {
387
+ return fmt(this);
388
+ }
358
389
  }
359
390
  /**
360
391
  * Shortcut wrapper around `LocalDate.parse` / `LocalDate.today`
@@ -1,6 +1,22 @@
1
1
  import { _assert } from '../error/assert';
2
2
  import { _ms } from '../time/time.util';
3
3
  import { LocalDate } from './localDate';
4
+ export var ISODayOfWeek;
5
+ (function (ISODayOfWeek) {
6
+ ISODayOfWeek[ISODayOfWeek["MONDAY"] = 1] = "MONDAY";
7
+ ISODayOfWeek[ISODayOfWeek["TUESDAY"] = 2] = "TUESDAY";
8
+ ISODayOfWeek[ISODayOfWeek["WEDNESDAY"] = 3] = "WEDNESDAY";
9
+ ISODayOfWeek[ISODayOfWeek["THURSDAY"] = 4] = "THURSDAY";
10
+ ISODayOfWeek[ISODayOfWeek["FRIDAY"] = 5] = "FRIDAY";
11
+ ISODayOfWeek[ISODayOfWeek["SATURDAY"] = 6] = "SATURDAY";
12
+ ISODayOfWeek[ISODayOfWeek["SUNDAY"] = 7] = "SUNDAY";
13
+ })(ISODayOfWeek || (ISODayOfWeek = {}));
14
+ const weekStartsOn = 1; // mon, as per ISO
15
+ const MILLISECONDS_IN_WEEK = 604800000;
16
+ const SECONDS_IN_DAY = 86400;
17
+ // const MILLISECONDS_IN_DAY = 86400000
18
+ // const MILLISECONDS_IN_MINUTE = 60000
19
+ const VALID_DAYS_OF_WEEK = new Set([1, 2, 3, 4, 5, 6, 7]);
4
20
  /* eslint-disable no-dupe-class-members */
5
21
  /**
6
22
  * @experimental
@@ -104,6 +120,9 @@ export class LocalTime {
104
120
  if (unit === 'minute') {
105
121
  return this.utcMode ? this.$date.getUTCMinutes() : this.$date.getMinutes();
106
122
  }
123
+ if (unit === 'week') {
124
+ return getWeek(this.$date);
125
+ }
107
126
  // second
108
127
  return this.utcMode ? this.$date.getUTCSeconds() : this.$date.getSeconds();
109
128
  }
@@ -128,6 +147,9 @@ export class LocalTime {
128
147
  else if (unit === 'second') {
129
148
  this.utcMode ? t.$date.setUTCSeconds(v) : t.$date.setSeconds(v);
130
149
  }
150
+ else if (unit === 'week') {
151
+ setWeek(t.$date, v, true);
152
+ }
131
153
  /* eslint-enable @typescript-eslint/no-unused-expressions */
132
154
  return t;
133
155
  }
@@ -137,9 +159,21 @@ export class LocalTime {
137
159
  month(v) {
138
160
  return v === undefined ? this.get('month') : this.set('month', v);
139
161
  }
162
+ week(v) {
163
+ return v === undefined ? getWeek(this.$date) : this.set('week', v);
164
+ }
140
165
  day(v) {
141
166
  return v === undefined ? this.get('day') : this.set('day', v);
142
167
  }
168
+ dayOfWeek(v) {
169
+ const dow = (this.$date.getDay() || 7);
170
+ if (v === undefined) {
171
+ return dow;
172
+ }
173
+ if (!VALID_DAYS_OF_WEEK.has(v))
174
+ throw new Error(`Invalid dayOfWeek: ${v}`);
175
+ return this.add(v - dow, 'day');
176
+ }
143
177
  hour(v) {
144
178
  return v === undefined ? this.get('hour') : this.set('hour', v);
145
179
  }
@@ -174,6 +208,10 @@ export class LocalTime {
174
208
  return mutate ? this : new LocalTime(d, this.utcMode);
175
209
  }
176
210
  add(num, unit, mutate = false) {
211
+ if (unit === 'week') {
212
+ num *= 7;
213
+ unit = 'day';
214
+ }
177
215
  return this.set(unit, this.get(unit) + num, mutate);
178
216
  }
179
217
  subtract(num, unit, mutate = false) {
@@ -184,21 +222,43 @@ export class LocalTime {
184
222
  }
185
223
  diff(other, unit) {
186
224
  const date2 = LocalTime.parseToDate(other);
187
- if (unit === 'year') {
188
- return this.$date.getFullYear() - date2.getFullYear();
189
- }
190
- if (unit === 'month') {
191
- return ((this.$date.getFullYear() - date2.getFullYear()) * 12 +
192
- this.$date.getMonth() -
193
- date2.getMonth());
194
- }
195
225
  const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000;
226
+ if (!secDiff)
227
+ return 0;
228
+ if (unit === 'year' || unit === 'month') {
229
+ const sign = secDiff > 0 ? 1 : -1;
230
+ // Put items in descending order: "big minus small"
231
+ const [big, small] = sign === 1 ? [this.$date, date2] : [date2, this.$date];
232
+ if (unit === 'year') {
233
+ let years = big.getFullYear() - small.getFullYear();
234
+ const big2 = new Date(big);
235
+ const small2 = new Date(small);
236
+ big2.setFullYear(1584);
237
+ small2.setFullYear(1584);
238
+ if (big2 < small2)
239
+ years--;
240
+ return years * sign || 0;
241
+ }
242
+ if (unit === 'month') {
243
+ let months = (big.getFullYear() - small.getFullYear()) * 12 + big.getMonth() - small.getMonth();
244
+ const big2 = new Date(big);
245
+ const small2 = new Date(small);
246
+ big2.setFullYear(1584, 0);
247
+ small2.setFullYear(1584, 0);
248
+ if (big2 < small2)
249
+ months--;
250
+ return months * sign || 0;
251
+ }
252
+ }
196
253
  let r;
197
254
  if (unit === 'day') {
198
- r = secDiff / (24 * 60 * 60);
255
+ r = secDiff / SECONDS_IN_DAY;
256
+ }
257
+ else if (unit === 'week') {
258
+ r = secDiff / (7 * 24 * 60 * 60);
199
259
  }
200
260
  else if (unit === 'hour') {
201
- r = secDiff / (60 * 60);
261
+ r = secDiff / 3600;
202
262
  }
203
263
  else if (unit === 'minute') {
204
264
  r = secDiff / 60;
@@ -207,25 +267,60 @@ export class LocalTime {
207
267
  // unit === 'second'
208
268
  r = secDiff;
209
269
  }
210
- r = r < 0 ? -Math.floor(-r) : Math.floor(r);
211
- if (Object.is(r, -0))
212
- return 0;
213
- return r;
270
+ // `|| 0` is to avoid returning -0
271
+ return Math.trunc(r) || 0;
214
272
  }
215
273
  startOf(unit, mutate = false) {
216
274
  if (unit === 'second')
217
275
  return this;
218
276
  const d = mutate ? this.$date : new Date(this.$date);
277
+ d.setSeconds(0, 0);
219
278
  /* eslint-disable @typescript-eslint/no-unused-expressions */
220
- this.utcMode ? d.setUTCSeconds(0) : d.setSeconds(0);
221
279
  if (unit !== 'minute') {
222
280
  this.utcMode ? d.setUTCMinutes(0) : d.setMinutes(0);
223
281
  if (unit !== 'hour') {
224
282
  this.utcMode ? d.setUTCHours(0) : d.setHours(0);
225
283
  if (unit !== 'day') {
226
- this.utcMode ? d.setUTCDate(0) : d.setDate(0);
227
- if (unit !== 'month') {
284
+ // year, month or week
285
+ if (unit === 'year') {
228
286
  this.utcMode ? d.setUTCMonth(0) : d.setMonth(0);
287
+ this.utcMode ? d.setUTCDate(1) : d.setDate(1);
288
+ }
289
+ else if (unit === 'month') {
290
+ this.utcMode ? d.setUTCDate(1) : d.setDate(1);
291
+ }
292
+ else {
293
+ // week
294
+ startOfWeek(d, true);
295
+ }
296
+ }
297
+ }
298
+ }
299
+ /* eslint-enable @typescript-eslint/no-unused-expressions */
300
+ return mutate ? this : new LocalTime(d, this.utcMode);
301
+ }
302
+ endOf(unit, mutate = false) {
303
+ if (unit === 'second')
304
+ return this;
305
+ const d = mutate ? this.$date : new Date(this.$date);
306
+ d.setSeconds(59, 0);
307
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
308
+ if (unit !== 'minute') {
309
+ this.utcMode ? d.setUTCMinutes(59) : d.setMinutes(59);
310
+ if (unit !== 'hour') {
311
+ this.utcMode ? d.setUTCHours(23) : d.setHours(23);
312
+ if (unit !== 'day') {
313
+ // year, month or week
314
+ if (unit === 'year') {
315
+ this.utcMode ? d.setUTCMonth(11) : d.setMonth(11);
316
+ }
317
+ if (unit === 'week') {
318
+ endOfWeek(d, true);
319
+ }
320
+ else {
321
+ // year or month
322
+ const lastDay = LocalDate.getMonthLength(d.getFullYear(), d.getMonth() + 1);
323
+ this.utcMode ? d.setUTCDate(lastDay) : d.setDate(lastDay);
229
324
  }
230
325
  }
231
326
  }
@@ -248,14 +343,18 @@ export class LocalTime {
248
343
  }
249
344
  static earliest(items) {
250
345
  _assert(items.length, 'LocalTime.earliest called on empty array');
251
- return items.reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
346
+ return items
347
+ .map(i => LocalTime.of(i))
348
+ .reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
252
349
  }
253
350
  static latestOrUndefined(items) {
254
351
  return items.length ? LocalTime.latest(items) : undefined;
255
352
  }
256
353
  static latest(items) {
257
354
  _assert(items.length, 'LocalTime.latest called on empty array');
258
- return items.reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
355
+ return items
356
+ .map(i => LocalTime.of(i))
357
+ .reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
259
358
  }
260
359
  isSame(d) {
261
360
  return this.cmp(d) === 0;
@@ -295,7 +394,6 @@ export class LocalTime {
295
394
  return 0;
296
395
  return t1 < t2 ? -1 : 1;
297
396
  }
298
- // todo: endOf
299
397
  components() {
300
398
  if (this.utcMode) {
301
399
  return {
@@ -368,7 +466,7 @@ export class LocalTime {
368
466
  // .join(' ')
369
467
  }
370
468
  /**
371
- * Returns e.g: `1984-06-21T17:56:21`, only the date part of DateTime
469
+ * Returns e.g: `1984-06-21T17:56:21`
372
470
  */
373
471
  toISODateTime() {
374
472
  return this.$date.toISOString().slice(0, 19);
@@ -420,6 +518,9 @@ export class LocalTime {
420
518
  toJSON() {
421
519
  return this.unix();
422
520
  }
521
+ format(fmt) {
522
+ return fmt(this);
523
+ }
423
524
  }
424
525
  /**
425
526
  * Shortcut wrapper around `LocalDate.parse` / `LocalDate.today`
@@ -427,3 +528,83 @@ export class LocalTime {
427
528
  export function localTime(d) {
428
529
  return d ? LocalTime.of(d) : LocalTime.now();
429
530
  }
531
+ // based on: https://github.com/date-fns/date-fns/blob/master/src/getISOWeek/index.ts
532
+ function getWeek(date) {
533
+ const diff = startOfWeek(date).getTime() - startOfWeekYear(date).getTime();
534
+ return Math.round(diff / MILLISECONDS_IN_WEEK) + 1;
535
+ }
536
+ function setWeek(date, week, mutate = false) {
537
+ const d = mutate ? date : new Date(date);
538
+ const diff = getWeek(d) - week;
539
+ d.setDate(d.getDate() - diff * 7);
540
+ return d;
541
+ }
542
+ // based on: https://github.com/date-fns/date-fns/blob/master/src/startOfISOWeekYear/index.ts
543
+ function startOfWeekYear(date) {
544
+ const year = getWeekYear(date);
545
+ const fourthOfJanuary = new Date(0);
546
+ fourthOfJanuary.setFullYear(year, 0, 4);
547
+ fourthOfJanuary.setHours(0, 0, 0, 0);
548
+ return startOfWeek(fourthOfJanuary, true);
549
+ }
550
+ // based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/getISOWeekYear/index.ts
551
+ function getWeekYear(date) {
552
+ const year = date.getFullYear();
553
+ const fourthOfJanuaryOfNextYear = new Date(0);
554
+ fourthOfJanuaryOfNextYear.setFullYear(year + 1, 0, 4);
555
+ fourthOfJanuaryOfNextYear.setHours(0, 0, 0, 0);
556
+ const startOfNextYear = startOfWeek(fourthOfJanuaryOfNextYear, true);
557
+ const fourthOfJanuaryOfThisYear = new Date(0);
558
+ fourthOfJanuaryOfThisYear.setFullYear(year, 0, 4);
559
+ fourthOfJanuaryOfThisYear.setHours(0, 0, 0, 0);
560
+ const startOfThisYear = startOfWeek(fourthOfJanuaryOfThisYear, true);
561
+ if (date.getTime() >= startOfNextYear.getTime()) {
562
+ return year + 1;
563
+ }
564
+ else if (date.getTime() >= startOfThisYear.getTime()) {
565
+ return year;
566
+ }
567
+ else {
568
+ return year - 1;
569
+ }
570
+ }
571
+ // function setWeekYear(
572
+ // date: Date,
573
+ // year: number,
574
+ // ): Date {
575
+ // const diff = differenceInCalendarDays(date, startOfWeekYear(date))
576
+ // const fourthOfJanuary = new Date(0)
577
+ // fourthOfJanuary.setFullYear(year, 0, 4)
578
+ // fourthOfJanuary.setHours(0, 0, 0, 0)
579
+ // date = startOfWeekYear(fourthOfJanuary)
580
+ // date.setDate(date.getDate() + diff)
581
+ // return date
582
+ // }
583
+ // function differenceInCalendarDays(
584
+ // dateLeft: Date,
585
+ // dateRight: Date,
586
+ // ): number {
587
+ // return Math.round((startOfDay(dateLeft).getTime() - startOfDay(dateRight).getTime()) / MILLISECONDS_IN_DAY)
588
+ // }
589
+ // function startOfDay(date: Date, mutate = false): Date {
590
+ // const d = mutate ? date : new Date(date)
591
+ // d.setHours(0, 0, 0, 0)
592
+ // return d
593
+ // }
594
+ // based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/startOfWeek/index.ts
595
+ function startOfWeek(date, mutate = false) {
596
+ const d = mutate ? date : new Date(date);
597
+ const day = d.getDay();
598
+ const diff = (day < weekStartsOn ? 7 : 0) + day - weekStartsOn;
599
+ d.setDate(d.getDate() - diff);
600
+ d.setHours(0, 0, 0, 0);
601
+ return d;
602
+ }
603
+ // based on: https://github.com/date-fns/date-fns/blob/master/src/endOfWeek/index.ts
604
+ function endOfWeek(date, mutate = false) {
605
+ const d = mutate ? date : new Date(date);
606
+ const day = d.getDay();
607
+ const diff = (day < weekStartsOn ? -7 : 0) + 6 - (day - weekStartsOn);
608
+ d.setDate(d.getDate() + diff);
609
+ return d;
610
+ }
package/dist-esm/index.js CHANGED
@@ -60,4 +60,5 @@ export * from './datetime/localDate';
60
60
  export * from './datetime/localTime';
61
61
  export * from './datetime/dateInterval';
62
62
  export * from './datetime/timeInterval';
63
+ import { ISODayOfWeek, } from './datetime/localTime';
63
64
  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.98.2",
3
+ "version": "14.99.1",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -16,7 +16,7 @@
16
16
  "@naturalcycles/nodejs-lib": "^12.33.4",
17
17
  "@naturalcycles/time-lib": "^3.5.1",
18
18
  "@types/node": "^17.0.4",
19
- "jest": "^27.0.1",
19
+ "jest": "^28.0.3",
20
20
  "patch-package": "^6.2.1",
21
21
  "prettier": "^2.1.2",
22
22
  "rxjs": "^7.0.1",
@@ -272,9 +272,38 @@ export function _shuffle<T>(array: T[], mutate = false): T[] {
272
272
  return a
273
273
  }
274
274
 
275
+ /**
276
+ * Returns last item of non-empty array.
277
+ * Throws if array is empty.
278
+ */
279
+ export function _last<T>(array: T[]): T {
280
+ if (!array.length) throw new Error('_last called on empty array')
281
+ return array[array.length - 1]!
282
+ }
283
+
275
284
  /**
276
285
  * Returns last item of the array (or undefined if array is empty).
277
286
  */
278
- export function _last<T>(array: T[]): T | undefined {
287
+ export function _lastOrUndefined<T>(array: T[]): T | undefined {
279
288
  return array[array.length - 1]
280
289
  }
290
+
291
+ export function _minOrUndefined<T>(array: T[]): T | undefined {
292
+ if (!array.length) return
293
+ return _min(array)
294
+ }
295
+
296
+ export function _min<T>(array: T[]): T {
297
+ if (!array.length) throw new Error('_min called on empty array')
298
+ return array.reduce((min, item) => (min <= item ? min : item))
299
+ }
300
+
301
+ export function _maxOrUndefined<T>(array: T[]): T | undefined {
302
+ if (!array.length) return
303
+ return _max(array)
304
+ }
305
+
306
+ export function _max<T>(array: T[]): T {
307
+ if (!array.length) throw new Error('_max called on empty array')
308
+ return array.reduce((max, item) => (max >= item ? max : item))
309
+ }