icalendar 2.4.1 → 2.8.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.
- checksums.yaml +5 -5
- data/.github/workflows/main.yml +32 -0
- data/.gitignore +1 -0
- data/History.txt +39 -0
- data/{COPYING → LICENSE} +0 -0
- data/README.md +6 -22
- data/icalendar.gemspec +17 -18
- data/lib/icalendar/alarm.rb +1 -0
- data/lib/icalendar/calendar.rb +10 -0
- data/lib/icalendar/component.rb +14 -3
- data/lib/icalendar/event.rb +3 -0
- data/lib/icalendar/has_components.rb +17 -5
- data/lib/icalendar/has_properties.rb +27 -19
- data/lib/icalendar/journal.rb +2 -0
- data/lib/icalendar/marshable.rb +34 -0
- data/lib/icalendar/parser.rb +49 -16
- data/lib/icalendar/timezone.rb +68 -0
- data/lib/icalendar/timezone_store.rb +36 -0
- data/lib/icalendar/todo.rb +4 -1
- data/lib/icalendar/tzinfo.rb +2 -2
- data/lib/icalendar/values/array.rb +1 -2
- data/lib/icalendar/values/date.rb +2 -0
- data/lib/icalendar/values/date_or_date_time.rb +23 -11
- data/lib/icalendar/values/date_time.rb +4 -0
- data/lib/icalendar/values/time_with_zone.rb +23 -6
- data/lib/icalendar/values/uri.rb +2 -2
- data/lib/icalendar/values/utc_offset.rb +10 -2
- data/lib/icalendar/version.rb +1 -1
- data/lib/icalendar.rb +3 -1
- data/spec/alarm_spec.rb +7 -2
- data/spec/calendar_spec.rb +34 -0
- data/spec/event_spec.rb +24 -0
- data/spec/fixtures/bad_wrapping.ics +14 -0
- data/spec/fixtures/custom_component.ics +158 -0
- data/spec/fixtures/single_event.ics +1 -1
- data/spec/fixtures/single_event_bad_organizer.ics +22 -0
- data/spec/fixtures/single_event_organizer_parsed.ics +22 -0
- data/spec/fixtures/tzid_search.ics +31 -0
- data/spec/parser_spec.rb +31 -9
- data/spec/roundtrip_spec.rb +32 -9
- data/spec/timezone_spec.rb +48 -0
- data/spec/tzinfo_spec.rb +1 -1
- data/spec/values/date_or_date_time_spec.rb +10 -0
- metadata +65 -40
- data/.travis.yml +0 -18
data/lib/icalendar/timezone.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ice_cube'
|
2
|
+
|
1
3
|
module Icalendar
|
2
4
|
|
3
5
|
class Timezone < Component
|
@@ -12,10 +14,40 @@ module Icalendar
|
|
12
14
|
optional_property :comment
|
13
15
|
optional_property :rdate, Icalendar::Values::DateTime
|
14
16
|
optional_property :tzname
|
17
|
+
|
18
|
+
transient_variable :@cached_occurrences
|
19
|
+
transient_variable :@occurrences
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def occurrences
|
24
|
+
@occurrences ||= IceCube::Schedule.new(dtstart.to_time) do |s|
|
25
|
+
rrule.each do |rule|
|
26
|
+
s.add_recurrence_rule IceCube::Rule.from_ical(rule.value_ical)
|
27
|
+
end
|
28
|
+
rdate.each do |date|
|
29
|
+
s.add_recurrence_time date.to_time
|
30
|
+
end
|
31
|
+
end.all_occurrences_enumerator
|
32
|
+
end
|
33
|
+
|
34
|
+
def previous_occurrence(from)
|
35
|
+
from = IceCube::TimeUtil.match_zone(from, dtstart.to_time)
|
36
|
+
|
37
|
+
@cached_occurrences ||= []
|
38
|
+
while @cached_occurrences.empty? || @cached_occurrences.last <= from
|
39
|
+
begin
|
40
|
+
@cached_occurrences << occurrences.next
|
41
|
+
rescue StopIteration
|
42
|
+
break
|
43
|
+
end
|
15
44
|
end
|
45
|
+
|
46
|
+
@cached_occurrences.reverse_each.find { |occurrence| occurrence < from }
|
16
47
|
end
|
17
48
|
end
|
18
49
|
class Daylight < Component
|
50
|
+
include Marshable
|
19
51
|
include TzProperties
|
20
52
|
|
21
53
|
def initialize
|
@@ -23,6 +55,7 @@ module Icalendar
|
|
23
55
|
end
|
24
56
|
end
|
25
57
|
class Standard < Component
|
58
|
+
include Marshable
|
26
59
|
include TzProperties
|
27
60
|
|
28
61
|
def initialize
|
@@ -49,5 +82,40 @@ module Icalendar
|
|
49
82
|
standards.all? { |s| s.valid? strict } or return false
|
50
83
|
super
|
51
84
|
end
|
85
|
+
|
86
|
+
def offset_for_local(local)
|
87
|
+
standard = standard_for local
|
88
|
+
daylight = daylight_for local
|
89
|
+
|
90
|
+
if standard.nil? && daylight.nil?
|
91
|
+
"+00:00"
|
92
|
+
elsif daylight.nil?
|
93
|
+
standard.last.tzoffsetto
|
94
|
+
elsif standard.nil?
|
95
|
+
daylight.last.tzoffsetto
|
96
|
+
else
|
97
|
+
sdst = standard.first
|
98
|
+
ddst = daylight.first
|
99
|
+
if sdst > ddst
|
100
|
+
standard.last.tzoffsetto
|
101
|
+
else
|
102
|
+
daylight.last.tzoffsetto
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def standard_for(local)
|
108
|
+
possible = standards.map do |std|
|
109
|
+
[std.previous_occurrence(local.to_time), std]
|
110
|
+
end
|
111
|
+
possible.sort_by(&:first).last
|
112
|
+
end
|
113
|
+
|
114
|
+
def daylight_for(local)
|
115
|
+
possible = daylights.map do |day|
|
116
|
+
[day.previous_occurrence(local.to_time), day]
|
117
|
+
end
|
118
|
+
possible.sort_by(&:first).last
|
119
|
+
end
|
52
120
|
end
|
53
121
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'icalendar/downcased_hash'
|
3
|
+
|
4
|
+
module Icalendar
|
5
|
+
class TimezoneStore < ::SimpleDelegator
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
super DowncasedHash.new({})
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.instance
|
12
|
+
warn "**** DEPRECATION WARNING ****\nTimezoneStore.instance will be removed in 3.0. Please instantiate a TimezoneStore object."
|
13
|
+
@instance ||= new
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.store(timezone)
|
17
|
+
warn "**** DEPRECATION WARNING ****\nTimezoneStore.store will be removed in 3.0. Please use instance methods."
|
18
|
+
instance.store timezone
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.retrieve(tzid)
|
22
|
+
warn "**** DEPRECATION WARNING ****\nTimezoneStore.retrieve will be removed in 3.0. Please use instance methods."
|
23
|
+
instance.retrieve tzid
|
24
|
+
end
|
25
|
+
|
26
|
+
def store(timezone)
|
27
|
+
self[timezone.tzid] = timezone
|
28
|
+
end
|
29
|
+
|
30
|
+
def retrieve(tzid)
|
31
|
+
self[tzid]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/icalendar/todo.rb
CHANGED
@@ -12,6 +12,7 @@ module Icalendar
|
|
12
12
|
mutually_exclusive_properties :due, :duration
|
13
13
|
|
14
14
|
optional_single_property :ip_class
|
15
|
+
optional_single_property :color
|
15
16
|
optional_single_property :completed, Icalendar::Values::DateTime
|
16
17
|
optional_single_property :created, Icalendar::Values::DateTime
|
17
18
|
optional_single_property :description
|
@@ -38,6 +39,8 @@ module Icalendar
|
|
38
39
|
optional_property :related_to
|
39
40
|
optional_property :resources
|
40
41
|
optional_property :rdate, Icalendar::Values::DateTime
|
42
|
+
optional_property :conference, Icalendar::Values::Uri, false, true
|
43
|
+
optional_property :image, Icalendar::Values::Uri, false, true
|
41
44
|
|
42
45
|
component :alarm, false
|
43
46
|
|
@@ -49,4 +52,4 @@ module Icalendar
|
|
49
52
|
|
50
53
|
end
|
51
54
|
|
52
|
-
end
|
55
|
+
end
|
data/lib/icalendar/tzinfo.rb
CHANGED
@@ -61,7 +61,7 @@ module Icalendar
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def rrule
|
64
|
-
start = local_start.to_datetime
|
64
|
+
start = (respond_to?(:local_start_at) ? local_start_at : local_start).to_datetime
|
65
65
|
# this is somewhat of a hack, but seems to work ok
|
66
66
|
# assumes that no timezone transition is in law as "4th X of the month"
|
67
67
|
# but only as 1st X, 2nd X, 3rd X, or Last X
|
@@ -76,7 +76,7 @@ module Icalendar
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def dtstart
|
79
|
-
local_start.to_datetime.strftime '%Y%m%dT%H%M%S'
|
79
|
+
(respond_to?(:local_start_at) ? local_start_at : local_start).to_datetime.strftime '%Y%m%dT%H%M%S'
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
@@ -22,7 +22,7 @@ module Icalendar
|
|
22
22
|
else
|
23
23
|
[klass.new(value, params)]
|
24
24
|
end
|
25
|
-
super mapped
|
25
|
+
super mapped
|
26
26
|
end
|
27
27
|
|
28
28
|
def params_ical
|
@@ -52,7 +52,6 @@ module Icalendar
|
|
52
52
|
def needs_value_type?(default_type)
|
53
53
|
value.first.class != default_type
|
54
54
|
end
|
55
|
-
|
56
55
|
end
|
57
56
|
|
58
57
|
end
|
@@ -2,25 +2,37 @@ module Icalendar
|
|
2
2
|
module Values
|
3
3
|
|
4
4
|
# DateOrDateTime can be used to set an attribute to either a Date or a DateTime value.
|
5
|
-
# It should not be used
|
6
|
-
class DateOrDateTime
|
5
|
+
# It should not be used without also invoking the `call` method.
|
6
|
+
class DateOrDateTime < Value
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
@value = value
|
11
|
-
@params = params
|
8
|
+
def call
|
9
|
+
parsed
|
12
10
|
end
|
13
11
|
|
14
|
-
def
|
12
|
+
def value_ical
|
13
|
+
parsed.value_ical
|
14
|
+
end
|
15
|
+
|
16
|
+
def params_ical
|
17
|
+
parsed.params_ical
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def parsed
|
15
23
|
@parsed ||= begin
|
16
|
-
Icalendar::Values::DateTime.new value,
|
24
|
+
Icalendar::Values::DateTime.new value, ical_params
|
17
25
|
rescue Icalendar::Values::DateTime::FormatError
|
18
|
-
Icalendar::Values::Date.new value,
|
26
|
+
Icalendar::Values::Date.new value, ical_params
|
19
27
|
end
|
20
28
|
end
|
21
29
|
|
22
|
-
def
|
23
|
-
|
30
|
+
def needs_value_type?(default_type)
|
31
|
+
parsed.class != default_type
|
32
|
+
end
|
33
|
+
|
34
|
+
def value_type
|
35
|
+
parsed.class.value_type
|
24
36
|
end
|
25
37
|
|
26
38
|
end
|
@@ -1,9 +1,17 @@
|
|
1
|
+
require 'icalendar/timezone_store'
|
2
|
+
|
1
3
|
begin
|
2
4
|
require 'active_support/time'
|
3
5
|
|
4
6
|
if defined?(ActiveSupport::TimeWithZone)
|
5
7
|
require 'icalendar/values/active_support_time_with_zone_adapter'
|
6
8
|
end
|
9
|
+
rescue NameError
|
10
|
+
# ActiveSupport v7+ needs the base require to be run first before loading
|
11
|
+
# specific parts of it.
|
12
|
+
# https://guides.rubyonrails.org/active_support_core_extensions.html#stand-alone-active-support
|
13
|
+
require 'active_support'
|
14
|
+
retry
|
7
15
|
rescue LoadError
|
8
16
|
# tis ok, just a bit less fancy
|
9
17
|
end
|
@@ -16,15 +24,24 @@ module Icalendar
|
|
16
24
|
def initialize(value, params = {})
|
17
25
|
params = Icalendar::DowncasedHash(params)
|
18
26
|
@tz_utc = params['tzid'] == 'UTC'
|
27
|
+
x_tz_info = params.delete 'x-tz-info'
|
19
28
|
|
20
|
-
|
29
|
+
offset_value = unless params['tzid'].nil?
|
21
30
|
tzid = params['tzid'].is_a?(::Array) ? params['tzid'].first : params['tzid']
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
31
|
+
if defined?(ActiveSupport::TimeZone) &&
|
32
|
+
defined?(ActiveSupportTimeWithZoneAdapter) &&
|
33
|
+
(tz = ActiveSupport::TimeZone[tzid])
|
34
|
+
ActiveSupportTimeWithZoneAdapter.new(nil, tz, value)
|
35
|
+
elsif !x_tz_info.nil?
|
36
|
+
offset = x_tz_info.offset_for_local(value).to_s
|
37
|
+
if value.respond_to?(:change)
|
38
|
+
value.change offset: offset
|
39
|
+
else
|
40
|
+
::Time.new value.year, value.month, value.day, value.hour, value.min, value.sec, offset
|
41
|
+
end
|
42
|
+
end
|
27
43
|
end
|
44
|
+
super((offset_value || value), params)
|
28
45
|
end
|
29
46
|
|
30
47
|
def params_ical
|
data/lib/icalendar/values/uri.rb
CHANGED
@@ -6,7 +6,7 @@ module Icalendar
|
|
6
6
|
class Uri < Value
|
7
7
|
|
8
8
|
def initialize(value, params = {})
|
9
|
-
parsed = URI.parse
|
9
|
+
parsed = URI.parse(value) rescue value
|
10
10
|
super parsed, params
|
11
11
|
end
|
12
12
|
|
@@ -16,4 +16,4 @@ module Icalendar
|
|
16
16
|
end
|
17
17
|
|
18
18
|
end
|
19
|
-
end
|
19
|
+
end
|
@@ -21,6 +21,15 @@ module Icalendar
|
|
21
21
|
"#{behind? ? '-' : '+'}#{'%02d' % hours}#{'%02d' % minutes}#{'%02d' % seconds if seconds > 0}"
|
22
22
|
end
|
23
23
|
|
24
|
+
def to_s
|
25
|
+
str = "#{behind? ? '-' : '+'}#{'%02d' % hours}:#{'%02d' % minutes}"
|
26
|
+
if seconds > 0
|
27
|
+
"#{str}:#{'%02d' % seconds}"
|
28
|
+
else
|
29
|
+
str
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
24
33
|
private
|
25
34
|
|
26
35
|
def zero_offset?
|
@@ -28,8 +37,7 @@ module Icalendar
|
|
28
37
|
end
|
29
38
|
|
30
39
|
def parse_fields(value)
|
31
|
-
value.gsub
|
32
|
-
md = /\A(?<behind>[+-])(?<hours>\d{2})(?<minutes>\d{2})(?<seconds>\d{2})?\z/.match value
|
40
|
+
md = /\A(?<behind>[+-])(?<hours>\d{2})(?<minutes>\d{2})(?<seconds>\d{2})?\z/.match value.gsub(/\s+/, '')
|
33
41
|
{
|
34
42
|
behind: (md[:behind] == '-'),
|
35
43
|
hours: md[:hours].to_i,
|
data/lib/icalendar/version.rb
CHANGED
data/lib/icalendar.rb
CHANGED
@@ -13,7 +13,7 @@ module Icalendar
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.parse(source, single = false)
|
16
|
-
warn "**** DEPRECATION WARNING ****\nIcalendar.parse will be removed. Please switch to Icalendar::Calendar.parse."
|
16
|
+
warn "**** DEPRECATION WARNING ****\nIcalendar.parse will be removed in 3.0. Please switch to Icalendar::Calendar.parse."
|
17
17
|
calendars = Parser.new(source).parse
|
18
18
|
single ? calendars.first : calendars
|
19
19
|
end
|
@@ -22,6 +22,7 @@ end
|
|
22
22
|
|
23
23
|
require 'icalendar/has_properties'
|
24
24
|
require 'icalendar/has_components'
|
25
|
+
require 'icalendar/marshable'
|
25
26
|
require 'icalendar/component'
|
26
27
|
require 'icalendar/value'
|
27
28
|
require 'icalendar/alarm'
|
@@ -32,3 +33,4 @@ require 'icalendar/freebusy'
|
|
32
33
|
require 'icalendar/timezone'
|
33
34
|
require 'icalendar/calendar'
|
34
35
|
require 'icalendar/parser'
|
36
|
+
require 'icalendar/version'
|
data/spec/alarm_spec.rb
CHANGED
@@ -6,14 +6,15 @@ describe Icalendar::Alarm do
|
|
6
6
|
describe '#valid?' do
|
7
7
|
subject do
|
8
8
|
described_class.new.tap do |a|
|
9
|
-
a.action = 'AUDIO'
|
10
9
|
a.trigger = Icalendar::Values::DateTime.new(Time.now.utc)
|
11
10
|
end
|
12
11
|
end
|
13
12
|
context 'neither duration or repeat is set' do
|
13
|
+
before(:each) { subject.action = 'AUDIO' }
|
14
14
|
it { should be_valid }
|
15
15
|
end
|
16
16
|
context 'both duration and repeat are set' do
|
17
|
+
before(:each) { subject.action = 'AUDIO' }
|
17
18
|
before(:each) do
|
18
19
|
subject.duration = 'PT15M'
|
19
20
|
subject.repeat = 4
|
@@ -29,8 +30,12 @@ describe Icalendar::Alarm do
|
|
29
30
|
it { should_not be_valid }
|
30
31
|
end
|
31
32
|
|
33
|
+
context 'DISPLAY is set as default action' do
|
34
|
+
it 'must be DISPLAY' do
|
35
|
+
expect(subject.action).to eq 'DISPLAY'
|
36
|
+
end
|
37
|
+
end
|
32
38
|
context 'display action' do
|
33
|
-
before(:each) { subject.action = 'DISPLAY' }
|
34
39
|
it 'requires description' do
|
35
40
|
expect(subject).to_not be_valid
|
36
41
|
subject.description = 'Display Text'
|
data/spec/calendar_spec.rb
CHANGED
@@ -2,6 +2,12 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Icalendar::Calendar do
|
4
4
|
|
5
|
+
context 'marshalling' do
|
6
|
+
it 'can be de/serialized' do
|
7
|
+
Marshal.load(Marshal.dump(subject))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
5
11
|
context 'values' do
|
6
12
|
let(:property) { 'my-value' }
|
7
13
|
|
@@ -117,6 +123,16 @@ describe Icalendar::Calendar do
|
|
117
123
|
describe '#to_ical' do
|
118
124
|
before(:each) do
|
119
125
|
Timecop.freeze DateTime.new(2013, 12, 26, 5, 0, 0, '+0000')
|
126
|
+
subject.ip_name = 'Company Vacation Days'
|
127
|
+
subject.description = 'The description'
|
128
|
+
subject.last_modified = "20140101T000000Z"
|
129
|
+
subject.url = 'https://example.com'
|
130
|
+
subject.color = 'red'
|
131
|
+
subject.image = 'https://example.com/image.png'
|
132
|
+
subject.uid = '5FC53010-1267-4F8E-BC28-1D7AE55A7C99'
|
133
|
+
subject.categories = 'MEETING'
|
134
|
+
subject.refresh_interval = 'P1W'
|
135
|
+
subject.source = 'https://example.com/holidays.ics'
|
120
136
|
subject.event do |e|
|
121
137
|
e.summary = 'An event'
|
122
138
|
e.dtstart = "20140101T000000Z"
|
@@ -139,6 +155,15 @@ BEGIN:VCALENDAR
|
|
139
155
|
VERSION:2.0
|
140
156
|
PRODID:icalendar-ruby
|
141
157
|
CALSCALE:GREGORIAN
|
158
|
+
LAST-MODIFIED;VALUE=DATE-TIME:20140101T000000Z
|
159
|
+
URL;VALUE=URI:https://example.com
|
160
|
+
REFRESH-INTERVAL;VALUE=DURATION:P1W
|
161
|
+
SOURCE;VALUE=URI:https://example.com/holidays.ics
|
162
|
+
COLOR:red
|
163
|
+
NAME:Company Vacation Days
|
164
|
+
DESCRIPTION:The description
|
165
|
+
CATEGORIES:MEETING
|
166
|
+
IMAGE;VALUE=URI:https://example.com/image.png
|
142
167
|
BEGIN:VEVENT
|
143
168
|
DTSTAMP:20131226T050000Z
|
144
169
|
DTSTART:20140101T000000Z
|
@@ -164,4 +189,13 @@ END:VCALENDAR
|
|
164
189
|
expect(subject.ip_method).to eq 'PUBLISH'
|
165
190
|
end
|
166
191
|
end
|
192
|
+
|
193
|
+
describe '.parse' do
|
194
|
+
let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', 'bad_wrapping.ics') }
|
195
|
+
|
196
|
+
it 'correctly parses a bad file' do
|
197
|
+
actual = described_class.parse(source)
|
198
|
+
expect(actual[0]).to be_a(described_class)
|
199
|
+
end
|
200
|
+
end
|
167
201
|
end
|
data/spec/event_spec.rb
CHANGED
@@ -96,6 +96,30 @@ describe Icalendar::Event do
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
+
describe "#append_custom_property" do
|
100
|
+
context "with custom property" do
|
101
|
+
it "appends to the custom properties hash" do
|
102
|
+
subject.append_custom_property "x_my_property", "test value"
|
103
|
+
expect(subject.custom_properties).to eq({"x_my_property" => ["test value"]})
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "with a defined property" do
|
108
|
+
it "sets the proper setter" do
|
109
|
+
subject.append_custom_property "summary", "event"
|
110
|
+
expect(subject.summary).to eq "event"
|
111
|
+
expect(subject.custom_properties).to eq({})
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#custom_property" do
|
117
|
+
it "returns a default for missing properties" do
|
118
|
+
expect(subject.x_doesnt_exist).to eq([])
|
119
|
+
expect(subject.custom_property "x_doesnt_exist").to eq([])
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
99
123
|
describe '.parse' do
|
100
124
|
let(:source) { File.read File.join(File.dirname(__FILE__), 'fixtures', fn) }
|
101
125
|
let(:fn) { 'event.ics' }
|
@@ -0,0 +1,14 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
VERSION:2.0
|
3
|
+
PRODID:manual
|
4
|
+
CALSCALE:GREGORIAN
|
5
|
+
BEGIN:VEVENT
|
6
|
+
DTSTAMP:20200902T223352Z
|
7
|
+
UID:6e7d7fe5-6735-4cdd-bfe4-761dfcecd7a7
|
8
|
+
DTSTART;VALUE=DATE:20200902
|
9
|
+
DTEND;VALUE=DATE:20200903
|
10
|
+
DESCRIPTION:Event description that puts a UTF-8 multi-octet sequence right�
|
11
|
+
�here.
|
12
|
+
SUMMARY:UTF-8 multi-octet sequence test
|
13
|
+
END:VEVENT
|
14
|
+
END:VCALENDAR
|
@@ -0,0 +1,158 @@
|
|
1
|
+
BEGIN:VCALENDAR
|
2
|
+
PRODID:-//Atlassian Confluence//Calendar Plugin 1.0//EN
|
3
|
+
VERSION:2.0
|
4
|
+
CALSCALE:GREGORIAN
|
5
|
+
X-WR-CALNAME:Grimsell testkalender
|
6
|
+
X-WR-CALDESC:
|
7
|
+
X-WR-TIMEZONE:Europe/Stockholm
|
8
|
+
X-MIGRATED-FOR-USER-KEY:true
|
9
|
+
METHOD:PUBLISH
|
10
|
+
BEGIN:VTIMEZONE
|
11
|
+
TZID:Europe/Stockholm
|
12
|
+
TZURL:http://tzurl.org/zoneinfo/Europe/Stockholm
|
13
|
+
SEQUENCE:9206
|
14
|
+
BEGIN:DAYLIGHT
|
15
|
+
TZOFFSETFROM:+0100
|
16
|
+
TZOFFSETTO:+0200
|
17
|
+
TZNAME:CEST
|
18
|
+
DTSTART:19810329T020000
|
19
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
20
|
+
END:DAYLIGHT
|
21
|
+
BEGIN:STANDARD
|
22
|
+
TZOFFSETFROM:+0200
|
23
|
+
TZOFFSETTO:+0100
|
24
|
+
TZNAME:CET
|
25
|
+
DTSTART:19961027T030000
|
26
|
+
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
27
|
+
END:STANDARD
|
28
|
+
BEGIN:STANDARD
|
29
|
+
TZOFFSETFROM:+011212
|
30
|
+
TZOFFSETTO:+010014
|
31
|
+
TZNAME:SET
|
32
|
+
DTSTART:18790101T000000
|
33
|
+
RDATE:18790101T000000
|
34
|
+
END:STANDARD
|
35
|
+
BEGIN:STANDARD
|
36
|
+
TZOFFSETFROM:+010014
|
37
|
+
TZOFFSETTO:+0100
|
38
|
+
TZNAME:CET
|
39
|
+
DTSTART:19000101T000000
|
40
|
+
RDATE:19000101T000000
|
41
|
+
END:STANDARD
|
42
|
+
BEGIN:DAYLIGHT
|
43
|
+
TZOFFSETFROM:+0100
|
44
|
+
TZOFFSETTO:+0200
|
45
|
+
TZNAME:CEST
|
46
|
+
DTSTART:19160514T230000
|
47
|
+
RDATE:19160514T230000
|
48
|
+
RDATE:19800406T020000
|
49
|
+
END:DAYLIGHT
|
50
|
+
BEGIN:STANDARD
|
51
|
+
TZOFFSETFROM:+0200
|
52
|
+
TZOFFSETTO:+0100
|
53
|
+
TZNAME:CET
|
54
|
+
DTSTART:19161001T010000
|
55
|
+
RDATE:19161001T010000
|
56
|
+
RDATE:19800928T030000
|
57
|
+
RDATE:19810927T030000
|
58
|
+
RDATE:19820926T030000
|
59
|
+
RDATE:19830925T030000
|
60
|
+
RDATE:19840930T030000
|
61
|
+
RDATE:19850929T030000
|
62
|
+
RDATE:19860928T030000
|
63
|
+
RDATE:19870927T030000
|
64
|
+
RDATE:19880925T030000
|
65
|
+
RDATE:19890924T030000
|
66
|
+
RDATE:19900930T030000
|
67
|
+
RDATE:19910929T030000
|
68
|
+
RDATE:19920927T030000
|
69
|
+
RDATE:19930926T030000
|
70
|
+
RDATE:19940925T030000
|
71
|
+
RDATE:19950924T030000
|
72
|
+
END:STANDARD
|
73
|
+
BEGIN:STANDARD
|
74
|
+
TZOFFSETFROM:+0100
|
75
|
+
TZOFFSETTO:+0100
|
76
|
+
TZNAME:CET
|
77
|
+
DTSTART:19800101T000000
|
78
|
+
RDATE:19800101T000000
|
79
|
+
END:STANDARD
|
80
|
+
END:VTIMEZONE
|
81
|
+
BEGIN:VEVENT
|
82
|
+
DTSTAMP:20151203T104347Z
|
83
|
+
SUMMARY:Kaffepaus
|
84
|
+
UID:20151203T101856Z-1543254179@intranet.idainfront.se
|
85
|
+
DESCRIPTION:
|
86
|
+
ORGANIZER;X-CONFLUENCE-USER-KEY=ff80818141e4d3b60141e4d4b75700a2;CN=Magnu
|
87
|
+
s Grimsell;CUTYPE=INDIVIDUAL:mailto:magnus.grimsell@idainfront.se
|
88
|
+
CREATED:20151203T101856Z
|
89
|
+
LAST-MODIFIED:20151203T101856Z
|
90
|
+
SEQUENCE:1
|
91
|
+
X-CONFLUENCE-SUBCALENDAR-TYPE:other
|
92
|
+
TRANSP:OPAQUE
|
93
|
+
STATUS:CONFIRMED
|
94
|
+
DTSTART:20151203T090000Z
|
95
|
+
DTEND:20151203T091500Z
|
96
|
+
END:VEVENT
|
97
|
+
BEGIN:X-EVENT-SERIES
|
98
|
+
SUMMARY:iipax - Sprints
|
99
|
+
DESCRIPTION:
|
100
|
+
X-CONFLUENCE-SUBCALENDAR-TYPE:jira-agile-sprint
|
101
|
+
URL:jira://8112ef9a-343e-30e6-b469-4f993bf0371d?projectKey=II&dateFieldNa
|
102
|
+
me=sprint
|
103
|
+
END:X-EVENT-SERIES
|
104
|
+
BEGIN:VEVENT
|
105
|
+
DTSTAMP:20151203T104348Z
|
106
|
+
DTSTART;TZID=Europe/Stockholm:20130115T170800
|
107
|
+
DTEND;TZID=Europe/Stockholm:20130204T170800
|
108
|
+
UID:3cb4df4b-eb18-43fd-934a-16c15acfb4b1-3@jira.idainfront.se
|
109
|
+
X-GREENHOPPER-SPRINT-CLOSED:false
|
110
|
+
X-GREENHOPPER-SPRINT-EDITABLE:true
|
111
|
+
X-JIRA-PROJECT;X-JIRA-PROJECT-KEY=II:iipax
|
112
|
+
X-GREENHOPPER-SPRINT-VIEWBOARDS-URL:https://jira.idainfront.se/secure/GHG
|
113
|
+
oToBoard.jspa?sprintId=3
|
114
|
+
SEQUENCE:0
|
115
|
+
X-CONFLUENCE-SUBCALENDAR-TYPE:jira-agile-sprint
|
116
|
+
TRANSP:OPAQUE
|
117
|
+
STATUS:CONFIRMED
|
118
|
+
DESCRIPTION:https://jira.idainfront.se/secure/GHGoToBoard.jspa?sprintId=3
|
119
|
+
|
120
|
+
SUMMARY:iipax - Callisto-6
|
121
|
+
END:VEVENT
|
122
|
+
BEGIN:VEVENT
|
123
|
+
DTSTAMP:20151203T104348Z
|
124
|
+
DTSTART;TZID=Europe/Stockholm:20130219T080000
|
125
|
+
DTEND;TZID=Europe/Stockholm:20130319T095900
|
126
|
+
UID:3cb4df4b-eb18-43fd-934a-16c15acfb4b1-5@jira.idainfront.se
|
127
|
+
X-GREENHOPPER-SPRINT-CLOSED:false
|
128
|
+
X-GREENHOPPER-SPRINT-EDITABLE:true
|
129
|
+
X-JIRA-PROJECT;X-JIRA-PROJECT-KEY=II:iipax
|
130
|
+
X-GREENHOPPER-SPRINT-VIEWBOARDS-URL:https://jira.idainfront.se/secure/GHG
|
131
|
+
oToBoard.jspa?sprintId=5
|
132
|
+
SEQUENCE:0
|
133
|
+
X-CONFLUENCE-SUBCALENDAR-TYPE:jira-agile-sprint
|
134
|
+
TRANSP:OPAQUE
|
135
|
+
STATUS:CONFIRMED
|
136
|
+
DESCRIPTION:https://jira.idainfront.se/secure/GHGoToBoard.jspa?sprintId=5
|
137
|
+
|
138
|
+
SUMMARY:iipax - Bulbasaur
|
139
|
+
END:VEVENT
|
140
|
+
BEGIN:VEVENT
|
141
|
+
DTSTAMP:20151203T104348Z
|
142
|
+
DTSTART;TZID=Europe/Stockholm:20130326T105900
|
143
|
+
DTEND;TZID=Europe/Stockholm:20130419T105900
|
144
|
+
UID:3cb4df4b-eb18-43fd-934a-16c15acfb4b1-7@jira.idainfront.se
|
145
|
+
X-GREENHOPPER-SPRINT-CLOSED:true
|
146
|
+
X-GREENHOPPER-SPRINT-EDITABLE:true
|
147
|
+
X-JIRA-PROJECT;X-JIRA-PROJECT-KEY=II:iipax
|
148
|
+
X-GREENHOPPER-SPRINT-VIEWBOARDS-URL:https://jira.idainfront.se/secure/GHG
|
149
|
+
oToBoard.jspa?sprintId=7
|
150
|
+
SEQUENCE:0
|
151
|
+
X-CONFLUENCE-SUBCALENDAR-TYPE:jira-agile-sprint
|
152
|
+
TRANSP:OPAQUE
|
153
|
+
STATUS:CONFIRMED
|
154
|
+
DESCRIPTION:https://jira.idainfront.se/secure/GHGoToBoard.jspa?sprintId=7
|
155
|
+
|
156
|
+
SUMMARY:iipax - Ivysaur
|
157
|
+
END:VEVENT
|
158
|
+
END:VCALENDAR
|
@@ -9,7 +9,7 @@ DTSTART;TZID=US-Mountain:20050120T170000
|
|
9
9
|
DTEND;TZID=US-Mountain:20050120T184500
|
10
10
|
CLASS:PRIVATE
|
11
11
|
GEO:37.386013;-122.0829322
|
12
|
-
ORGANIZER:mailto:joebob@random.net
|
12
|
+
ORGANIZER;CN="Joe Bob: Magician":mailto:joebob@random.net
|
13
13
|
PRIORITY:2
|
14
14
|
SUMMARY:This is a really long summary to test the method of unfolding lines
|
15
15
|
\, so I'm just going to make it a whole bunch of lines. With a twist: a "
|