@naturalcycles/js-lib 14.88.0 → 14.91.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/localDate.d.ts +80 -0
- package/dist/datetime/localDate.js +310 -0
- package/dist/datetime/localTime.d.ts +87 -0
- package/dist/datetime/localTime.js +339 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +47 -45
- package/dist-esm/datetime/localDate.js +305 -0
- package/dist-esm/datetime/localTime.js +334 -0
- package/dist-esm/index.js +2 -0
- package/dist-esm/vendor/is.js +4 -4
- package/package.json +2 -1
- package/src/__exclude/lazyLocalDate.ts +73 -0
- package/src/datetime/localDate.ts +385 -0
- package/src/datetime/localTime.ts +401 -0
- package/src/index.ts +9 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { _assert } from '../error/assert'
|
|
2
|
+
import { IsoDateTime, UnixTimestamp } from '../types'
|
|
3
|
+
|
|
4
|
+
export type LocalTimeUnit = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second'
|
|
5
|
+
|
|
6
|
+
export type LocalTimeConfig = LocalTime | Date | IsoDateTime | UnixTimestamp
|
|
7
|
+
|
|
8
|
+
export interface LocalTimeComponents {
|
|
9
|
+
year: number
|
|
10
|
+
month: number
|
|
11
|
+
day: number
|
|
12
|
+
hour: number
|
|
13
|
+
minute: number
|
|
14
|
+
second: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* eslint-disable no-dupe-class-members */
|
|
18
|
+
|
|
19
|
+
// Design choices:
|
|
20
|
+
// No milliseconds
|
|
21
|
+
// No timezone support, ISO8601 is parsed as LocalDateTime, discarding Timezone information
|
|
22
|
+
// Formats as unix timestamp, ISO8601 or "pretty string"
|
|
23
|
+
// toString and .toJSON formats as unix timestamp
|
|
24
|
+
// No "unixMillis", just pure unixtimestamp
|
|
25
|
+
// .valueOf returns unix timestamp (no millis)
|
|
26
|
+
// Prevents dayjs(undefined) being dayjs.now()
|
|
27
|
+
// Validates on parse, throws if invalid. Doesn't allow invalid objects
|
|
28
|
+
/**
|
|
29
|
+
* @experimental
|
|
30
|
+
*/
|
|
31
|
+
export class LocalTime {
|
|
32
|
+
private constructor(private $date: Date) {}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parses input String into LocalDate.
|
|
36
|
+
* Input can already be a LocalDate - it is returned as-is in that case.
|
|
37
|
+
*/
|
|
38
|
+
static of(d: LocalTimeConfig): LocalTime {
|
|
39
|
+
const t = this.parseOrNull(d)
|
|
40
|
+
|
|
41
|
+
if (t === null) {
|
|
42
|
+
throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return t
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns null if invalid
|
|
50
|
+
*/
|
|
51
|
+
static parseOrNull(d: LocalTimeConfig): LocalTime | null {
|
|
52
|
+
if (d instanceof LocalTime) return d
|
|
53
|
+
|
|
54
|
+
let date
|
|
55
|
+
|
|
56
|
+
if (d instanceof Date) {
|
|
57
|
+
date = d
|
|
58
|
+
} else if (typeof d === 'number') {
|
|
59
|
+
date = new Date(d * 1000)
|
|
60
|
+
} else {
|
|
61
|
+
date = new Date(d)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// validation
|
|
65
|
+
if (isNaN(date.getDate())) {
|
|
66
|
+
// throw new TypeError(`Cannot parse "${d}" into LocalTime`)
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return new LocalTime(date)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static isValid(d: LocalTimeConfig): boolean {
|
|
74
|
+
return this.parseOrNull(d) !== null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static unix(ts: UnixTimestamp): LocalTime {
|
|
78
|
+
return new LocalTime(new Date(ts * 1000))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static now(): LocalTime {
|
|
82
|
+
return this.of(new Date())
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static fromComponents(
|
|
86
|
+
c: { year: number; month: number } & Partial<LocalTimeComponents>,
|
|
87
|
+
): LocalTime {
|
|
88
|
+
return new LocalTime(new Date(c.year, c.month - 1, c.day, c.hour, c.minute, c.second))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
get(unit: LocalTimeUnit): number {
|
|
92
|
+
if (unit === 'year') {
|
|
93
|
+
return this.$date.getFullYear()
|
|
94
|
+
}
|
|
95
|
+
if (unit === 'month') {
|
|
96
|
+
return this.$date.getMonth() + 1
|
|
97
|
+
}
|
|
98
|
+
if (unit === 'day') {
|
|
99
|
+
return this.$date.getDate()
|
|
100
|
+
}
|
|
101
|
+
if (unit === 'hour') {
|
|
102
|
+
return this.$date.getHours()
|
|
103
|
+
}
|
|
104
|
+
if (unit === 'minute') {
|
|
105
|
+
return this.$date.getMinutes()
|
|
106
|
+
}
|
|
107
|
+
// second
|
|
108
|
+
return this.$date.getSeconds()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
set(unit: LocalTimeUnit, v: number, mutate = false): LocalTime {
|
|
112
|
+
const t = mutate ? this : this.clone()
|
|
113
|
+
|
|
114
|
+
if (unit === 'year') {
|
|
115
|
+
t.$date.setFullYear(v)
|
|
116
|
+
} else if (unit === 'month') {
|
|
117
|
+
t.$date.setMonth(v - 1)
|
|
118
|
+
} else if (unit === 'day') {
|
|
119
|
+
t.$date.setDate(v)
|
|
120
|
+
} else if (unit === 'hour') {
|
|
121
|
+
t.$date.setHours(v)
|
|
122
|
+
} else if (unit === 'minute') {
|
|
123
|
+
t.$date.setMinutes(v)
|
|
124
|
+
} else if (unit === 'second') {
|
|
125
|
+
t.$date.setSeconds(v)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return t
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
year(): number
|
|
132
|
+
year(v: number): LocalTime
|
|
133
|
+
year(v?: number): number | LocalTime {
|
|
134
|
+
return v === undefined ? this.$date.getFullYear() : this.set('year', v)
|
|
135
|
+
}
|
|
136
|
+
month(): number
|
|
137
|
+
month(v: number): LocalTime
|
|
138
|
+
month(v?: number): number | LocalTime {
|
|
139
|
+
return v === undefined ? this.$date.getMonth() + 1 : this.set('month', v)
|
|
140
|
+
}
|
|
141
|
+
date(): number
|
|
142
|
+
date(v: number): LocalTime
|
|
143
|
+
date(v?: number): number | LocalTime {
|
|
144
|
+
return v === undefined ? this.$date.getDate() : this.set('day', v)
|
|
145
|
+
}
|
|
146
|
+
hour(): number
|
|
147
|
+
hour(v: number): LocalTime
|
|
148
|
+
hour(v?: number): number | LocalTime {
|
|
149
|
+
return v === undefined ? this.$date.getHours() : this.set('hour', v)
|
|
150
|
+
}
|
|
151
|
+
minute(): number
|
|
152
|
+
minute(v: number): LocalTime
|
|
153
|
+
minute(v?: number): number | LocalTime {
|
|
154
|
+
return v === undefined ? this.$date.getMinutes() : this.set('minute', v)
|
|
155
|
+
}
|
|
156
|
+
second(): number
|
|
157
|
+
second(v: number): LocalTime
|
|
158
|
+
second(v?: number): number | LocalTime {
|
|
159
|
+
return v === undefined ? this.$date.getSeconds() : this.set('second', v)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
setComponents(c: Partial<LocalTimeComponents>, mutate = false): LocalTime {
|
|
163
|
+
const d = mutate ? this.$date : new Date(this.$date)
|
|
164
|
+
|
|
165
|
+
if (c.year) {
|
|
166
|
+
d.setFullYear(c.year)
|
|
167
|
+
}
|
|
168
|
+
if (c.month) {
|
|
169
|
+
d.setMonth(c.month - 1)
|
|
170
|
+
}
|
|
171
|
+
if (c.day) {
|
|
172
|
+
d.setDate(c.day)
|
|
173
|
+
}
|
|
174
|
+
if (c.hour !== undefined) {
|
|
175
|
+
d.setHours(c.hour)
|
|
176
|
+
}
|
|
177
|
+
if (c.minute !== undefined) {
|
|
178
|
+
d.setMinutes(c.minute)
|
|
179
|
+
}
|
|
180
|
+
if (c.second !== undefined) {
|
|
181
|
+
d.setSeconds(c.second)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return mutate ? this : new LocalTime(d)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
add(num: number, unit: LocalTimeUnit, mutate = false): LocalTime {
|
|
188
|
+
return this.set(unit, this.get(unit) + num, mutate)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
subtract(num: number, unit: LocalTimeUnit, mutate = false): LocalTime {
|
|
192
|
+
return this.add(-num, unit, mutate)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
absDiff(other: LocalTimeConfig, unit: LocalTimeUnit): number {
|
|
196
|
+
return Math.abs(this.diff(other, unit))
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
diff(other: LocalTimeConfig, unit: LocalTimeUnit): number {
|
|
200
|
+
const date2 = LocalTime.of(other).$date
|
|
201
|
+
|
|
202
|
+
if (unit === 'year') {
|
|
203
|
+
return this.$date.getFullYear() - date2.getFullYear()
|
|
204
|
+
}
|
|
205
|
+
if (unit === 'month') {
|
|
206
|
+
return (
|
|
207
|
+
(this.$date.getFullYear() - date2.getFullYear()) * 12 +
|
|
208
|
+
this.$date.getMonth() -
|
|
209
|
+
date2.getMonth()
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const secDiff = (this.$date.valueOf() - date2.valueOf()) / 1000
|
|
214
|
+
let r
|
|
215
|
+
|
|
216
|
+
if (unit === 'day') {
|
|
217
|
+
r = secDiff / (24 * 60 * 60)
|
|
218
|
+
} else if (unit === 'hour') {
|
|
219
|
+
r = secDiff / (60 * 60)
|
|
220
|
+
} else if (unit === 'minute') {
|
|
221
|
+
r = secDiff / 60
|
|
222
|
+
} else {
|
|
223
|
+
// unit === 'second'
|
|
224
|
+
r = secDiff
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
r = r < 0 ? -Math.floor(-r) : Math.floor(r)
|
|
228
|
+
if (Object.is(r, -0)) return 0
|
|
229
|
+
return r
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
startOf(unit: LocalTimeUnit, mutate = false): LocalTime {
|
|
233
|
+
if (unit === 'second') return this
|
|
234
|
+
|
|
235
|
+
if (mutate) {
|
|
236
|
+
const d = this.$date
|
|
237
|
+
d.setSeconds(0)
|
|
238
|
+
if (unit === 'minute') return this
|
|
239
|
+
d.setMinutes(0)
|
|
240
|
+
if (unit === 'hour') return this
|
|
241
|
+
d.setHours(0)
|
|
242
|
+
if (unit === 'day') return this
|
|
243
|
+
d.setDate(0)
|
|
244
|
+
if (unit === 'month') return this
|
|
245
|
+
d.setMonth(0)
|
|
246
|
+
return this
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const c = this.components()
|
|
250
|
+
|
|
251
|
+
c.second = 0
|
|
252
|
+
if (unit === 'year') {
|
|
253
|
+
c.month = c.day = 1
|
|
254
|
+
c.hour = c.minute = 0
|
|
255
|
+
} else if (unit === 'month') {
|
|
256
|
+
c.day = 1
|
|
257
|
+
c.hour = c.minute = 0
|
|
258
|
+
} else if (unit === 'day') {
|
|
259
|
+
c.hour = c.minute = 0
|
|
260
|
+
} else if (unit === 'hour') {
|
|
261
|
+
c.minute = 0
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return LocalTime.fromComponents(c)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
static sort(items: LocalTime[], mutate = false, descending = false): LocalTime[] {
|
|
268
|
+
const mod = descending ? -1 : 1
|
|
269
|
+
return (mutate ? items : [...items]).sort((a, b) => {
|
|
270
|
+
const v1 = a.$date.valueOf()
|
|
271
|
+
const v2 = b.$date.valueOf()
|
|
272
|
+
if (v1 === v2) return 0
|
|
273
|
+
return (v1 < v2 ? -1 : 1) * mod
|
|
274
|
+
})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
static earliestOrUndefined(items: LocalTime[]): LocalTime | undefined {
|
|
278
|
+
return items.length ? LocalTime.earliest(items) : undefined
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
static earliest(items: LocalTime[]): LocalTime {
|
|
282
|
+
_assert(items.length, 'LocalTime.earliest called on empty array')
|
|
283
|
+
|
|
284
|
+
return items.reduce((min, item) => (min.isSameOrBefore(item) ? min : item))
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
static latestOrUndefined(items: LocalTime[]): LocalTime | undefined {
|
|
288
|
+
return items.length ? LocalTime.latest(items) : undefined
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
static latest(items: LocalTime[]): LocalTime {
|
|
292
|
+
_assert(items.length, 'LocalTime.latest called on empty array')
|
|
293
|
+
|
|
294
|
+
return items.reduce((max, item) => (max.isSameOrAfter(item) ? max : item))
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
isSame(d: LocalTimeConfig): boolean {
|
|
298
|
+
return this.cmp(d) === 0
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
isBefore(d: LocalTimeConfig): boolean {
|
|
302
|
+
return this.cmp(d) === -1
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
isSameOrBefore(d: LocalTimeConfig): boolean {
|
|
306
|
+
return this.cmp(d) <= 0
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
isAfter(d: LocalTimeConfig): boolean {
|
|
310
|
+
return this.cmp(d) === 1
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
isSameOrAfter(d: LocalTimeConfig): boolean {
|
|
314
|
+
return this.cmp(d) >= 0
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Returns 1 if this > d
|
|
319
|
+
* returns 0 if they are equal
|
|
320
|
+
* returns -1 if this < d
|
|
321
|
+
*/
|
|
322
|
+
cmp(d: LocalTimeConfig): -1 | 0 | 1 {
|
|
323
|
+
const t1 = this.$date.valueOf()
|
|
324
|
+
const t2 = LocalTime.of(d).$date.valueOf()
|
|
325
|
+
if (t1 === t2) return 0
|
|
326
|
+
return t1 < t2 ? -1 : 1
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// todo: endOf
|
|
330
|
+
|
|
331
|
+
components(): LocalTimeComponents {
|
|
332
|
+
return {
|
|
333
|
+
year: this.$date.getFullYear(),
|
|
334
|
+
month: this.$date.getMonth() + 1,
|
|
335
|
+
day: this.$date.getDate(),
|
|
336
|
+
hour: this.$date.getHours(),
|
|
337
|
+
minute: this.$date.getMinutes(),
|
|
338
|
+
second: this.$date.getSeconds(),
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
getDate(): Date {
|
|
343
|
+
return this.$date
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
clone(): LocalTime {
|
|
347
|
+
return new LocalTime(new Date(this.$date))
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
unix(): UnixTimestamp {
|
|
351
|
+
return Math.floor(this.$date.valueOf() / 1000)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
valueOf(): UnixTimestamp {
|
|
355
|
+
return Math.floor(this.$date.valueOf() / 1000)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
toISO8601(): IsoDateTime {
|
|
359
|
+
return this.$date.toISOString().slice(0, 19)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
toPretty(seconds = true): IsoDateTime {
|
|
363
|
+
return this.$date
|
|
364
|
+
.toISOString()
|
|
365
|
+
.slice(0, seconds ? 19 : 16)
|
|
366
|
+
.split('T')
|
|
367
|
+
.join(' ')
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Returns e.g: `19840621_1705`
|
|
372
|
+
*/
|
|
373
|
+
toStringCompact(seconds = false): string {
|
|
374
|
+
return [
|
|
375
|
+
String(this.$date.getFullYear()).padStart(4, '0'),
|
|
376
|
+
String(this.$date.getMonth() + 1).padStart(2, '0'),
|
|
377
|
+
String(this.$date.getDate()).padStart(2, '0'),
|
|
378
|
+
'_',
|
|
379
|
+
String(this.$date.getHours()).padStart(2, '0'),
|
|
380
|
+
String(this.$date.getMinutes()).padStart(2, '0'),
|
|
381
|
+
seconds ? String(this.$date.getSeconds()).padStart(2, '0') : '',
|
|
382
|
+
].join('')
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
toString(): string {
|
|
386
|
+
return String(this.unix())
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
toJSON(): UnixTimestamp {
|
|
390
|
+
return this.unix()
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Shortcut wrapper around `LocalDate.parse` / `LocalDate.today`
|
|
396
|
+
*/
|
|
397
|
+
export function localTime(d?: LocalTimeConfig): LocalTime {
|
|
398
|
+
return d ? LocalTime.of(d) : LocalTime.now()
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// todo: range
|
package/src/index.ts
CHANGED
|
@@ -156,8 +156,17 @@ import { PQueue, PQueueCfg } from './promise/pQueue'
|
|
|
156
156
|
export * from './seq/seq'
|
|
157
157
|
export * from './math/stack.util'
|
|
158
158
|
export * from './string/leven'
|
|
159
|
+
export * from './datetime/localDate'
|
|
160
|
+
export * from './datetime/localTime'
|
|
161
|
+
import { LocalDateConfig, LocalDateUnit } from './datetime/localDate'
|
|
162
|
+
import { LocalTimeConfig, LocalTimeUnit, LocalTimeComponents } from './datetime/localTime'
|
|
159
163
|
|
|
160
164
|
export type {
|
|
165
|
+
LocalDateConfig,
|
|
166
|
+
LocalDateUnit,
|
|
167
|
+
LocalTimeConfig,
|
|
168
|
+
LocalTimeUnit,
|
|
169
|
+
LocalTimeComponents,
|
|
161
170
|
AbortableMapper,
|
|
162
171
|
AbortablePredicate,
|
|
163
172
|
AbortableAsyncPredicate,
|