iamcal 1.1.0 → 2.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/lib/component.d.ts +25 -0
- package/lib/component.d.ts.map +1 -0
- package/lib/component.js +120 -0
- package/lib/components/Calendar.d.ts +45 -0
- package/lib/components/Calendar.d.ts.map +1 -0
- package/lib/components/Calendar.js +93 -0
- package/lib/components/CalendarEvent.d.ts +47 -0
- package/lib/components/CalendarEvent.d.ts.map +1 -0
- package/lib/components/CalendarEvent.js +148 -0
- package/lib/components/TimeZone.d.ts +51 -0
- package/lib/components/TimeZone.d.ts.map +1 -0
- package/lib/components/TimeZone.js +132 -0
- package/lib/components/index.d.ts +4 -0
- package/lib/components/index.d.ts.map +1 -0
- package/lib/components/index.js +20 -0
- package/lib/index.d.ts +4 -149
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +19 -473
- package/lib/io.d.ts +1 -1
- package/lib/io.d.ts.map +1 -1
- package/lib/io.js +3 -3
- package/lib/parse.d.ts +2 -2
- package/lib/parse.d.ts.map +1 -1
- package/lib/parse.js +6 -5
- package/package.json +4 -11
- package/src/component.ts +139 -0
- package/src/components/Calendar.ts +124 -0
- package/src/components/CalendarEvent.ts +166 -0
- package/src/components/TimeZone.ts +162 -0
- package/src/components/index.ts +3 -0
- package/src/index.ts +4 -579
- package/src/io.ts +1 -2
- package/src/parse.ts +2 -2
- package/lib/types.d.ts +0 -6
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -3
- package/src/types.ts +0 -5
package/package.json
CHANGED
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iamcal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Read and write ICalendar files",
|
|
5
5
|
"files": [
|
|
6
6
|
"/lib",
|
|
7
7
|
"/src"
|
|
8
8
|
],
|
|
9
|
+
"type": "commonjs",
|
|
10
|
+
"main": "./lib/index.js",
|
|
11
|
+
"types": "./lib/index.d.ts",
|
|
9
12
|
"exports": {
|
|
10
13
|
".": {
|
|
11
14
|
"default": "./lib/index.js",
|
|
12
15
|
"types": "./lib/index.d.ts"
|
|
13
|
-
},
|
|
14
|
-
"./parse": {
|
|
15
|
-
"default": "./lib/parse.js",
|
|
16
|
-
"types": "./lib/parse.d.ts"
|
|
17
|
-
},
|
|
18
|
-
"./io": {
|
|
19
|
-
"default": "./lib/io.js",
|
|
20
|
-
"types": "./lib/io.d.ts"
|
|
21
16
|
}
|
|
22
17
|
},
|
|
23
|
-
"main": "./lib/index.js",
|
|
24
|
-
"types": "./lib/index.d.ts",
|
|
25
18
|
"scripts": {
|
|
26
19
|
"build": "rimraf lib && tsc",
|
|
27
20
|
"test": "echo \"Error: no test specified\" && exit 1"
|
package/src/component.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Max line length as defined by RFC 5545 3.1.
|
|
2
|
+
const MAX_LINE_LENGTH = 75
|
|
3
|
+
|
|
4
|
+
export interface Property {
|
|
5
|
+
name: string
|
|
6
|
+
params: string[]
|
|
7
|
+
value: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class Component {
|
|
11
|
+
name: string
|
|
12
|
+
properties: Property[]
|
|
13
|
+
components: Component[]
|
|
14
|
+
|
|
15
|
+
constructor(name: string, properties?: Property[], components?: Component[]) {
|
|
16
|
+
this.name = name
|
|
17
|
+
if (properties) {
|
|
18
|
+
this.properties = properties
|
|
19
|
+
} else {
|
|
20
|
+
this.properties = new Array()
|
|
21
|
+
}
|
|
22
|
+
if (components) {
|
|
23
|
+
this.components = components
|
|
24
|
+
} else {
|
|
25
|
+
this.components = new Array()
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
serialize(): string {
|
|
30
|
+
// Create lines
|
|
31
|
+
const lines = [`BEGIN:${this.name}`]
|
|
32
|
+
|
|
33
|
+
for (let property of this.properties) {
|
|
34
|
+
let line =
|
|
35
|
+
property['name'] + //
|
|
36
|
+
property.params.map(p => ';' + p).join('') +
|
|
37
|
+
':' +
|
|
38
|
+
property['value']
|
|
39
|
+
|
|
40
|
+
// Wrap lines
|
|
41
|
+
while (line.length > MAX_LINE_LENGTH) {
|
|
42
|
+
lines.push(line.substring(0, MAX_LINE_LENGTH))
|
|
43
|
+
line = ' ' + line.substring(MAX_LINE_LENGTH)
|
|
44
|
+
}
|
|
45
|
+
lines.push(line)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
for (let component of this.components) {
|
|
49
|
+
lines.push(component.serialize())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
lines.push(`END:${this.name}`)
|
|
53
|
+
|
|
54
|
+
// Join lines
|
|
55
|
+
const serialized = lines.join('\n')
|
|
56
|
+
|
|
57
|
+
return serialized
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getProperty(name: string): Property | null {
|
|
61
|
+
for (const property of this.properties) {
|
|
62
|
+
if (property.name === name) {
|
|
63
|
+
return property
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setProperty(name: string, value: string): this {
|
|
70
|
+
for (const property of this.properties) {
|
|
71
|
+
if (property.name === name) {
|
|
72
|
+
property.value = value
|
|
73
|
+
return this
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
this.properties.push({
|
|
77
|
+
name: name,
|
|
78
|
+
params: [],
|
|
79
|
+
value: value,
|
|
80
|
+
})
|
|
81
|
+
return this
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
removePropertiesWithName(name: string) {
|
|
85
|
+
const index = this.properties.findIndex(p => p.name === name)
|
|
86
|
+
if (index === -1) return this
|
|
87
|
+
// Remove property at index
|
|
88
|
+
this.properties.splice(index, 1)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getPropertyParams(name: string): string[] | null {
|
|
92
|
+
for (const property of this.properties) {
|
|
93
|
+
if (property.name === name) {
|
|
94
|
+
return property.params
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setPropertyParams(name: string, params: string[]): this {
|
|
101
|
+
for (const property of this.properties) {
|
|
102
|
+
if (property.name === name) {
|
|
103
|
+
property.params = params
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return this
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
addComponent(component: Component): this {
|
|
110
|
+
this.components.push(component)
|
|
111
|
+
return this
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Remove a component from this component
|
|
116
|
+
* @returns `true` if the component was removed. `false` if the component was not present
|
|
117
|
+
*/
|
|
118
|
+
removeComponent(component: Component): boolean {
|
|
119
|
+
const index = this.components.indexOf(component)
|
|
120
|
+
if (index === -1) return false
|
|
121
|
+
|
|
122
|
+
// Remove element at index from list
|
|
123
|
+
this.components.splice(index, 1)
|
|
124
|
+
|
|
125
|
+
return true
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
getComponents(name: string): Component[] {
|
|
129
|
+
const components: Component[] = []
|
|
130
|
+
|
|
131
|
+
for (let component of this.components) {
|
|
132
|
+
if (component.name === name) {
|
|
133
|
+
components.push(component)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return components
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Component } from '../component'
|
|
2
|
+
import { CalendarEvent } from './CalendarEvent'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a VCALENDAR component, the root component of an iCalendar file.
|
|
6
|
+
*/
|
|
7
|
+
export class Calendar extends Component {
|
|
8
|
+
name = 'VCALENDAR';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param prodid A unique identifier of the program creating the calendar.
|
|
12
|
+
*
|
|
13
|
+
* Example: `-//Google Inc//Google Calendar 70.9054//EN`
|
|
14
|
+
*/
|
|
15
|
+
constructor(prodid: string)
|
|
16
|
+
/**
|
|
17
|
+
* @param prodid A unique identifier of the program creating the calendar.
|
|
18
|
+
*
|
|
19
|
+
* Example: `-//Google Inc//Google Calendar 70.9054//EN`
|
|
20
|
+
* @param version The version of the iCalendar specification that this calendar uses.
|
|
21
|
+
*/
|
|
22
|
+
constructor(prodid: string, version: string)
|
|
23
|
+
/**
|
|
24
|
+
* @param component A VCALENDAR component.
|
|
25
|
+
*/
|
|
26
|
+
constructor(component: Component)
|
|
27
|
+
constructor(a?: string | Component, b?: string) {
|
|
28
|
+
var component: Component
|
|
29
|
+
if (a instanceof Component) {
|
|
30
|
+
component = a as Component
|
|
31
|
+
} else {
|
|
32
|
+
const prodid = a as string
|
|
33
|
+
const version = (b as string) ?? '2.0'
|
|
34
|
+
component = new Component('VCALENDAR')
|
|
35
|
+
component.setProperty('PRODID', prodid)
|
|
36
|
+
component.setProperty('VERSION', version)
|
|
37
|
+
}
|
|
38
|
+
super(component.name, component.properties, component.components)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
events(): CalendarEvent[] {
|
|
42
|
+
return this.getComponents('VEVENT').map(c => new CalendarEvent(c))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
removeEvent(event: CalendarEvent): boolean
|
|
46
|
+
removeEvent(uid: string): boolean
|
|
47
|
+
removeEvent(a: CalendarEvent | string): boolean {
|
|
48
|
+
if (a instanceof CalendarEvent) {
|
|
49
|
+
const event = a as CalendarEvent
|
|
50
|
+
return this.removeComponent(event)
|
|
51
|
+
} else {
|
|
52
|
+
const uid = a as string
|
|
53
|
+
for (const event of this.events()) {
|
|
54
|
+
if (event.uid() !== uid) continue
|
|
55
|
+
return this.removeComponent(event)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
prodId(): string {
|
|
62
|
+
return this.getProperty('PRODID')!.value
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setProdId(value: string): this {
|
|
66
|
+
return this.setProperty('PRODID', value)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
version(): string {
|
|
70
|
+
return this.getProperty('VERSION')!.value
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setVersion(value: string): this {
|
|
74
|
+
return this.setProperty('VERSION', value)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
calScale(): string | undefined {
|
|
78
|
+
return this.getProperty('CALSCALE')?.value
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setCalScale(value: string): this {
|
|
82
|
+
return this.setProperty('CALSCALE', value)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
removeCalScale() {
|
|
86
|
+
this.removePropertiesWithName('CALSCALE')
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
method(): string | undefined {
|
|
90
|
+
return this.getProperty('METHOD')?.value
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
setMethod(value: string): this {
|
|
94
|
+
return this.setProperty('METHOD', value)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
removeMethod() {
|
|
98
|
+
this.removePropertiesWithName('METHOD')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
calendarName(): string | undefined {
|
|
102
|
+
return this.getProperty('X-WR-CALNAME')?.value
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
setCalendarName(value: string): this {
|
|
106
|
+
return this.setProperty('X-WR-CALNAME', value)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
removeCalendarName() {
|
|
110
|
+
this.removePropertiesWithName('X-WR-CALNAME')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
calendarDescription(): string | undefined {
|
|
114
|
+
return this.getProperty('X-WR-CALDESC')?.value
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
setCalendarDescription(value: string): this {
|
|
118
|
+
return this.setProperty('X-WR-CALDESC', value)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
removeCalendarDescription() {
|
|
122
|
+
this.removePropertiesWithName('X-WR-CALDESC')
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { Component } from "../component"
|
|
2
|
+
import { parseDate, toDateString, toDateTimeString } from "../parse"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a VEVENT component, representing an event in a calendar.
|
|
6
|
+
*/
|
|
7
|
+
export class CalendarEvent extends Component {
|
|
8
|
+
name = 'VEVENT';
|
|
9
|
+
|
|
10
|
+
constructor(uid: string, dtstamp: Date)
|
|
11
|
+
constructor(component: Component)
|
|
12
|
+
constructor(a: string | Component, b?: Date) {
|
|
13
|
+
var component: Component
|
|
14
|
+
if (b) {
|
|
15
|
+
const uid = a as string
|
|
16
|
+
const dtstamp = b as Date
|
|
17
|
+
component = new Component('VEVENT')
|
|
18
|
+
component.setProperty('UID', uid)
|
|
19
|
+
component.setProperty('DTSTAMP', toDateTimeString(dtstamp))
|
|
20
|
+
} else {
|
|
21
|
+
component = a as Component
|
|
22
|
+
}
|
|
23
|
+
super(component.name, component.properties, component.components)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
stamp(): Date {
|
|
27
|
+
return parseDate(this.getProperty('DTSTAMP')!)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setStamp(value: Date, fullDay: boolean = false): this {
|
|
31
|
+
if (fullDay) {
|
|
32
|
+
this.setProperty('DTSTAMP', toDateString(value))
|
|
33
|
+
this.setPropertyParams('DTSTAMP', ['VALUE=DATE'])
|
|
34
|
+
} else {
|
|
35
|
+
this.setProperty('DTSTAMP', toDateTimeString(value))
|
|
36
|
+
}
|
|
37
|
+
return this
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
uid(): string {
|
|
41
|
+
return this.getProperty('UID')!.value
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setUid(value: string): this {
|
|
45
|
+
return this.setProperty('UID', value)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
summary(): string {
|
|
49
|
+
return this.getProperty('SUMMARY')!.value
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
setSummary(value: string): this {
|
|
53
|
+
return this.setProperty('SUMMARY', value)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
removeSummary() {
|
|
57
|
+
this.removePropertiesWithName('SUMMARY')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
description(): string {
|
|
61
|
+
return this.getProperty('DESCRIPTION')!.value
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
setDescription(value: string): this {
|
|
65
|
+
return this.setProperty('DESCRIPTION', value)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
removeDescription() {
|
|
69
|
+
this.removePropertiesWithName('DESCRIPTION')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
location(): string | undefined {
|
|
73
|
+
return this.getProperty('LOCATION')?.value
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
setLocation(value: string): this {
|
|
77
|
+
return this.setProperty('LOCATION', value)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
removeLocation() {
|
|
81
|
+
this.removePropertiesWithName('LOCATION')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get the start of the event.
|
|
86
|
+
* If set as a full day the time will be at the start of the day.
|
|
87
|
+
*/
|
|
88
|
+
start(): Date {
|
|
89
|
+
return parseDate(this.getProperty('DTSTART')!)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Set the start of the event. */
|
|
93
|
+
setStart(value: Date, fullDay: boolean = false): this {
|
|
94
|
+
if (fullDay) {
|
|
95
|
+
this.setProperty('DTSTART', toDateString(value))
|
|
96
|
+
this.setPropertyParams('DTSTART', ['VALUE=DATE'])
|
|
97
|
+
} else {
|
|
98
|
+
this.setProperty('DTSTART', toDateTimeString(value))
|
|
99
|
+
}
|
|
100
|
+
return this
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
removeStart() {
|
|
104
|
+
this.removePropertiesWithName('DTSTART')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get the non-inclusive end of the event.
|
|
109
|
+
* If set as a full day the time will be at the start of the day.
|
|
110
|
+
*/
|
|
111
|
+
end(): Date {
|
|
112
|
+
return parseDate(this.getProperty('DTEND')!)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Set the non-inclusive end of the event.
|
|
117
|
+
*/
|
|
118
|
+
setEnd(value: Date, fullDay: boolean = false): this {
|
|
119
|
+
if (fullDay) {
|
|
120
|
+
this.setProperty('DTEND', toDateString(value))
|
|
121
|
+
this.setPropertyParams('DTEND', ['VALUE=DATE'])
|
|
122
|
+
} else {
|
|
123
|
+
this.setProperty('DTEND', toDateTimeString(value))
|
|
124
|
+
}
|
|
125
|
+
return this
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
removeEnd() {
|
|
129
|
+
this.removePropertiesWithName('DTEND')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
created(): Date | undefined {
|
|
133
|
+
const property = this.getProperty('CREATED')
|
|
134
|
+
if (!property) return
|
|
135
|
+
return parseDate(property)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
setCreated(value: Date): this {
|
|
139
|
+
return this.setProperty('CREATED', toDateTimeString(value))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
removeCreated() {
|
|
143
|
+
this.removePropertiesWithName('CREATED')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
geo(): [number, number] | undefined {
|
|
147
|
+
const text = this.getProperty('GEO')?.value
|
|
148
|
+
if (!text) return
|
|
149
|
+
const pattern = /^[+-]?\d+(\.\d+)?,[+-]?\d+(\.\d+)?$/
|
|
150
|
+
if (!pattern.test(text)) throw new Error(`Failed to parse GEO property: ${text}`)
|
|
151
|
+
const [longitude, latitude] = text.split(',')
|
|
152
|
+
return [parseFloat(longitude), parseFloat(latitude)]
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
setGeo(latitude: number, longitude: number): this {
|
|
156
|
+
const text = `${latitude},${longitude}`
|
|
157
|
+
return this
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
removeGeo() {
|
|
161
|
+
this.removePropertiesWithName('GEO')
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const event = new CalendarEvent('abc', new Date())
|
|
166
|
+
event.getPropertyParams('DTEND')?.includes('VALUE=DATE')
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Component } from '../component'
|
|
2
|
+
import { parseDate, toDateString, toDateTimeString } from '../parse'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a VTIMEZONE component, containing time zone definitions.
|
|
6
|
+
*/
|
|
7
|
+
export class TimeZone extends Component {
|
|
8
|
+
constructor(id: string)
|
|
9
|
+
constructor(component: Component)
|
|
10
|
+
constructor(a: string | Component) {
|
|
11
|
+
var component: Component
|
|
12
|
+
if (a instanceof Component) {
|
|
13
|
+
component = a as Component
|
|
14
|
+
} else {
|
|
15
|
+
const tzid = a as string
|
|
16
|
+
component = new Component('VTIMEZONE')
|
|
17
|
+
component.setProperty('TZID', tzid)
|
|
18
|
+
}
|
|
19
|
+
super(component.name, component.properties, component.components)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
id(): string {
|
|
23
|
+
return this.getProperty('TZID')!.value
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setId(value: string): this {
|
|
27
|
+
return this.setProperty('TZID', value)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
lastMod(): Date | undefined {
|
|
31
|
+
const text = this.getProperty('LAST-MOD')
|
|
32
|
+
if (!text) return
|
|
33
|
+
return parseDate(text)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
setLastMod(value: Date): this {
|
|
37
|
+
return this.setProperty('LAST-MOD', value.toISOString())
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
removeLastMod() {
|
|
41
|
+
this.removePropertiesWithName('LAST-MOD')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
url(): string | undefined {
|
|
45
|
+
return this.getProperty('TZURL')?.value
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
setUrl(value: string): this {
|
|
49
|
+
return this.setProperty('TZURL', value)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
removeUrl() {
|
|
53
|
+
this.removePropertiesWithName('TZURL')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Get all time offsets. */
|
|
57
|
+
offsets(): TimeZoneOffset[] {
|
|
58
|
+
const offsets: TimeZoneOffset[] = []
|
|
59
|
+
this.components.forEach(component => {
|
|
60
|
+
if (component.name === 'STANDARD' || component.name === 'DAYLIGHT') {
|
|
61
|
+
offsets.push(new TimeZoneOffset(component))
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
return offsets
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Get standard/winter time offsets. */
|
|
68
|
+
standardOffsets(): TimeZoneOffset[] {
|
|
69
|
+
return this.getComponents('STANDARD').map(c => new TimeZoneOffset(c))
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Get daylight savings time offsets. */
|
|
73
|
+
daylightOffsets(): TimeZoneOffset[] {
|
|
74
|
+
return this.getComponents('DAYLIGHT').map(c => new TimeZoneOffset(c))
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type OffsetType = 'DAYLIGHT' | 'STANDARD'
|
|
79
|
+
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
|
|
80
|
+
export type Offset = `${'-' | '+'}${Digit}${Digit}${Digit}${Digit}`
|
|
81
|
+
/** Represents a STANDARD or DAYLIGHT component, defining a time zone offset. */
|
|
82
|
+
class TimeZoneOffset extends Component {
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @param type If this is a STANDARD or DAYLIGHT component.
|
|
86
|
+
* @param start From when this offset is active.
|
|
87
|
+
* @param offsetFrom The offset that is in use prior to this time zone observance.
|
|
88
|
+
* @param offsetTo The offset that is in use during this time zone observance.
|
|
89
|
+
*/
|
|
90
|
+
constructor(type: OffsetType, start: Date, offsetFrom: Offset, offsetTo: Offset)
|
|
91
|
+
constructor(component: Component)
|
|
92
|
+
constructor(a: OffsetType | Component, b?: Date, c?: Offset, d?: Offset) {
|
|
93
|
+
var component: Component
|
|
94
|
+
if (a instanceof Component) {
|
|
95
|
+
component = a as Component
|
|
96
|
+
} else {
|
|
97
|
+
const name = a as OffsetType
|
|
98
|
+
const start = b as Date
|
|
99
|
+
const offsetFrom = c as Offset
|
|
100
|
+
const offsetTo = d as Offset
|
|
101
|
+
component = new Component(name)
|
|
102
|
+
component.setProperty('DTSTART', toDateTimeString(start))
|
|
103
|
+
component.setProperty('TZOFFSETFROM', offsetFrom)
|
|
104
|
+
component.setProperty('TZOFFSETTO', offsetTo)
|
|
105
|
+
}
|
|
106
|
+
super(component.name, component.properties, component.components)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
start(): Date {
|
|
110
|
+
return parseDate(this.getProperty('DTSTART')!)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
setStart(value: Date, fullDay: boolean = false): this {
|
|
114
|
+
if (fullDay) {
|
|
115
|
+
this.setProperty('DTSTART', toDateString(value))
|
|
116
|
+
this.setPropertyParams('DTSTART', ['VALUE=DATE'])
|
|
117
|
+
} else {
|
|
118
|
+
this.setProperty('DTSTART', toDateTimeString(value))
|
|
119
|
+
}
|
|
120
|
+
return this
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
offsetFrom(): Offset {
|
|
124
|
+
return this.getProperty('TZOFFSETFROM')!.value as Offset
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
setOffsetFrom(value: Offset): this {
|
|
128
|
+
return this.setProperty('TZOFFSETFROM', value)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
offsetTo(): Offset {
|
|
132
|
+
return this.getProperty('TZOFFSETTO')!.value as Offset
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
setOffsetTo(value: Offset): this {
|
|
136
|
+
return this.setProperty('TZOFFSETTO', value)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
comment(): string | undefined {
|
|
140
|
+
return this.getProperty('COMMENT')?.value
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
setComment(value: string): this {
|
|
144
|
+
return this.setProperty('COMMENT', value)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
removeComment() {
|
|
148
|
+
this.removePropertiesWithName('COMMENT')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
timeZoneName(): string | undefined {
|
|
152
|
+
return this.getProperty('TZNAME')?.value
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
setTimeZoneName(value: string): this {
|
|
156
|
+
return this.setProperty('TZNAME', value)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
removeTimeZoneName() {
|
|
160
|
+
this.removePropertiesWithName('TZNAME')
|
|
161
|
+
}
|
|
162
|
+
}
|