@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.
@@ -1,14 +1,16 @@
1
1
  import { _assert } from '../error/assert'
2
- import { IsoDateString, UnixTimestampNumber } from '../types'
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(
@@ -400,6 +430,13 @@ export class LocalDate {
400
430
  return this.toString()
401
431
  }
402
432
 
433
+ /**
434
+ * Returns e.g: `1984-06-21T17:56:21`
435
+ */
436
+ toISODateTime(): IsoDateTimeString {
437
+ return this.toString() + 'T00:00:00'
438
+ }
439
+
403
440
  toString(): IsoDateString {
404
441
  return [
405
442
  String(this.$year).padStart(4, '0'),
@@ -428,6 +465,10 @@ export class LocalDate {
428
465
  toJSON(): IsoDateString {
429
466
  return this.toString()
430
467
  }
468
+
469
+ format(fmt: LocalDateFormatter): string {
470
+ return fmt(this)
471
+ }
431
472
  }
432
473
 
433
474
  /**
@@ -3,9 +3,20 @@ import { _ms } from '../time/time.util'
3
3
  import { IsoDateString, IsoDateTimeString, UnixTimestampNumber } from '../types'
4
4
  import { Inclusiveness, LocalDate } from './localDate'
5
5
 
6
- export type LocalTimeUnit = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'
6
+ export type LocalTimeUnit = 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second'
7
+
8
+ export enum ISODayOfWeek {
9
+ MONDAY = 1,
10
+ TUESDAY = 2,
11
+ WEDNESDAY = 3,
12
+ THURSDAY = 4,
13
+ FRIDAY = 5,
14
+ SATURDAY = 6,
15
+ SUNDAY = 7,
16
+ }
7
17
 
8
18
  export type LocalTimeConfig = LocalTime | Date | IsoDateTimeString | UnixTimestampNumber
19
+ export type LocalTimeFormatter = (ld: LocalTime) => string
9
20
 
10
21
  export interface LocalTimeComponents {
11
22
  year: number
@@ -16,6 +27,13 @@ export interface LocalTimeComponents {
16
27
  second: number
17
28
  }
18
29
 
30
+ const weekStartsOn = 1 // mon, as per ISO
31
+ const MILLISECONDS_IN_WEEK = 604800000
32
+ const SECONDS_IN_DAY = 86400
33
+ // const MILLISECONDS_IN_DAY = 86400000
34
+ // const MILLISECONDS_IN_MINUTE = 60000
35
+ const VALID_DAYS_OF_WEEK = new Set([1, 2, 3, 4, 5, 6, 7])
36
+
19
37
  /* eslint-disable no-dupe-class-members */
20
38
 
21
39
  /**
@@ -134,6 +152,9 @@ export class LocalTime {
134
152
  if (unit === 'minute') {
135
153
  return this.utcMode ? this.$date.getUTCMinutes() : this.$date.getMinutes()
136
154
  }
155
+ if (unit === 'week') {
156
+ return getWeek(this.$date)
157
+ }
137
158
  // second
138
159
  return this.utcMode ? this.$date.getUTCSeconds() : this.$date.getSeconds()
139
160
  }
@@ -154,6 +175,8 @@ export class LocalTime {
154
175
  this.utcMode ? t.$date.setUTCMinutes(v) : t.$date.setMinutes(v)
155
176
  } else if (unit === 'second') {
156
177
  this.utcMode ? t.$date.setUTCSeconds(v) : t.$date.setSeconds(v)
178
+ } else if (unit === 'week') {
179
+ setWeek(t.$date, v, true)
157
180
  }
158
181
  /* eslint-enable @typescript-eslint/no-unused-expressions */
159
182
 
@@ -170,11 +193,33 @@ export class LocalTime {
170
193
  month(v?: number): number | LocalTime {
171
194
  return v === undefined ? this.get('month') : this.set('month', v)
172
195
  }
196
+ week(): number
197
+ week(v: number): LocalTime
198
+ week(v?: number): number | LocalTime {
199
+ return v === undefined ? getWeek(this.$date) : this.set('week', v)
200
+ }
173
201
  day(): number
174
202
  day(v: number): LocalTime
175
203
  day(v?: number): number | LocalTime {
176
204
  return v === undefined ? this.get('day') : this.set('day', v)
177
205
  }
206
+
207
+ /**
208
+ * Based on ISO: 1-7 is Mon-Sun.
209
+ */
210
+ dayOfWeek(): ISODayOfWeek
211
+ dayOfWeek(v: ISODayOfWeek): LocalTime
212
+ dayOfWeek(v?: ISODayOfWeek): ISODayOfWeek | LocalTime {
213
+ const dow = (this.$date.getDay() || 7) as ISODayOfWeek
214
+
215
+ if (v === undefined) {
216
+ return dow
217
+ }
218
+
219
+ if (!VALID_DAYS_OF_WEEK.has(v)) throw new Error(`Invalid dayOfWeek: ${v}`)
220
+
221
+ return this.add(v - dow, 'day')
222
+ }
178
223
  hour(): number
179
224
  hour(v: number): LocalTime
180
225
  hour(v?: number): number | LocalTime {
@@ -219,6 +264,10 @@ export class LocalTime {
219
264
  }
220
265
 
221
266
  add(num: number, unit: LocalTimeUnit, mutate = false): LocalTime {
267
+ if (unit === 'week') {
268
+ num *= 7
269
+ unit = 'day'
270
+ }
222
271
  return this.set(unit, this.get(unit) + num, mutate)
223
272
  }
224
273
 
@@ -233,24 +282,45 @@ export class LocalTime {
233
282
  diff(other: LocalTimeConfig, unit: LocalTimeUnit): number {
234
283
  const date2 = LocalTime.parseToDate(other)
235
284
 
236
- if (unit === 'year') {
237
- return this.$date.getFullYear() - date2.getFullYear()
238
- }
239
- if (unit === 'month') {
240
- return (
241
- (this.$date.getFullYear() - date2.getFullYear()) * 12 +
242
- this.$date.getMonth() -
243
- date2.getMonth()
244
- )
285
+ const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000
286
+ if (!secDiff) return 0
287
+
288
+ if (unit === 'year' || unit === 'month') {
289
+ const sign = secDiff > 0 ? 1 : -1
290
+
291
+ // Put items in descending order: "big minus small"
292
+ const [big, small] = sign === 1 ? [this.$date, date2] : [date2, this.$date]
293
+
294
+ if (unit === 'year') {
295
+ let years = big.getFullYear() - small.getFullYear()
296
+ const big2 = new Date(big)
297
+ const small2 = new Date(small)
298
+ big2.setFullYear(1584)
299
+ small2.setFullYear(1584)
300
+ if (big2 < small2) years--
301
+ return years * sign || 0
302
+ }
303
+
304
+ if (unit === 'month') {
305
+ let months =
306
+ (big.getFullYear() - small.getFullYear()) * 12 + big.getMonth() - small.getMonth()
307
+ const big2 = new Date(big)
308
+ const small2 = new Date(small)
309
+ big2.setFullYear(1584, 0)
310
+ small2.setFullYear(1584, 0)
311
+ if (big2 < small2) months--
312
+ return months * sign || 0
313
+ }
245
314
  }
246
315
 
247
- const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000
248
316
  let r
249
317
 
250
318
  if (unit === 'day') {
251
- r = secDiff / (24 * 60 * 60)
319
+ r = secDiff / SECONDS_IN_DAY
320
+ } else if (unit === 'week') {
321
+ r = secDiff / (7 * 24 * 60 * 60)
252
322
  } else if (unit === 'hour') {
253
- r = secDiff / (60 * 60)
323
+ r = secDiff / 3600
254
324
  } else if (unit === 'minute') {
255
325
  r = secDiff / 60
256
326
  } else {
@@ -258,26 +328,63 @@ export class LocalTime {
258
328
  r = secDiff
259
329
  }
260
330
 
261
- r = r < 0 ? -Math.floor(-r) : Math.floor(r)
262
- if (Object.is(r, -0)) return 0
263
- return r
331
+ // `|| 0` is to avoid returning -0
332
+ return Math.trunc(r) || 0
264
333
  }
265
334
 
266
335
  startOf(unit: LocalTimeUnit, mutate = false): LocalTime {
267
336
  if (unit === 'second') return this
268
-
269
337
  const d = mutate ? this.$date : new Date(this.$date)
338
+ d.setSeconds(0, 0)
270
339
 
271
340
  /* eslint-disable @typescript-eslint/no-unused-expressions */
272
- this.utcMode ? d.setUTCSeconds(0) : d.setSeconds(0)
273
341
  if (unit !== 'minute') {
274
342
  this.utcMode ? d.setUTCMinutes(0) : d.setMinutes(0)
275
343
  if (unit !== 'hour') {
276
344
  this.utcMode ? d.setUTCHours(0) : d.setHours(0)
277
345
  if (unit !== 'day') {
278
- this.utcMode ? d.setUTCDate(0) : d.setDate(0)
279
- if (unit !== 'month') {
346
+ // year, month or week
347
+
348
+ if (unit === 'year') {
280
349
  this.utcMode ? d.setUTCMonth(0) : d.setMonth(0)
350
+ this.utcMode ? d.setUTCDate(1) : d.setDate(1)
351
+ } else if (unit === 'month') {
352
+ this.utcMode ? d.setUTCDate(1) : d.setDate(1)
353
+ } else {
354
+ // week
355
+ startOfWeek(d, true)
356
+ }
357
+ }
358
+ }
359
+ }
360
+ /* eslint-enable @typescript-eslint/no-unused-expressions */
361
+
362
+ return mutate ? this : new LocalTime(d, this.utcMode)
363
+ }
364
+
365
+ endOf(unit: LocalTimeUnit, mutate = false): LocalTime {
366
+ if (unit === 'second') return this
367
+ const d = mutate ? this.$date : new Date(this.$date)
368
+ d.setSeconds(59, 0)
369
+
370
+ /* eslint-disable @typescript-eslint/no-unused-expressions */
371
+ if (unit !== 'minute') {
372
+ this.utcMode ? d.setUTCMinutes(59) : d.setMinutes(59)
373
+ if (unit !== 'hour') {
374
+ this.utcMode ? d.setUTCHours(23) : d.setHours(23)
375
+ if (unit !== 'day') {
376
+ // year, month or week
377
+
378
+ if (unit === 'year') {
379
+ this.utcMode ? d.setUTCMonth(11) : d.setMonth(11)
380
+ }
381
+
382
+ if (unit === 'week') {
383
+ endOfWeek(d, true)
384
+ } else {
385
+ // year or month
386
+ const lastDay = LocalDate.getMonthLength(d.getFullYear(), d.getMonth() + 1)
387
+ this.utcMode ? d.setUTCDate(lastDay) : d.setDate(lastDay)
281
388
  }
282
389
  }
283
390
  }
@@ -297,24 +404,28 @@ export class LocalTime {
297
404
  })
298
405
  }
299
406
 
300
- static earliestOrUndefined(items: LocalTime[]): LocalTime | undefined {
407
+ static earliestOrUndefined(items: LocalTimeConfig[]): LocalTime | undefined {
301
408
  return items.length ? LocalTime.earliest(items) : undefined
302
409
  }
303
410
 
304
- static earliest(items: LocalTime[]): LocalTime {
411
+ static earliest(items: LocalTimeConfig[]): LocalTime {
305
412
  _assert(items.length, 'LocalTime.earliest called on empty array')
306
413
 
307
- return items.reduce((min, item) => (min.isSameOrBefore(item) ? min : item))
414
+ return items
415
+ .map(i => LocalTime.of(i))
416
+ .reduce((min, item) => (min.isSameOrBefore(item) ? min : item))
308
417
  }
309
418
 
310
- static latestOrUndefined(items: LocalTime[]): LocalTime | undefined {
419
+ static latestOrUndefined(items: LocalTimeConfig[]): LocalTime | undefined {
311
420
  return items.length ? LocalTime.latest(items) : undefined
312
421
  }
313
422
 
314
- static latest(items: LocalTime[]): LocalTime {
423
+ static latest(items: LocalTimeConfig[]): LocalTime {
315
424
  _assert(items.length, 'LocalTime.latest called on empty array')
316
425
 
317
- return items.reduce((max, item) => (max.isSameOrAfter(item) ? max : item))
426
+ return items
427
+ .map(i => LocalTime.of(i))
428
+ .reduce((max, item) => (max.isSameOrAfter(item) ? max : item))
318
429
  }
319
430
 
320
431
  isSame(d: LocalTimeConfig): boolean {
@@ -359,8 +470,6 @@ export class LocalTime {
359
470
  return t1 < t2 ? -1 : 1
360
471
  }
361
472
 
362
- // todo: endOf
363
-
364
473
  components(): LocalTimeComponents {
365
474
  if (this.utcMode) {
366
475
  return {
@@ -458,7 +567,7 @@ export class LocalTime {
458
567
  }
459
568
 
460
569
  /**
461
- * Returns e.g: `1984-06-21T17:56:21`, only the date part of DateTime
570
+ * Returns e.g: `1984-06-21T17:56:21`
462
571
  */
463
572
  toISODateTime(): IsoDateTimeString {
464
573
  return this.$date.toISOString().slice(0, 19)
@@ -519,6 +628,10 @@ export class LocalTime {
519
628
  toJSON(): UnixTimestampNumber {
520
629
  return this.unix()
521
630
  }
631
+
632
+ format(fmt: LocalTimeFormatter): string {
633
+ return fmt(this)
634
+ }
522
635
  }
523
636
 
524
637
  /**
@@ -527,3 +640,97 @@ export class LocalTime {
527
640
  export function localTime(d?: LocalTimeConfig): LocalTime {
528
641
  return d ? LocalTime.of(d) : LocalTime.now()
529
642
  }
643
+
644
+ // based on: https://github.com/date-fns/date-fns/blob/master/src/getISOWeek/index.ts
645
+ function getWeek(date: Date): number {
646
+ const diff = startOfWeek(date).getTime() - startOfWeekYear(date).getTime()
647
+ return Math.round(diff / MILLISECONDS_IN_WEEK) + 1
648
+ }
649
+
650
+ function setWeek(date: Date, week: number, mutate = false): Date {
651
+ const d = mutate ? date : new Date(date)
652
+ const diff = getWeek(d) - week
653
+ d.setDate(d.getDate() - diff * 7)
654
+ return d
655
+ }
656
+
657
+ // based on: https://github.com/date-fns/date-fns/blob/master/src/startOfISOWeekYear/index.ts
658
+ function startOfWeekYear(date: Date): Date {
659
+ const year = getWeekYear(date)
660
+ const fourthOfJanuary = new Date(0)
661
+ fourthOfJanuary.setFullYear(year, 0, 4)
662
+ fourthOfJanuary.setHours(0, 0, 0, 0)
663
+ return startOfWeek(fourthOfJanuary, true)
664
+ }
665
+
666
+ // based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/getISOWeekYear/index.ts
667
+ function getWeekYear(date: Date): number {
668
+ const year = date.getFullYear()
669
+
670
+ const fourthOfJanuaryOfNextYear = new Date(0)
671
+ fourthOfJanuaryOfNextYear.setFullYear(year + 1, 0, 4)
672
+ fourthOfJanuaryOfNextYear.setHours(0, 0, 0, 0)
673
+ const startOfNextYear = startOfWeek(fourthOfJanuaryOfNextYear, true)
674
+
675
+ const fourthOfJanuaryOfThisYear = new Date(0)
676
+ fourthOfJanuaryOfThisYear.setFullYear(year, 0, 4)
677
+ fourthOfJanuaryOfThisYear.setHours(0, 0, 0, 0)
678
+ const startOfThisYear = startOfWeek(fourthOfJanuaryOfThisYear, true)
679
+
680
+ if (date.getTime() >= startOfNextYear.getTime()) {
681
+ return year + 1
682
+ } else if (date.getTime() >= startOfThisYear.getTime()) {
683
+ return year
684
+ } else {
685
+ return year - 1
686
+ }
687
+ }
688
+
689
+ // function setWeekYear(
690
+ // date: Date,
691
+ // year: number,
692
+ // ): Date {
693
+ // const diff = differenceInCalendarDays(date, startOfWeekYear(date))
694
+ // const fourthOfJanuary = new Date(0)
695
+ // fourthOfJanuary.setFullYear(year, 0, 4)
696
+ // fourthOfJanuary.setHours(0, 0, 0, 0)
697
+ // date = startOfWeekYear(fourthOfJanuary)
698
+ // date.setDate(date.getDate() + diff)
699
+ // return date
700
+ // }
701
+
702
+ // function differenceInCalendarDays(
703
+ // dateLeft: Date,
704
+ // dateRight: Date,
705
+ // ): number {
706
+ // return Math.round((startOfDay(dateLeft).getTime() - startOfDay(dateRight).getTime()) / MILLISECONDS_IN_DAY)
707
+ // }
708
+
709
+ // function startOfDay(date: Date, mutate = false): Date {
710
+ // const d = mutate ? date : new Date(date)
711
+ // d.setHours(0, 0, 0, 0)
712
+ // return d
713
+ // }
714
+
715
+ // based on: https://github.com/date-fns/date-fns/blob/fd6bb1a0bab143f2da068c05a9c562b9bee1357d/src/startOfWeek/index.ts
716
+ function startOfWeek(date: Date, mutate = false): Date {
717
+ const d = mutate ? date : new Date(date)
718
+
719
+ const day = d.getDay()
720
+ const diff = (day < weekStartsOn ? 7 : 0) + day - weekStartsOn
721
+
722
+ d.setDate(d.getDate() - diff)
723
+ d.setHours(0, 0, 0, 0)
724
+ return d
725
+ }
726
+
727
+ // based on: https://github.com/date-fns/date-fns/blob/master/src/endOfWeek/index.ts
728
+ function endOfWeek(date: Date, mutate = false): Date {
729
+ const d = mutate ? date : new Date(date)
730
+
731
+ const day = d.getDay()
732
+ const diff = (day < weekStartsOn ? -7 : 0) + 6 - (day - weekStartsOn)
733
+
734
+ d.setDate(d.getDate() + diff)
735
+ return d
736
+ }
package/src/index.ts CHANGED
@@ -162,8 +162,20 @@ export * from './datetime/localDate'
162
162
  export * from './datetime/localTime'
163
163
  export * from './datetime/dateInterval'
164
164
  export * from './datetime/timeInterval'
165
- import { LocalDateConfig, LocalDateUnit, Inclusiveness } from './datetime/localDate'
166
- import { LocalTimeConfig, LocalTimeUnit, LocalTimeComponents } from './datetime/localTime'
165
+ import {
166
+ LocalDateConfig,
167
+ LocalDateFormatter,
168
+ LocalDateUnit,
169
+ LocalDateUnitStrict,
170
+ Inclusiveness,
171
+ } from './datetime/localDate'
172
+ import {
173
+ LocalTimeConfig,
174
+ LocalTimeFormatter,
175
+ LocalTimeUnit,
176
+ LocalTimeComponents,
177
+ ISODayOfWeek,
178
+ } from './datetime/localTime'
167
179
  import { DateIntervalConfig, DateIntervalString } from './datetime/dateInterval'
168
180
  import { TimeIntervalConfig, TimeIntervalString } from './datetime/timeInterval'
169
181
 
@@ -173,10 +185,14 @@ export type {
173
185
  TimeIntervalConfig,
174
186
  TimeIntervalString,
175
187
  LocalDateConfig,
188
+ LocalDateFormatter,
176
189
  LocalDateUnit,
190
+ LocalDateUnitStrict,
177
191
  Inclusiveness,
178
192
  LocalTimeConfig,
193
+ LocalTimeFormatter,
179
194
  LocalTimeUnit,
195
+ ISODayOfWeek,
180
196
  LocalTimeComponents,
181
197
  AbortableMapper,
182
198
  AbortablePredicate,
@@ -169,7 +169,7 @@ export async function pRetry<T>(
169
169
 
170
170
  const r = await fn(attempt)
171
171
 
172
- clearTimeout(timer!)
172
+ clearTimeout(timer)
173
173
 
174
174
  if (logSuccess) {
175
175
  logger.log(`${fname} attempt #${attempt} succeeded in ${_since(started)}`)
@@ -177,7 +177,7 @@ export async function pRetry<T>(
177
177
 
178
178
  resolve(r)
179
179
  } catch (err) {
180
- clearTimeout(timer!)
180
+ clearTimeout(timer)
181
181
 
182
182
  if (logFailures) {
183
183
  logger.warn(