Skip to content

Commit 318153e

Browse files
committed
Added support for parsing VEVENT from iCal format
1 parent 49534bf commit 318153e

File tree

3 files changed

+87
-12
lines changed

3 files changed

+87
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Current
4+
5+
* [FEATURE] Added support for parsing VEVENT from iCal format (#465)
6+
37
## 0.16.3 / 2018-07-23
48

59
* [FEATURE] Added support for parsing RDATE from iCal format

lib/ice_cube/parsers/ical_parser.rb

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,57 @@ module IceCube
22
class IcalParser
33
def self.schedule_from_ical(ical_string, options = {})
44
data = {}
5+
parser = :parse_line
56
ical_string.each_line do |line|
67
(property, value) = line.split(':')
78
(property, tzid) = property.split(';')
8-
case property
9+
10+
parser, attr, occurrences = *send(parser, property, value)
11+
12+
case attr
13+
when :start_time,
14+
:end_time
15+
data[attr] = occurrences
16+
when :rtimes,
17+
:rrules,
18+
:extimes
19+
data[attr] ||= []
20+
data[attr] += occurrences
21+
end
22+
end
23+
Schedule.from_hash data
24+
end
25+
26+
def self.parse_line(property, value)
27+
result = case property
928
when 'DTSTART'
10-
data[:start_time] = TimeUtil.deserialize_time(value)
29+
[:parse_line, :start_time, TimeUtil.deserialize_time(value)]
1130
when 'DTEND'
12-
data[:end_time] = TimeUtil.deserialize_time(value)
31+
[:parse_line, :end_time, TimeUtil.deserialize_time(value)]
1332
when 'RDATE'
14-
data[:rtimes] ||= []
15-
data[:rtimes] += value.split(',').map { |v| TimeUtil.deserialize_time(v) }
33+
[:parse_line, :rtimes, value.split(',').map { |v| TimeUtil.deserialize_time(v) }]
1634
when 'EXDATE'
17-
data[:extimes] ||= []
18-
data[:extimes] += value.split(',').map { |v| TimeUtil.deserialize_time(v) }
35+
[:parse_line, :extimes, value.split(',').map { |v| TimeUtil.deserialize_time(v) }]
1936
when 'DURATION'
20-
data[:duration] # FIXME
37+
nil # FIXME
2138
when 'RRULE'
22-
data[:rrules] ||= []
23-
data[:rrules] += [rule_from_ical(value)]
39+
[:parse_line, :rrules, [rule_from_ical(value)]]
40+
when 'BEGIN'
41+
[:parse_vevent_line] if value.chomp == 'VEVENT'
2442
end
25-
end
26-
Schedule.from_hash data
43+
44+
result || [:parse_line]
45+
end
46+
47+
def self.parse_vevent_line(property, value)
48+
result = case property
49+
when 'DTSTART'
50+
[:parse_vevent_line, :rtimes, [TimeUtil.deserialize_time(value)]]
51+
when 'END'
52+
[:parse_line] if value.chomp == 'VEVENT'
53+
end
54+
55+
result || [:parse_vevent_line]
2756
end
2857

2958
def self.rule_from_ical(ical)

spec/examples/from_ical_spec.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,17 @@ module IceCube
119119
RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU;BYDAY=FR
120120
ICAL
121121

122+
ical_string_with_events = <<-ICAL.gsub(/^\s*/, '' )
123+
BEGIN:VEVENT
124+
DTSTART;VALUE=DATE:20120406
125+
DTEND;VALUE=DATE:20120407
126+
END:VEVENT
127+
BEGIN:VEVENT
128+
DTSTART;VALUE=DATE:20120409
129+
DTEND;VALUE=DATE:20120410
130+
END:VEVENT
131+
ICAL
132+
122133
def sorted_ical(ical)
123134
ical.split(/\n/).sort.map { |field|
124135
k, v = field.split(':')
@@ -383,13 +394,44 @@ def sorted_ical(ical)
383394
end
384395
end
385396

397+
describe 'one-off dates' do
398+
it 'handles a specific day as a recurrence time' do
399+
start_time = Time.now
400+
401+
schedule = IceCube::Schedule.new(start_time)
402+
schedule.add_recurrence_time(start_time)
403+
404+
ical = schedule.to_ical
405+
expect(sorted_ical(IceCube::Schedule.from_ical(ical).to_ical)).to eq(sorted_ical(ical))
406+
end
407+
408+
it 'converts events to recurrence times' do
409+
start_time = Time.utc(2019, 1, 16)
410+
411+
schedule = IceCube::Schedule.new(start_time)
412+
schedule.add_recurrence_time(start_time)
413+
414+
event_ical = "DTSTART:20190116T000000Z\nBEGIN:VEVENT\nDTSTART:20190116T000000Z\nDTEND:20190117T000000Z\nEND:VEVENT"
415+
416+
ical = schedule.to_ical
417+
expect(sorted_ical(IceCube::Schedule.from_ical(event_ical).to_ical)).to eq(sorted_ical(ical))
418+
end
419+
end
420+
386421
describe 'multiple rules' do
387422
it 'handles multiple recurrence rules' do
388423
schedule = IceCube::Schedule.from_ical ical_string_with_multiple_rules
389424
expect(schedule.recurrence_rules.count).to eq(2)
390425
end
391426
end
392427

428+
describe 'events' do
429+
it "converts each event into it's own recurrence time" do
430+
schedule = IceCube::Schedule.from_ical ical_string_with_events
431+
expect(schedule.recurrence_times.count).to eq(2)
432+
end
433+
end
434+
393435
describe 'invalid ical data' do
394436
shared_examples_for('an invalid ical string') do
395437
it do

0 commit comments

Comments
 (0)