iamcal 3.0.3 → 4.0.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/README.md +2 -2
- package/lib/component.d.ts +2 -1
- package/lib/component.d.ts.map +1 -1
- package/lib/component.js +14 -16
- package/lib/components/CalendarEvent.d.ts +34 -8
- package/lib/components/CalendarEvent.d.ts.map +1 -1
- package/lib/components/CalendarEvent.js +67 -8
- package/lib/date.d.ts +41 -3
- package/lib/date.d.ts.map +1 -1
- package/lib/date.js +75 -7
- package/lib/duration.d.ts +106 -0
- package/lib/duration.d.ts.map +1 -0
- package/lib/duration.js +277 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/io.d.ts +20 -0
- package/lib/io.d.ts.map +1 -1
- package/lib/io.js +30 -1
- package/lib/parse.d.ts +11 -3
- package/lib/parse.d.ts.map +1 -1
- package/lib/parse.js +42 -22
- package/lib/property/Property.d.ts +24 -2
- package/lib/property/Property.d.ts.map +1 -1
- package/lib/property/Property.js +71 -4
- package/lib/property/validate.js +2 -2
- package/package.json +1 -1
- package/src/component.ts +15 -23
- package/src/components/CalendarEvent.ts +73 -9
- package/src/date.ts +96 -13
- package/src/duration.ts +329 -0
- package/src/index.ts +1 -0
- package/src/io.ts +42 -1
- package/src/parse.ts +43 -19
- package/src/property/Property.ts +80 -5
- package/src/property/validate.ts +1 -1
package/src/date.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import * as patterns from './patterns'
|
|
2
2
|
import { Property } from './property/Property'
|
|
3
|
+
import { CalendarDuration } from './duration'
|
|
3
4
|
|
|
4
5
|
export const ONE_SECOND_MS = 1000
|
|
5
6
|
export const ONE_MINUTE_MS = 60 * ONE_SECOND_MS
|
|
6
7
|
export const ONE_HOUR_MS = 60 * ONE_MINUTE_MS
|
|
7
8
|
export const ONE_DAY_MS = 24 * ONE_HOUR_MS
|
|
9
|
+
export const ONE_WEEK_MS = 7 * ONE_DAY_MS
|
|
10
|
+
|
|
11
|
+
export const ONE_MINUTE_SECONDS = 60
|
|
12
|
+
export const ONE_HOUR_SECONDS = 60 * ONE_MINUTE_SECONDS
|
|
13
|
+
export const ONE_DAY_SECONDS = 24 * ONE_HOUR_SECONDS
|
|
14
|
+
export const ONE_WEEK_SECONDS = 7 * ONE_DAY_SECONDS
|
|
8
15
|
|
|
9
16
|
export interface CalendarDateOrTime {
|
|
10
17
|
/**
|
|
@@ -24,7 +31,14 @@ export interface CalendarDateOrTime {
|
|
|
24
31
|
* Check if this date represents a full day, as opposed to a date-time.
|
|
25
32
|
* @returns `true` if this object is a {@link CalendarDate}.
|
|
26
33
|
*/
|
|
27
|
-
isFullDay():
|
|
34
|
+
isFullDay(): this is CalendarDate
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get a new time offset by a duration.
|
|
38
|
+
* @param duration The duration to offset the time by.
|
|
39
|
+
* @returns A new {@link CalendarDateOrTime} of the same type.
|
|
40
|
+
*/
|
|
41
|
+
offset(duration: CalendarDuration): CalendarDateOrTime
|
|
28
42
|
}
|
|
29
43
|
|
|
30
44
|
/**
|
|
@@ -36,10 +50,10 @@ export class CalendarDate implements CalendarDateOrTime {
|
|
|
36
50
|
|
|
37
51
|
constructor(date: Date | string | CalendarDateOrTime) {
|
|
38
52
|
if (typeof date === 'object') {
|
|
39
|
-
if (
|
|
40
|
-
this.date = date
|
|
53
|
+
if (isDateObject(date)) {
|
|
54
|
+
this.date = date
|
|
41
55
|
} else {
|
|
42
|
-
this.date = (date
|
|
56
|
+
this.date = (date).getDate()
|
|
43
57
|
}
|
|
44
58
|
} else {
|
|
45
59
|
try {
|
|
@@ -64,9 +78,30 @@ export class CalendarDate implements CalendarDateOrTime {
|
|
|
64
78
|
return new Date(this.date)
|
|
65
79
|
}
|
|
66
80
|
|
|
67
|
-
isFullDay():
|
|
81
|
+
isFullDay(): this is CalendarDate {
|
|
68
82
|
return true
|
|
69
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get a new date offset by a duration.
|
|
87
|
+
* @param duration The duration to offset the date by.
|
|
88
|
+
* @returns A new {@link CalendarDate} offset by the duration.
|
|
89
|
+
*/
|
|
90
|
+
offset(duration: CalendarDuration): CalendarDate {
|
|
91
|
+
const offsetMs = duration.floor('D').inMilliseconds()
|
|
92
|
+
const ms = this.getDate().getTime()
|
|
93
|
+
const time = new Date(ms + offsetMs)
|
|
94
|
+
return new CalendarDate(time)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
[Symbol.toPrimitive](hint: string): number | string | null {
|
|
98
|
+
if (hint === 'string') {
|
|
99
|
+
return this.getValue()
|
|
100
|
+
} else if (hint === 'number') {
|
|
101
|
+
return this.date.getTime()
|
|
102
|
+
}
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
70
105
|
}
|
|
71
106
|
|
|
72
107
|
export class CalendarDateTime implements CalendarDateOrTime {
|
|
@@ -74,10 +109,10 @@ export class CalendarDateTime implements CalendarDateOrTime {
|
|
|
74
109
|
|
|
75
110
|
constructor(date: Date | string | CalendarDateOrTime) {
|
|
76
111
|
if (typeof date === 'object') {
|
|
77
|
-
if (
|
|
78
|
-
this.date = date
|
|
112
|
+
if (isDateObject(date)) {
|
|
113
|
+
this.date = date
|
|
79
114
|
} else {
|
|
80
|
-
this.date = (date
|
|
115
|
+
this.date = (date).getDate()
|
|
81
116
|
}
|
|
82
117
|
} else {
|
|
83
118
|
try {
|
|
@@ -100,9 +135,57 @@ export class CalendarDateTime implements CalendarDateOrTime {
|
|
|
100
135
|
return new Date(this.date)
|
|
101
136
|
}
|
|
102
137
|
|
|
103
|
-
isFullDay():
|
|
138
|
+
isFullDay(): this is CalendarDate {
|
|
104
139
|
return false
|
|
105
140
|
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get a new time offset by a duration.
|
|
144
|
+
* @param duration The duration to offset the time by.
|
|
145
|
+
* @returns A new {@link CalendarDateTime} offset by the duration.
|
|
146
|
+
*/
|
|
147
|
+
offset(duration: CalendarDuration): CalendarDateTime {
|
|
148
|
+
const offsetMs = duration.inMilliseconds()
|
|
149
|
+
const ms = this.getDate().getTime()
|
|
150
|
+
const time = new Date(ms + offsetMs)
|
|
151
|
+
return new CalendarDateTime(time)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
[Symbol.toPrimitive](hint: string): number | string | null {
|
|
155
|
+
if (hint === 'string') {
|
|
156
|
+
return this.getValue()
|
|
157
|
+
} else if (hint === 'number') {
|
|
158
|
+
return this.date.getTime()
|
|
159
|
+
}
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Check if an object is a JavaScript `Date`.
|
|
166
|
+
* @param maybeDate The object to check.
|
|
167
|
+
* @returns `true` if the object is a `Date`, `false` otherwise.
|
|
168
|
+
*/
|
|
169
|
+
export function isDateObject(maybeDate: unknown): maybeDate is Date {
|
|
170
|
+
return Object.prototype.toString.call(maybeDate) === '[object Date]'
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check if an object is a `CalendarDateOrTime`.
|
|
175
|
+
* @param maybeDate The object to check.
|
|
176
|
+
* @returns `true` if the object is a `CalendarDateOrTime`, `false` otherwise.
|
|
177
|
+
*/
|
|
178
|
+
export function isCalendarDateOrTime(
|
|
179
|
+
maybeDate: unknown
|
|
180
|
+
): maybeDate is CalendarDateOrTime {
|
|
181
|
+
return (
|
|
182
|
+
maybeDate != null &&
|
|
183
|
+
typeof maybeDate === "object" &&
|
|
184
|
+
typeof (maybeDate as CalendarDateOrTime).getValue === "function" &&
|
|
185
|
+
typeof (maybeDate as CalendarDateOrTime).getDate === "function" &&
|
|
186
|
+
typeof (maybeDate as CalendarDateOrTime).isFullDay === "function" &&
|
|
187
|
+
typeof (maybeDate as CalendarDateOrTime).offset === "function"
|
|
188
|
+
)
|
|
106
189
|
}
|
|
107
190
|
|
|
108
191
|
/**
|
|
@@ -370,10 +453,10 @@ export function convertDate(
|
|
|
370
453
|
date: Date | CalendarDateOrTime,
|
|
371
454
|
fullDay: boolean = false
|
|
372
455
|
): CalendarDateOrTime {
|
|
373
|
-
if (
|
|
374
|
-
if (fullDay) return new CalendarDate(date
|
|
375
|
-
else return new CalendarDateTime(date
|
|
456
|
+
if (isDateObject(date)) {
|
|
457
|
+
if (fullDay) return new CalendarDate(date)
|
|
458
|
+
else return new CalendarDateTime(date)
|
|
376
459
|
} else {
|
|
377
|
-
return date
|
|
460
|
+
return date
|
|
378
461
|
}
|
|
379
462
|
}
|
package/src/duration.ts
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CalendarDateOrTime,
|
|
3
|
+
isDateObject,
|
|
4
|
+
isCalendarDateOrTime,
|
|
5
|
+
ONE_DAY_MS,
|
|
6
|
+
ONE_DAY_SECONDS,
|
|
7
|
+
ONE_HOUR_SECONDS,
|
|
8
|
+
ONE_MINUTE_SECONDS,
|
|
9
|
+
ONE_SECOND_MS,
|
|
10
|
+
ONE_WEEK_SECONDS,
|
|
11
|
+
} from './date'
|
|
12
|
+
import { validateDuration } from './property'
|
|
13
|
+
|
|
14
|
+
export type DurationUnit = 'W' | 'D' | 'H' | 'M' | 'S'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Represents a DURATION value as defined by RFC5545.
|
|
18
|
+
*/
|
|
19
|
+
export class CalendarDuration {
|
|
20
|
+
weeks?: number
|
|
21
|
+
days?: number
|
|
22
|
+
hours?: number
|
|
23
|
+
minutes?: number
|
|
24
|
+
seconds?: number
|
|
25
|
+
|
|
26
|
+
constructor(duration: string | CalendarDuration) {
|
|
27
|
+
if (typeof duration === 'string') {
|
|
28
|
+
validateDuration(duration)
|
|
29
|
+
|
|
30
|
+
const negativeMultiplier = duration.startsWith('-') ? -1 : 1
|
|
31
|
+
const findParts = /(\d+)([WDHMS])/g
|
|
32
|
+
const parts = duration.matchAll(findParts)
|
|
33
|
+
for (const part of parts) {
|
|
34
|
+
const value = parseInt(part[1], 10)
|
|
35
|
+
const name = part[2] as DurationUnit
|
|
36
|
+
switch (name) {
|
|
37
|
+
case 'W':
|
|
38
|
+
this.weeks = value * negativeMultiplier
|
|
39
|
+
break
|
|
40
|
+
case 'D':
|
|
41
|
+
this.days = value * negativeMultiplier
|
|
42
|
+
break
|
|
43
|
+
case 'H':
|
|
44
|
+
this.hours = value * negativeMultiplier
|
|
45
|
+
break
|
|
46
|
+
case 'M':
|
|
47
|
+
this.minutes = value * negativeMultiplier
|
|
48
|
+
break
|
|
49
|
+
case 'S':
|
|
50
|
+
this.seconds = value * negativeMultiplier
|
|
51
|
+
break
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
this.weeks = duration.weeks
|
|
56
|
+
this.days = duration.days
|
|
57
|
+
this.hours = duration.hours
|
|
58
|
+
this.minutes = duration.minutes
|
|
59
|
+
this.seconds = duration.seconds
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get the length of this duration object in seconds.
|
|
65
|
+
* @returns The total number of seconds represented by this duration.
|
|
66
|
+
*/
|
|
67
|
+
inSeconds(): number {
|
|
68
|
+
return (
|
|
69
|
+
(this.weeks ?? 0) * ONE_WEEK_SECONDS +
|
|
70
|
+
(this.days ?? 0) * ONE_DAY_SECONDS +
|
|
71
|
+
(this.hours ?? 0) * ONE_HOUR_SECONDS +
|
|
72
|
+
(this.minutes ?? 0) * ONE_MINUTE_SECONDS +
|
|
73
|
+
(this.seconds ?? 0)
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get the length of this duration object in milliseconds.
|
|
79
|
+
*
|
|
80
|
+
* Note that this will always return whole seconds, as durations do not
|
|
81
|
+
* support milliseconds.
|
|
82
|
+
* @returns The total number of milliseconds represented by this duration.
|
|
83
|
+
*/
|
|
84
|
+
inMilliseconds(): number {
|
|
85
|
+
return this.inSeconds() * ONE_SECOND_MS
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Get the duration string that represents this duration.
|
|
90
|
+
* @returns A duration string in the format `P[n]W` for weeks, or `P[n]DT[n]H[n]M[n]S` for days, hours, minutes and seconds. Prefixed with a `-` if negative.
|
|
91
|
+
*/
|
|
92
|
+
getValue(): string {
|
|
93
|
+
return formatDurationString(
|
|
94
|
+
this.weeks,
|
|
95
|
+
this.days,
|
|
96
|
+
this.hours,
|
|
97
|
+
this.minutes,
|
|
98
|
+
this.seconds
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get the floor the duration to the nearest of a given unit.
|
|
104
|
+
* @param unit The unit to floor to.
|
|
105
|
+
* @returns The duration with units smaller than `unit` removed.
|
|
106
|
+
* @example
|
|
107
|
+
* const duration = new CalendarDuration("1W2D5H30M")
|
|
108
|
+
* const days = duration.floor("D") // Floor to days
|
|
109
|
+
* console.log(days.getValue()) // "1W2D"
|
|
110
|
+
*/
|
|
111
|
+
floor(unit: DurationUnit): CalendarDuration {
|
|
112
|
+
const result = new CalendarDuration(this)
|
|
113
|
+
/* eslint-disable no-fallthrough */
|
|
114
|
+
switch (unit) {
|
|
115
|
+
case 'W':
|
|
116
|
+
result.days = undefined
|
|
117
|
+
case 'D':
|
|
118
|
+
result.hours = undefined
|
|
119
|
+
case 'H':
|
|
120
|
+
result.minutes = undefined
|
|
121
|
+
case 'M':
|
|
122
|
+
result.seconds = undefined
|
|
123
|
+
}
|
|
124
|
+
/* eslint-enable no-fallthrough */
|
|
125
|
+
return result
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create a duration from a number of seconds.
|
|
130
|
+
* @param seconds How many seconds the duration should represent.
|
|
131
|
+
* @returns A {@link CalendarDuration} representing the specified number of seconds.
|
|
132
|
+
* @throws {Error} If seconds is NaN.
|
|
133
|
+
*/
|
|
134
|
+
static fromSeconds(seconds: number): CalendarDuration {
|
|
135
|
+
return new CalendarDuration(secondsToDurationString(seconds))
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Create a duration from a number of days.
|
|
140
|
+
* @param days How many days the duration should represent.
|
|
141
|
+
* @returns A {@link CalendarDuration} representing the specified number of days.
|
|
142
|
+
* @throws {Error} If days is NaN.
|
|
143
|
+
*/
|
|
144
|
+
static fromDays(days: number): CalendarDuration {
|
|
145
|
+
return new CalendarDuration(daysToDurationString(days))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Create a duration from a number of weeks.
|
|
150
|
+
* @param weeks How many weeks the duration should represent.
|
|
151
|
+
* @returns A {@link CalendarDuration} representing the specified number of weeks.
|
|
152
|
+
* @throws {Error} If weeks is NaN.
|
|
153
|
+
*/
|
|
154
|
+
static fromWeeks(weeks: number): CalendarDuration {
|
|
155
|
+
return new CalendarDuration(weeksToDurationString(weeks))
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Creates a duration from the difference between two dates.
|
|
160
|
+
* @param start The start date or time.
|
|
161
|
+
* @param end The end date or time.
|
|
162
|
+
* @returns A {@link CalendarDuration} representing the difference between the two dates.
|
|
163
|
+
* @throws {Error} If the start date and end date have different types.
|
|
164
|
+
*/
|
|
165
|
+
static fromDifference(
|
|
166
|
+
start: Date | CalendarDateOrTime,
|
|
167
|
+
end: Date | CalendarDateOrTime
|
|
168
|
+
): CalendarDuration {
|
|
169
|
+
if (isDateObject(start)) {
|
|
170
|
+
// Date
|
|
171
|
+
if (isDateObject(end)) {
|
|
172
|
+
const differenceSeconds =
|
|
173
|
+
(end.getTime() - start.getTime()) / ONE_SECOND_MS
|
|
174
|
+
return CalendarDuration.fromSeconds(differenceSeconds)
|
|
175
|
+
}
|
|
176
|
+
} else if (start.isFullDay()) {
|
|
177
|
+
// CalendarDate
|
|
178
|
+
if (isCalendarDateOrTime(end) && end.isFullDay()) {
|
|
179
|
+
const startMs = start.getDate().getTime()
|
|
180
|
+
const endMs = end.getDate().getTime()
|
|
181
|
+
const differenceDays = (endMs - startMs) / ONE_DAY_MS
|
|
182
|
+
return CalendarDuration.fromDays(differenceDays)
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
// CalendarDateTime
|
|
186
|
+
if (isCalendarDateOrTime(end) && !end.isFullDay()) {
|
|
187
|
+
const startMs = start.getDate().getTime()
|
|
188
|
+
const endMs = end.getDate().getTime()
|
|
189
|
+
const differenceSeconds = (endMs - startMs) / ONE_SECOND_MS
|
|
190
|
+
return CalendarDuration.fromSeconds(differenceSeconds)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
throw new Error('Start and end dates must be of the same type')
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Convert time units to a duration string.
|
|
200
|
+
*
|
|
201
|
+
* A duration is considered negative if any unit is less than 0.
|
|
202
|
+
* @param weeks How many weeks the duration should represent.
|
|
203
|
+
* @param days The days part of the duration.
|
|
204
|
+
* @param hours The hours part of the duration.
|
|
205
|
+
* @param minutes The minutes part of the duration.
|
|
206
|
+
* @param seconds The seconds part of the duration.
|
|
207
|
+
* @returns A string representing the duration in the format `P[n]W` or `P[n]DT[n]H[n]M[n]S` where values may be omitted if 0, prefixed with `-` if negative.
|
|
208
|
+
* @throws {Error} If weeks are combined with other values.
|
|
209
|
+
* @throws {Error} If any unit is NaN.
|
|
210
|
+
*/
|
|
211
|
+
export function formatDurationString(
|
|
212
|
+
weeks: number | undefined,
|
|
213
|
+
days: number | undefined,
|
|
214
|
+
hours: number | undefined,
|
|
215
|
+
minutes: number | undefined,
|
|
216
|
+
seconds: number | undefined,
|
|
217
|
+
): string {
|
|
218
|
+
let prefix = ''
|
|
219
|
+
let durationString = prefix + 'P'
|
|
220
|
+
let hasTime = false
|
|
221
|
+
let isEmpty = true
|
|
222
|
+
|
|
223
|
+
const appendUnit = (time: number | undefined, unit: string) => {
|
|
224
|
+
if (time === undefined) return
|
|
225
|
+
if (isNaN(time)) {
|
|
226
|
+
throw new Error(`${unit} must not be NaN`)
|
|
227
|
+
}
|
|
228
|
+
if (time < 0) prefix = '-'
|
|
229
|
+
const absTime = Math.abs(Math.floor(time))
|
|
230
|
+
durationString += absTime + unit.charAt(0)
|
|
231
|
+
isEmpty = false
|
|
232
|
+
}
|
|
233
|
+
const appendTimeUnit = (time: number | undefined, unit: string) => {
|
|
234
|
+
if (time === undefined) return
|
|
235
|
+
if (!hasTime) {
|
|
236
|
+
durationString += 'T'
|
|
237
|
+
hasTime = true
|
|
238
|
+
}
|
|
239
|
+
appendUnit(time, unit)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Add units
|
|
243
|
+
if (weeks !== undefined) {
|
|
244
|
+
if (days !== undefined ||
|
|
245
|
+
hours !== undefined ||
|
|
246
|
+
minutes !== undefined ||
|
|
247
|
+
seconds !== undefined) {
|
|
248
|
+
throw new Error('Cannot combine weeks with other units in duration string')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
appendUnit(weeks, 'Weeks')
|
|
252
|
+
}
|
|
253
|
+
appendUnit(days, 'Days')
|
|
254
|
+
appendTimeUnit(hours, 'Hours')
|
|
255
|
+
if (minutes !== undefined) {
|
|
256
|
+
appendTimeUnit(minutes, 'Minutes')
|
|
257
|
+
} else if (hours !== undefined && seconds !== undefined) {
|
|
258
|
+
appendTimeUnit(0, 'Minutes')
|
|
259
|
+
}
|
|
260
|
+
appendTimeUnit(seconds, 'Seconds')
|
|
261
|
+
|
|
262
|
+
if (isEmpty) {
|
|
263
|
+
throw new Error('Duration string must not be empty')
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return prefix + durationString
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Convert a number of seconds to a duration string.
|
|
271
|
+
* @param seconds How many seconds the duration should represent.
|
|
272
|
+
* @returns A string representing the duration in the format `PT[n]H[n]M[n]S` where values may be omitted if 0, prefixed with `-` if negative.
|
|
273
|
+
* @throws {Error} If seconds is NaN.
|
|
274
|
+
*/
|
|
275
|
+
export function secondsToDurationString(seconds: number): string {
|
|
276
|
+
if (isNaN(seconds)) throw new Error('Seconds must not be NaN')
|
|
277
|
+
seconds = Math.floor(seconds)
|
|
278
|
+
|
|
279
|
+
const absSeconds = Math.abs(seconds)
|
|
280
|
+
const minutes = Math.floor(absSeconds / 60)
|
|
281
|
+
const hours = Math.floor(minutes / 60)
|
|
282
|
+
let secondsValue: number | undefined = seconds % 60
|
|
283
|
+
let minutesValue: number | undefined = minutes % 60
|
|
284
|
+
let hoursValue: number | undefined = hours
|
|
285
|
+
|
|
286
|
+
if (hoursValue === 0) hoursValue = undefined
|
|
287
|
+
if (minutesValue === 0) minutesValue = undefined
|
|
288
|
+
if (secondsValue === 0 && !(minutesValue === undefined && hoursValue === undefined)) secondsValue = undefined
|
|
289
|
+
|
|
290
|
+
return formatDurationString(
|
|
291
|
+
undefined,
|
|
292
|
+
undefined,
|
|
293
|
+
hoursValue,
|
|
294
|
+
minutesValue,
|
|
295
|
+
secondsValue,
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Convert a number of days to a duration string.
|
|
301
|
+
* @param days How many days the duration should represent.
|
|
302
|
+
* @returns A string representing the duration in the format `P[n]D`, prefixed with `-` if negative.
|
|
303
|
+
* @throws {Error} If days is NaN.
|
|
304
|
+
*/
|
|
305
|
+
export function daysToDurationString(days: number): string {
|
|
306
|
+
return formatDurationString(
|
|
307
|
+
undefined,
|
|
308
|
+
days,
|
|
309
|
+
undefined,
|
|
310
|
+
undefined,
|
|
311
|
+
undefined
|
|
312
|
+
)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Convert a number of weeks to a duration string.
|
|
317
|
+
* @param weeks How many weeks the duration should represent.
|
|
318
|
+
* @returns A string representing the duration in the format `P[n]W`, prefixed with `-` if negative.
|
|
319
|
+
* @throws {Error} If weeks is NaN.
|
|
320
|
+
*/
|
|
321
|
+
export function weeksToDurationString(weeks: number): string {
|
|
322
|
+
return formatDurationString(
|
|
323
|
+
weeks,
|
|
324
|
+
undefined,
|
|
325
|
+
undefined,
|
|
326
|
+
undefined,
|
|
327
|
+
undefined
|
|
328
|
+
)
|
|
329
|
+
}
|
package/src/index.ts
CHANGED
package/src/io.ts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import readline from 'readline'
|
|
3
3
|
import { Calendar } from './components/Calendar'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DeserializationError,
|
|
6
|
+
deserializeComponent,
|
|
7
|
+
deserializeComponentString,
|
|
8
|
+
} from './parse'
|
|
9
|
+
import { ComponentValidationError } from './component'
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* Read a calendar from a iCalendar file.
|
|
8
13
|
* @param path Path to the file.
|
|
9
14
|
* @returns The calendar deserialized from the file.
|
|
10
15
|
* @throws {DeserializationError} If the file content is not a valid calendar.
|
|
16
|
+
* @deprecated Use `loadCalendarSync` instead.
|
|
11
17
|
*/
|
|
12
18
|
export async function load(path: fs.PathLike): Promise<Calendar> {
|
|
13
19
|
const stream = fs.createReadStream(path)
|
|
@@ -25,12 +31,30 @@ export async function load(path: fs.PathLike): Promise<Calendar> {
|
|
|
25
31
|
return new Calendar(component)
|
|
26
32
|
}
|
|
27
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Read a calendar from a iCalendar file.
|
|
36
|
+
* @param path Path to the file.
|
|
37
|
+
* @param encoding The file encoding, defaults to UTF-8.
|
|
38
|
+
* @returns The calendar deserialized from the file.
|
|
39
|
+
* @throws {DeserializationError} If the file content is not a valid component.
|
|
40
|
+
* @throws {ComponentValidationError} If the deserialized component is not a VCALENDAR.
|
|
41
|
+
*/
|
|
42
|
+
export function loadCalendarSync(
|
|
43
|
+
path: fs.PathLike,
|
|
44
|
+
encoding: BufferEncoding = 'utf8'
|
|
45
|
+
): Calendar {
|
|
46
|
+
const text = fs.readFileSync(path, { encoding: encoding })
|
|
47
|
+
const component = deserializeComponentString(text)
|
|
48
|
+
return new Calendar(component)
|
|
49
|
+
}
|
|
50
|
+
|
|
28
51
|
/**
|
|
29
52
|
* Write a calendar to a file.
|
|
30
53
|
* @param calendar The calendar to write to file.
|
|
31
54
|
* @param path Path to the file to write.
|
|
32
55
|
* @example
|
|
33
56
|
* dump(myCalendar, 'calendar.ics')
|
|
57
|
+
* @deprecated Use `dumpCalendarSync` instead.
|
|
34
58
|
*/
|
|
35
59
|
export function dump(calendar: Calendar, path: string): Promise<void> {
|
|
36
60
|
return new Promise(resolve => {
|
|
@@ -39,3 +63,20 @@ export function dump(calendar: Calendar, path: string): Promise<void> {
|
|
|
39
63
|
})
|
|
40
64
|
})
|
|
41
65
|
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Write a calendar to a file.
|
|
69
|
+
* @param calendar The calendar to write to file.
|
|
70
|
+
* @param path Path to the file to write.
|
|
71
|
+
* @param encoding The file encoding, defaults to UTF-8.
|
|
72
|
+
* @example
|
|
73
|
+
* dump(myCalendar, 'calendar.ics')
|
|
74
|
+
*/
|
|
75
|
+
export function dumpCalendarSync(
|
|
76
|
+
calendar: Calendar,
|
|
77
|
+
path: string,
|
|
78
|
+
encoding: BufferEncoding = 'utf8'
|
|
79
|
+
): void {
|
|
80
|
+
const text = calendar.serialize()
|
|
81
|
+
fs.writeFileSync(path, text, { encoding: encoding })
|
|
82
|
+
}
|