@naturalcycles/js-lib 14.98.3 → 14.99.2

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.
@@ -2,13 +2,15 @@ import { _assert } from '../error/assert'
2
2
  import { IsoDateString, IsoDateTimeString, UnixTimestampNumber } from '../types'
3
3
  import { LocalTime } from './localTime'
4
4
 
5
- export type LocalDateUnit = 'year' | 'month' | 'day'
5
+ export type LocalDateUnit = LocalDateUnitStrict | 'week'
6
+ export type LocalDateUnitStrict = 'year' | 'month' | 'day'
6
7
  export type Inclusiveness = '()' | '[]' | '[)' | '(]'
7
8
 
8
9
  const MDAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
9
10
  const DATE_REGEX = /^(\d\d\d\d)-(\d\d)-(\d\d)$/
10
11
 
11
12
  export type LocalDateConfig = LocalDate | IsoDateString
13
+ export type LocalDateFormatter = (ld: LocalDate) => string
12
14
 
13
15
  /* eslint-disable no-dupe-class-members */
14
16
 
@@ -106,24 +108,28 @@ export class LocalDate {
106
108
  return (mutate ? items : [...items]).sort((a, b) => a.cmp(b) * mod)
107
109
  }
108
110
 
109
- static earliestOrUndefined(items: LocalDate[]): LocalDate | undefined {
111
+ static earliestOrUndefined(items: LocalDateConfig[]): LocalDate | undefined {
110
112
  return items.length ? LocalDate.earliest(items) : undefined
111
113
  }
112
114
 
113
- static earliest(items: LocalDate[]): LocalDate {
115
+ static earliest(items: LocalDateConfig[]): LocalDate {
114
116
  _assert(items.length, 'LocalDate.earliest called on empty array')
115
117
 
116
- return items.reduce((min, item) => (min.isSameOrBefore(item) ? min : item))
118
+ return items
119
+ .map(i => LocalDate.of(i))
120
+ .reduce((min, item) => (min.isSameOrBefore(item) ? min : item))
117
121
  }
118
122
 
119
- static latestOrUndefined(items: LocalDate[]): LocalDate | undefined {
123
+ static latestOrUndefined(items: LocalDateConfig[]): LocalDate | undefined {
120
124
  return items.length ? LocalDate.latest(items) : undefined
121
125
  }
122
126
 
123
- static latest(items: LocalDate[]): LocalDate {
127
+ static latest(items: LocalDateConfig[]): LocalDate {
124
128
  _assert(items.length, 'LocalDate.latest called on empty array')
125
129
 
126
- return items.reduce((max, item) => (max.isSameOrAfter(item) ? max : item))
130
+ return items
131
+ .map(i => LocalDate.of(i))
132
+ .reduce((max, item) => (max.isSameOrAfter(item) ? max : item))
127
133
  }
128
134
 
129
135
  static range(
@@ -133,6 +139,11 @@ export class LocalDate {
133
139
  step = 1,
134
140
  stepUnit: LocalDateUnit = 'day',
135
141
  ): LocalDate[] {
142
+ if (stepUnit === 'week') {
143
+ step *= 7
144
+ stepUnit = 'day'
145
+ }
146
+
136
147
  const dates: LocalDate[] = []
137
148
  const $min = LocalDate.of(min)
138
149
  const $max = LocalDate.of(max).startOf(stepUnit)
@@ -153,11 +164,11 @@ export class LocalDate {
153
164
  return dates
154
165
  }
155
166
 
156
- get(unit: LocalDateUnit): number {
167
+ get(unit: LocalDateUnitStrict): number {
157
168
  return unit === 'year' ? this.$year : unit === 'month' ? this.$month : this.$day
158
169
  }
159
170
 
160
- set(unit: LocalDateUnit, v: number, mutate = false): LocalDate {
171
+ set(unit: LocalDateUnitStrict, v: number, mutate = false): LocalDate {
161
172
  const t = mutate ? this : this.clone()
162
173
 
163
174
  if (unit === 'year') {
@@ -242,50 +253,69 @@ export class LocalDate {
242
253
  }
243
254
 
244
255
  /**
245
- * Returns the number of **full** units difference (aka `Math.ceil`).
256
+ * Returns the number of **full** units difference (aka `Math.floor`).
246
257
  *
247
258
  * a.diff(b) means "a minus b"
248
259
  */
249
260
  diff(d: LocalDateConfig, unit: LocalDateUnit): number {
250
261
  d = LocalDate.of(d)
251
262
 
263
+ const sign = this.cmp(d)
264
+ if (!sign) return 0
265
+
266
+ // Put items in descending order: "big minus small"
267
+ const [big, small] = sign === 1 ? [this, d] : [d, this]
268
+
252
269
  if (unit === 'year') {
253
- return this.$year - d.$year
270
+ let years = big.$year - small.$year
271
+
272
+ if (big.$month < small.$month || (big.$month === small.$month && big.$day < small.$day)) {
273
+ years--
274
+ }
275
+
276
+ return years * sign || 0
254
277
  }
255
278
 
256
279
  if (unit === 'month') {
257
- return (this.$year - d.$year) * 12 + (this.$month - d.$month)
280
+ let months = (big.$year - small.$year) * 12 + (big.$month - small.$month)
281
+ if (big.$day < small.$day) months--
282
+ return months * sign || 0
258
283
  }
259
284
 
260
- // unit is 'day'
261
- let days = this.$day - d.$day
285
+ // unit is 'day' or 'week'
286
+ let days = big.$day - small.$day
262
287
 
263
- if (d.$year < this.$year) {
264
- for (let year = d.$year; year < this.$year; year++) {
265
- days += LocalDate.getYearLength(year)
266
- }
267
- } else if (this.$year < d.$year) {
268
- for (let year = this.$year; year < d.$year; year++) {
269
- days -= LocalDate.getYearLength(year)
270
- }
288
+ // If small date is after 1st of March - next year's "leapness" should be used
289
+ const offsetYear = small.$month >= 3 ? 1 : 0
290
+ for (let year = small.$year; year < big.$year; year++) {
291
+ days += LocalDate.getYearLength(year + offsetYear)
271
292
  }
272
293
 
273
- if (d.$month < this.$month) {
274
- for (let month = d.$month; month < this.$month; month++) {
275
- days += LocalDate.getMonthLength(this.$year, month)
294
+ if (small.$month < big.$month) {
295
+ for (let month = small.$month; month < big.$month; month++) {
296
+ days += LocalDate.getMonthLength(big.$year, month)
276
297
  }
277
- } else if (this.$month < d.$month) {
278
- for (let month = this.$month; month < d.$month; month++) {
279
- days -= LocalDate.getMonthLength(d.$year, month)
298
+ } else if (big.$month < small.$month) {
299
+ for (let month = big.$month; month < small.$month; month++) {
300
+ days -= LocalDate.getMonthLength(big.$year, month)
280
301
  }
281
302
  }
282
303
 
283
- return days
304
+ if (unit === 'week') {
305
+ return Math.trunc(days / 7) * sign || 0
306
+ }
307
+
308
+ return days * sign || 0
284
309
  }
285
310
 
286
311
  add(num: number, unit: LocalDateUnit, mutate = false): LocalDate {
287
312
  let { $day, $month, $year } = this
288
313
 
314
+ if (unit === 'week') {
315
+ num *= 7
316
+ unit = 'day'
317
+ }
318
+
289
319
  if (unit === 'day') {
290
320
  $day += num
291
321
  } else if (unit === 'month') {
@@ -346,14 +376,14 @@ export class LocalDate {
346
376
  return this.add(-num, unit, mutate)
347
377
  }
348
378
 
349
- startOf(unit: LocalDateUnit): LocalDate {
379
+ startOf(unit: LocalDateUnitStrict): LocalDate {
350
380
  if (unit === 'day') return this
351
381
  if (unit === 'month') return LocalDate.create(this.$year, this.$month, 1)
352
382
  // year
353
383
  return LocalDate.create(this.$year, 1, 1)
354
384
  }
355
385
 
356
- endOf(unit: LocalDateUnit): LocalDate {
386
+ endOf(unit: LocalDateUnitStrict): LocalDate {
357
387
  if (unit === 'day') return this
358
388
  if (unit === 'month')
359
389
  return LocalDate.create(
@@ -435,6 +465,10 @@ export class LocalDate {
435
465
  toJSON(): IsoDateString {
436
466
  return this.toString()
437
467
  }
468
+
469
+ format(fmt: LocalDateFormatter): string {
470
+ return fmt(this)
471
+ }
438
472
  }
439
473
 
440
474
  /**