ical-generator 8.1.2-develop.9 → 9.0.0-develop.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/src/tools.ts CHANGED
@@ -8,172 +8,54 @@ import {
8
8
  type ICalMomentStub,
9
9
  type ICalMomentTimezoneStub,
10
10
  type ICalOrganizer,
11
- type ICalRRuleStub
11
+ type ICalRRuleStub,
12
12
  } from './types.ts';
13
13
 
14
- /**
15
- * Converts a valid date/time object supported by this library to a string.
16
- */
17
- export function formatDate (timezone: string | null, d: ICalDateTimeValue, dateonly?: boolean, floating?: boolean): string {
18
- if(timezone?.startsWith('/')) {
19
- timezone = timezone.substr(1);
20
- }
21
-
22
- if(typeof d === 'string' || d instanceof Date) {
23
- const m = new Date(d);
24
-
25
- // (!dateonly && !floating) || !timezone => utc
26
- let s = m.getUTCFullYear() +
27
- String(m.getUTCMonth() + 1).padStart(2, '0') +
28
- m.getUTCDate().toString().padStart(2, '0');
29
-
30
- // (dateonly || floating) && timezone => tz
31
- if(timezone) {
32
- s = m.getFullYear() +
33
- String(m.getMonth() + 1).padStart(2, '0') +
34
- m.getDate().toString().padStart(2, '0');
35
- }
36
-
37
- if(dateonly) {
38
- return s;
39
- }
40
-
41
- if(timezone) {
42
- s += 'T' + m.getHours().toString().padStart(2, '0') +
43
- m.getMinutes().toString().padStart(2, '0') +
44
- m.getSeconds().toString().padStart(2, '0');
45
-
46
- return s;
47
- }
48
-
49
- s += 'T' + m.getUTCHours().toString().padStart(2, '0') +
50
- m.getUTCMinutes().toString().padStart(2, '0') +
51
- m.getUTCSeconds().toString().padStart(2, '0') +
52
- (floating ? '' : 'Z');
53
-
54
- return s;
55
- }
56
- else if(isMoment(d)) {
57
- // @see https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/
58
- const m = timezone
59
- ? (isMomentTZ(d) && !d.tz() ? d.clone().tz(timezone) : d)
60
- : (floating || (dateonly && isMomentTZ(d) && d.tz()) ? d : d.utc());
61
-
62
- return m.format('YYYYMMDD') + (!dateonly ? (
63
- 'T' + m.format('HHmmss') + (floating || timezone ? '' : 'Z')
64
- ) : '');
65
- }
66
- else if(isLuxonDate(d)) {
67
- const m = timezone
68
- ? d.setZone(timezone)
69
- : (floating || (dateonly && d.zone.type !== 'system') ? d : d.setZone('utc'));
70
-
71
- return m.toFormat('yyyyLLdd') + (!dateonly ? (
72
- 'T' + m.toFormat('HHmmss') + (floating || timezone ? '' : 'Z')
73
- ) : '');
74
- }
75
- else {
76
- // @see https://day.js.org/docs/en/plugin/utc
77
-
78
- let m = d;
79
- if(timezone) {
80
- m = typeof d.tz === 'function' ? d.tz(timezone) : d;
81
- }
82
- else if(floating) {
83
- // m = d;
84
- }
85
-
86
- else if (typeof d.utc === 'function') {
87
- m = d.utc();
88
- }
89
- else {
90
- throw new Error('Unable to convert dayjs object to UTC value: UTC plugin is not available!');
91
- }
92
-
93
- return m.format('YYYYMMDD') + (!dateonly ? (
94
- 'T' + m.format('HHmmss') + (floating || timezone ? '' : 'Z')
95
- ) : '');
96
- }
97
- }
98
-
99
- /**
100
- * Converts a valid date/time object supported by this library to a string.
101
- * For information about this format, see RFC 5545, section 3.3.5
102
- * https://tools.ietf.org/html/rfc5545#section-3.3.5
103
- */
104
- export function formatDateTZ (timezone: string | null, property: string, date: ICalDateTimeValue | Date | string, eventData?: {floating?: boolean | null, timezone?: string | null}): string {
105
- let tzParam = '';
106
- let floating = eventData?.floating || false;
107
-
108
- if (eventData?.timezone) {
109
- tzParam = ';TZID=' + eventData.timezone;
110
-
111
- // This isn't a 'floating' event because it has a timezone;
112
- // but we use it to omit the 'Z' UTC specifier in formatDate()
113
- floating = true;
114
- }
115
-
116
- return property + tzParam + ':' + formatDate(timezone, date, false, floating);
117
- }
118
-
119
- /**
120
- * Escapes special characters in the given string
121
- */
122
- export function escape (str: string | unknown, inQuotes: boolean): string {
123
- return String(str).replace(inQuotes ? /[\\"]/g : /[\\;,]/g, function (match) {
124
- return '\\' + match;
125
- }).replace(/(?:\r\n|\r|\n)/g, '\\n');
126
- }
127
-
128
- /**
129
- * Trim line length of given string
130
- */
131
- export function foldLines (input: string): string {
132
- return input.split('\r\n').map(function (line) {
133
- let result = '';
134
- let c = 0;
135
- for (let i = 0; i < line.length; i++) {
136
- let ch = line.charAt(i);
137
-
138
- // surrogate pair, see https://mathiasbynens.be/notes/javascript-encoding#surrogate-pairs
139
- if (ch >= '\ud800' && ch <= '\udbff') {
140
- ch += line.charAt(++i);
141
- }
142
-
143
- // TextEncoder is available in browsers and node.js >= 11.0.0
144
- const charsize = new TextEncoder().encode(ch).length;
145
- c += charsize;
146
- if (c > 74) {
147
- result += '\r\n ';
148
- c = charsize;
149
- }
150
-
151
- result += ch;
152
- }
153
- return result;
154
- }).join('\r\n');
155
- }
156
-
157
- export function addOrGetCustomAttributes (data: {x: [string, string][]}, keyOrArray: ({key: string, value: string})[] | [string, string][] | Record<string, string>): void;
158
- export function addOrGetCustomAttributes (data: {x: [string, string][]}, keyOrArray: string, value: string): void;
159
- export function addOrGetCustomAttributes (data: {x: [string, string][]}): ({key: string, value: string})[];
160
- export function addOrGetCustomAttributes (data: {x: [string, string][]}, keyOrArray?: ({key: string, value: string})[] | [string, string][] | Record<string, string> | string | undefined, value?: string | undefined): void | ({key: string, value: string})[] {
14
+ export function addOrGetCustomAttributes(
15
+ data: { x: [string, string][] },
16
+ keyOrArray:
17
+ | [string, string][]
18
+ | Record<string, string>
19
+ | { key: string; value: string }[],
20
+ ): void;
21
+
22
+ export function addOrGetCustomAttributes(
23
+ data: { x: [string, string][] },
24
+ keyOrArray: string,
25
+ value: string,
26
+ ): void;
27
+
28
+ export function addOrGetCustomAttributes(data: {
29
+ x: [string, string][];
30
+ }): { key: string; value: string }[];
31
+
32
+ export function addOrGetCustomAttributes(
33
+ data: { x: [string, string][] },
34
+ keyOrArray?:
35
+ | [string, string][]
36
+ | Record<string, string>
37
+ | string
38
+ | undefined
39
+ | { key: string; value: string }[],
40
+ value?: string | undefined,
41
+ ): void | { key: string; value: string }[] {
161
42
  if (Array.isArray(keyOrArray)) {
162
- data.x = keyOrArray.map((o: {key: string, value: string} | [string, string]) => {
163
- if(Array.isArray(o)) {
164
- return o;
165
- }
166
- if (typeof o.key !== 'string' || typeof o.value !== 'string') {
167
- throw new Error('Either key or value is not a string!');
168
- }
169
- if (o.key.substr(0, 2) !== 'X-') {
170
- throw new Error('Key has to start with `X-`!');
171
- }
172
-
173
- return [o.key, o.value] as [string, string];
174
- });
175
- }
176
- else if (typeof keyOrArray === 'object') {
43
+ data.x = keyOrArray.map(
44
+ (o: [string, string] | { key: string; value: string }) => {
45
+ if (Array.isArray(o)) {
46
+ return o;
47
+ }
48
+ if (typeof o.key !== 'string' || typeof o.value !== 'string') {
49
+ throw new Error('Either key or value is not a string!');
50
+ }
51
+ if (o.key.substr(0, 2) !== 'X-') {
52
+ throw new Error('Key has to start with `X-`!');
53
+ }
54
+
55
+ return [o.key, o.value] as [string, string];
56
+ },
57
+ );
58
+ } else if (typeof keyOrArray === 'object') {
177
59
  data.x = Object.entries(keyOrArray).map(([key, value]) => {
178
60
  if (typeof key !== 'string' || typeof value !== 'string') {
179
61
  throw new Error('Either key or value is not a string!');
@@ -184,29 +66,70 @@ export function addOrGetCustomAttributes (data: {x: [string, string][]}, keyOrAr
184
66
 
185
67
  return [key, value];
186
68
  });
187
- }
188
- else if (typeof keyOrArray === 'string' && typeof value === 'string') {
69
+ } else if (typeof keyOrArray === 'string' && typeof value === 'string') {
189
70
  if (keyOrArray.substr(0, 2) !== 'X-') {
190
71
  throw new Error('Key has to start with `X-`!');
191
72
  }
192
73
 
193
74
  data.x.push([keyOrArray, value]);
194
- }
195
- else {
196
- return data.x.map(a => ({
75
+ } else {
76
+ return data.x.map((a) => ({
197
77
  key: a[0],
198
- value: a[1]
78
+ value: a[1],
199
79
  }));
200
80
  }
201
81
  }
202
82
 
203
- export function generateCustomAttributes (data: {x: [string, string][]}): string {
204
- const str = data.x
205
- .map(([key, value]) => key.toUpperCase() + ':' + escape(value, false))
206
- .join('\r\n');
207
- return str.length ? str + '\r\n' : '';
83
+ /**
84
+ * Checks if the given input is a valid date and
85
+ * returns the internal representation (= moment object)
86
+ */
87
+ export function checkDate(
88
+ value: ICalDateTimeValue,
89
+ attribute: string,
90
+ ): ICalDateTimeValue {
91
+ // Date & String
92
+ if (
93
+ (value instanceof Date && isNaN(value.getTime())) ||
94
+ (typeof value === 'string' && isNaN(new Date(value).getTime()))
95
+ ) {
96
+ throw new Error(`\`${attribute}\` has to be a valid date!`);
97
+ }
98
+ if (value instanceof Date || typeof value === 'string') {
99
+ return value;
100
+ }
101
+
102
+ // Luxon
103
+ if (isLuxonDate(value) && value.isValid === true) {
104
+ return value;
105
+ }
106
+
107
+ // Moment / Moment Timezone
108
+ if ((isMoment(value) || isDayjs(value)) && value.isValid()) {
109
+ return value;
110
+ }
111
+
112
+ throw new Error(`\`${attribute}\` has to be a valid date!`);
208
113
  }
114
+ /**
115
+ * Checks if the given string `value` is a
116
+ * valid one for the type `type`
117
+ */
118
+ export function checkEnum(
119
+ type: Record<string, string>,
120
+ value: unknown,
121
+ ): unknown {
122
+ const allowedValues = Object.values(type);
123
+ const valueStr = String(value).toUpperCase();
124
+
125
+ if (!valueStr || !allowedValues.includes(valueStr)) {
126
+ throw new Error(
127
+ `Input must be one of the following: ${allowedValues.join(', ')}`,
128
+ );
129
+ }
209
130
 
131
+ return valueStr;
132
+ }
210
133
  /**
211
134
  * Check the given string or ICalOrganizer. Parses
212
135
  * the string for name and email address if possible.
@@ -214,43 +137,47 @@ export function generateCustomAttributes (data: {x: [string, string][]}): string
214
137
  * @param attribute Attribute name for error messages
215
138
  * @param value Value to parse name/email from
216
139
  */
217
- export function checkNameAndMail (attribute: string, value: string | ICalOrganizer): ICalOrganizer {
140
+ export function checkNameAndMail(
141
+ attribute: string,
142
+ value: ICalOrganizer | string,
143
+ ): ICalOrganizer {
218
144
  let result: ICalOrganizer | null = null;
219
145
 
220
146
  if (typeof value === 'string') {
221
147
  const match = value.match(/^(.+) ?<([^>]+)>$/);
222
148
  if (match) {
223
149
  result = {
150
+ email: match[2].trim(),
224
151
  name: match[1].trim(),
225
- email: match[2].trim()
226
152
  };
227
- }
228
- else if(value.includes('@')) {
153
+ } else if (value.includes('@')) {
229
154
  result = {
155
+ email: value.trim(),
230
156
  name: value.trim(),
231
- email: value.trim()
232
157
  };
233
158
  }
234
- }
235
- else if (typeof value === 'object') {
159
+ } else if (typeof value === 'object') {
236
160
  result = {
237
- name: value.name,
238
161
  email: value.email,
239
162
  mailto: value.mailto,
240
- sentBy: value.sentBy
163
+ name: value.name,
164
+ sentBy: value.sentBy,
241
165
  };
242
166
  }
243
167
 
244
168
  if (!result && typeof value === 'string') {
245
169
  throw new Error(
246
- '`' + attribute + '` isn\'t formated correctly. See https://sebbo2002.github.io/ical-generator/develop/'+
247
- 'reference/interfaces/ICalOrganizer.html'
170
+ '`' +
171
+ attribute +
172
+ "` isn't formated correctly. See https://sebbo2002.github.io/ical-generator/develop/" +
173
+ 'reference/interfaces/ICalOrganizer.html',
248
174
  );
249
- }
250
- else if (!result) {
175
+ } else if (!result) {
251
176
  throw new Error(
252
- '`' + attribute + '` needs to be a valid formed string or an object. See https://sebbo2002.github.io/'+
253
- 'ical-generator/develop/reference/interfaces/ICalOrganizer.html'
177
+ '`' +
178
+ attribute +
179
+ '` needs to be a valid formed string or an object. See https://sebbo2002.github.io/' +
180
+ 'ical-generator/develop/reference/interfaces/ICalOrganizer.html',
254
181
  );
255
182
  }
256
183
 
@@ -260,107 +187,258 @@ export function checkNameAndMail (attribute: string, value: string | ICalOrganiz
260
187
 
261
188
  return result;
262
189
  }
263
-
264
190
  /**
265
- * Checks if the given string `value` is a
266
- * valid one for the type `type`
191
+ * Escapes special characters in the given string
267
192
  */
268
- export function checkEnum(type: Record<string, string>, value: unknown): unknown {
269
- const allowedValues = Object.values(type);
270
- const valueStr = String(value).toUpperCase();
271
-
272
- if (!valueStr || !allowedValues.includes(valueStr)) {
273
- throw new Error(`Input must be one of the following: ${allowedValues.join(', ')}`);
274
- }
275
-
276
- return valueStr;
193
+ export function escape(str: string | unknown, inQuotes: boolean): string {
194
+ return String(str)
195
+ .replace(inQuotes ? /[\\"]/g : /[\\;,]/g, function (match) {
196
+ return '\\' + match;
197
+ })
198
+ .replace(/(?:\r\n|\r|\n)/g, '\\n');
277
199
  }
278
200
 
279
201
  /**
280
- * Checks if the given input is a valid date and
281
- * returns the internal representation (= moment object)
202
+ * Trim line length of given string
282
203
  */
283
- export function checkDate(value: ICalDateTimeValue, attribute: string): ICalDateTimeValue {
204
+ export function foldLines(input: string): string {
205
+ return input
206
+ .split('\r\n')
207
+ .map(function (line) {
208
+ let result = '';
209
+ let c = 0;
210
+ for (let i = 0; i < line.length; i++) {
211
+ let ch = line.charAt(i);
212
+
213
+ // surrogate pair, see https://mathiasbynens.be/notes/javascript-encoding#surrogate-pairs
214
+ if (ch >= '\ud800' && ch <= '\udbff') {
215
+ ch += line.charAt(++i);
216
+ }
217
+
218
+ // TextEncoder is available in browsers and node.js >= 11.0.0
219
+ const charsize = new TextEncoder().encode(ch).length;
220
+ c += charsize;
221
+ if (c > 74) {
222
+ result += '\r\n ';
223
+ c = charsize;
224
+ }
225
+
226
+ result += ch;
227
+ }
228
+ return result;
229
+ })
230
+ .join('\r\n');
231
+ }
284
232
 
285
- // Date & String
286
- if(
287
- (value instanceof Date && isNaN(value.getTime())) ||
288
- (typeof value === 'string' && isNaN(new Date(value).getTime()))
289
- ) {
290
- throw new Error(`\`${attribute}\` has to be a valid date!`);
291
- }
292
- if(value instanceof Date || typeof value === 'string') {
293
- return value;
233
+ /**
234
+ * Converts a valid date/time object supported by this library to a string.
235
+ */
236
+ export function formatDate(
237
+ timezone: null | string,
238
+ d: ICalDateTimeValue,
239
+ dateonly?: boolean,
240
+ floating?: boolean,
241
+ ): string {
242
+ if (timezone?.startsWith('/')) {
243
+ timezone = timezone.substr(1);
294
244
  }
295
245
 
296
- // Luxon
297
- if(isLuxonDate(value) && value.isValid === true) {
298
- return value;
299
- }
246
+ if (typeof d === 'string' || d instanceof Date) {
247
+ const m = new Date(d);
300
248
 
301
- // Moment / Moment Timezone
302
- if((isMoment(value) || isDayjs(value)) && value.isValid()) {
303
- return value;
304
- }
249
+ // (!dateonly && !floating) || !timezone => utc
250
+ let s =
251
+ m.getUTCFullYear() +
252
+ String(m.getUTCMonth() + 1).padStart(2, '0') +
253
+ m.getUTCDate().toString().padStart(2, '0');
305
254
 
306
- throw new Error(`\`${attribute}\` has to be a valid date!`);
307
- }
255
+ // (dateonly || floating) && timezone => tz
256
+ if (timezone) {
257
+ s =
258
+ m.getFullYear() +
259
+ String(m.getMonth() + 1).padStart(2, '0') +
260
+ m.getDate().toString().padStart(2, '0');
261
+ }
308
262
 
309
- export function toDate(value: ICalDateTimeValue): Date {
310
- if(typeof value === 'string' || value instanceof Date) {
311
- return new Date(value);
312
- }
263
+ if (dateonly) {
264
+ return s;
265
+ }
313
266
 
314
- if(isLuxonDate(value)) {
315
- return value.toJSDate();
316
- }
267
+ if (timezone) {
268
+ s +=
269
+ 'T' +
270
+ m.getHours().toString().padStart(2, '0') +
271
+ m.getMinutes().toString().padStart(2, '0') +
272
+ m.getSeconds().toString().padStart(2, '0');
317
273
 
318
- return value.toDate();
274
+ return s;
275
+ }
276
+
277
+ s +=
278
+ 'T' +
279
+ m.getUTCHours().toString().padStart(2, '0') +
280
+ m.getUTCMinutes().toString().padStart(2, '0') +
281
+ m.getUTCSeconds().toString().padStart(2, '0') +
282
+ (floating ? '' : 'Z');
283
+
284
+ return s;
285
+ } else if (isMoment(d)) {
286
+ // @see https://momentjs.com/timezone/docs/#/using-timezones/parsing-in-zone/
287
+ const m = timezone
288
+ ? isMomentTZ(d) && !d.tz()
289
+ ? d.clone().tz(timezone)
290
+ : d
291
+ : floating || (dateonly && isMomentTZ(d) && d.tz())
292
+ ? d
293
+ : d.utc();
294
+
295
+ return (
296
+ m.format('YYYYMMDD') +
297
+ (!dateonly
298
+ ? 'T' + m.format('HHmmss') + (floating || timezone ? '' : 'Z')
299
+ : '')
300
+ );
301
+ } else if (isLuxonDate(d)) {
302
+ const m = timezone
303
+ ? d.setZone(timezone)
304
+ : floating || (dateonly && d.zone.type !== 'system')
305
+ ? d
306
+ : d.setZone('utc');
307
+
308
+ return (
309
+ m.toFormat('yyyyLLdd') +
310
+ (!dateonly
311
+ ? 'T' + m.toFormat('HHmmss') + (floating || timezone ? '' : 'Z')
312
+ : '')
313
+ );
314
+ } else {
315
+ // @see https://day.js.org/docs/en/plugin/utc
316
+
317
+ let m = d;
318
+ if (timezone) {
319
+ m = typeof d.tz === 'function' ? d.tz(timezone) : d;
320
+ } else if (floating) {
321
+ // m = d;
322
+ } else if (typeof d.utc === 'function') {
323
+ m = d.utc();
324
+ } else {
325
+ throw new Error(
326
+ 'Unable to convert dayjs object to UTC value: UTC plugin is not available!',
327
+ );
328
+ }
329
+
330
+ return (
331
+ m.format('YYYYMMDD') +
332
+ (!dateonly
333
+ ? 'T' + m.format('HHmmss') + (floating || timezone ? '' : 'Z')
334
+ : '')
335
+ );
336
+ }
319
337
  }
320
338
 
321
- export function isMoment(value: ICalDateTimeValue): value is ICalMomentStub {
339
+ /**
340
+ * Converts a valid date/time object supported by this library to a string.
341
+ * For information about this format, see RFC 5545, section 3.3.5
342
+ * https://tools.ietf.org/html/rfc5545#section-3.3.5
343
+ */
344
+ export function formatDateTZ(
345
+ timezone: null | string,
346
+ property: string,
347
+ date: Date | ICalDateTimeValue | string,
348
+ eventData?: { floating?: boolean | null; timezone?: null | string },
349
+ ): string {
350
+ let tzParam = '';
351
+ let floating = eventData?.floating || false;
322
352
 
323
- // @ts-expect-error _isAMomentObject is a private property
324
- return value != null && value._isAMomentObject != null;
353
+ if (eventData?.timezone) {
354
+ tzParam = ';TZID=' + eventData.timezone;
355
+
356
+ // This isn't a 'floating' event because it has a timezone;
357
+ // but we use it to omit the 'Z' UTC specifier in formatDate()
358
+ floating = true;
359
+ }
360
+
361
+ return (
362
+ property + tzParam + ':' + formatDate(timezone, date, false, floating)
363
+ );
325
364
  }
326
- export function isMomentTZ(value: ICalDateTimeValue): value is ICalMomentTimezoneStub {
327
- return isMoment(value) && 'tz' in value && typeof value.tz === 'function';
365
+
366
+ export function generateCustomAttributes(data: {
367
+ x: [string, string][];
368
+ }): string {
369
+ const str = data.x
370
+ .map(([key, value]) => key.toUpperCase() + ':' + escape(value, false))
371
+ .join('\r\n');
372
+ return str.length ? str + '\r\n' : '';
328
373
  }
374
+
329
375
  export function isDayjs(value: ICalDateTimeValue): value is ICalDayJsStub {
330
- return typeof value === 'object' &&
376
+ return (
377
+ typeof value === 'object' &&
331
378
  value !== null &&
332
379
  !(value instanceof Date) &&
333
380
  !isMoment(value) &&
334
- !isLuxonDate(value);
335
- }
336
- export function isLuxonDate(value: ICalDateTimeValue): value is ICalLuxonDateTimeStub {
337
- return typeof value === 'object' && value !== null && 'toJSDate' in value && typeof value.toJSDate === 'function';
381
+ !isLuxonDate(value)
382
+ );
338
383
  }
339
384
 
340
- export function isMomentDuration(value: unknown): value is ICalMomentDurationStub {
341
- return value !== null && typeof value === 'object' && 'asSeconds' in value && typeof value.asSeconds === 'function';
385
+ export function isLuxonDate(
386
+ value: ICalDateTimeValue,
387
+ ): value is ICalLuxonDateTimeStub {
388
+ return (
389
+ typeof value === 'object' &&
390
+ value !== null &&
391
+ 'toJSDate' in value &&
392
+ typeof value.toJSDate === 'function'
393
+ );
394
+ }
395
+ export function isMoment(value: ICalDateTimeValue): value is ICalMomentStub {
396
+ // @ts-expect-error _isAMomentObject is a private property
397
+ return value != null && value._isAMomentObject != null;
398
+ }
399
+ export function isMomentDuration(
400
+ value: unknown,
401
+ ): value is ICalMomentDurationStub {
402
+ return (
403
+ value !== null &&
404
+ typeof value === 'object' &&
405
+ 'asSeconds' in value &&
406
+ typeof value.asSeconds === 'function'
407
+ );
408
+ }
409
+ export function isMomentTZ(
410
+ value: ICalDateTimeValue,
411
+ ): value is ICalMomentTimezoneStub {
412
+ return isMoment(value) && 'tz' in value && typeof value.tz === 'function';
342
413
  }
343
414
 
344
415
  export function isRRule(value: unknown): value is ICalRRuleStub {
345
- return value !== null && typeof value === 'object' && 'between' in value && typeof value.between === 'function' && typeof value.toString === 'function';
416
+ return (
417
+ value !== null &&
418
+ typeof value === 'object' &&
419
+ 'between' in value &&
420
+ typeof value.between === 'function' &&
421
+ typeof value.toString === 'function'
422
+ );
346
423
  }
347
424
 
348
- export function toJSON(value: ICalDateTimeValue | null | undefined): string | null | undefined {
349
- if(!value) {
350
- return null;
425
+ export function toDate(value: ICalDateTimeValue): Date {
426
+ if (typeof value === 'string' || value instanceof Date) {
427
+ return new Date(value);
351
428
  }
352
- if(typeof value === 'string') {
353
- return value;
429
+
430
+ if (isLuxonDate(value)) {
431
+ return value.toJSDate();
354
432
  }
355
433
 
356
- return value.toJSON();
434
+ return value.toDate();
357
435
  }
358
436
 
359
437
  export function toDurationString(seconds: number): string {
360
438
  let string = '';
361
439
 
362
440
  // < 0
363
- if(seconds < 0) {
441
+ if (seconds < 0) {
364
442
  string = '-';
365
443
  seconds *= -1;
366
444
  }
@@ -368,35 +446,47 @@ export function toDurationString(seconds: number): string {
368
446
  string += 'P';
369
447
 
370
448
  // DAYS
371
- if(seconds >= 86400) {
449
+ if (seconds >= 86400) {
372
450
  string += Math.floor(seconds / 86400) + 'D';
373
451
  seconds %= 86400;
374
452
  }
375
- if(!seconds && string.length > 1) {
453
+ if (!seconds && string.length > 1) {
376
454
  return string;
377
455
  }
378
456
 
379
457
  string += 'T';
380
458
 
381
459
  // HOURS
382
- if(seconds >= 3600) {
460
+ if (seconds >= 3600) {
383
461
  string += Math.floor(seconds / 3600) + 'H';
384
462
  seconds %= 3600;
385
463
  }
386
464
 
387
465
  // MINUTES
388
- if(seconds >= 60) {
466
+ if (seconds >= 60) {
389
467
  string += Math.floor(seconds / 60) + 'M';
390
468
  seconds %= 60;
391
469
  }
392
470
 
393
471
  // SECONDS
394
- if(seconds > 0) {
472
+ if (seconds > 0) {
395
473
  string += seconds + 'S';
396
- }
397
- else if(string.length <= 2) {
474
+ } else if (string.length <= 2) {
398
475
  string += '0S';
399
476
  }
400
477
 
401
478
  return string;
402
479
  }
480
+
481
+ export function toJSON(
482
+ value: ICalDateTimeValue | null | undefined,
483
+ ): null | string | undefined {
484
+ if (!value) {
485
+ return null;
486
+ }
487
+ if (typeof value === 'string') {
488
+ return value;
489
+ }
490
+
491
+ return value.toJSON();
492
+ }