Skip to content

Commit d4423a5

Browse files
committed
Added support for parsing VEVENT from iCal format
1 parent 10ae8dc commit d4423a5

File tree

3 files changed

+86
-17
lines changed

3 files changed

+86
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88
### Added
9+
- Added support for parsing VEVENT from iCal format (#465)
910
- Indonesian translations. ([#505](https://github.com/seejohnrun/ice_cube/pull/505)) by [@achmiral](https://github.com/achmiral)
1011

1112
### Changed

lib/ice_cube/parsers/ical_parser.rb

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,57 @@
11
module IceCube
22
class IcalParser
3-
def self.schedule_from_ical(ical_string, options = {})
3+
def self.schedule_from_ical(ical_string, _options = {})
44
data = {}
5+
current_state = :parsing_calendar
56
ical_string.each_line do |line|
67
(property, value) = line.split(":")
78
(property, _tzid) = property.split(";")
8-
case property
9-
when "DTSTART"
10-
data[:start_time] = TimeUtil.deserialize_time(value)
11-
when "DTEND"
12-
data[:end_time] = TimeUtil.deserialize_time(value)
13-
when "RDATE"
14-
data[:rtimes] ||= []
15-
data[:rtimes] += value.split(",").map { |v| TimeUtil.deserialize_time(v) }
16-
when "EXDATE"
17-
data[:extimes] ||= []
18-
data[:extimes] += value.split(",").map { |v| TimeUtil.deserialize_time(v) }
19-
when "DURATION"
20-
data[:duration] # FIXME
21-
when "RRULE"
22-
data[:rrules] ||= []
23-
data[:rrules] += [rule_from_ical(value)]
9+
10+
if current_state == :parsing_calendar
11+
current_state = parse_calendar(data, property, value)
12+
elsif current_state == :parsing_event
13+
current_state = parse_event(data, property, value)
2414
end
2515
end
2616
Schedule.from_hash data
2717
end
2818

19+
def self.parse_calendar(data, property, value)
20+
case property
21+
when "DTSTART"
22+
data[:start_time] = TimeUtil.deserialize_time(value)
23+
when "DTEND"
24+
data[:end_time] = TimeUtil.deserialize_time(value)
25+
when "RDATE"
26+
data[:rtimes] ||= []
27+
data[:rtimes] += value.split(",").map { |v| TimeUtil.deserialize_time(v) }
28+
when "EXDATE"
29+
data[:extimes] ||= []
30+
data[:extimes] += value.split(",").map { |v| TimeUtil.deserialize_time(v) }
31+
when "DURATION"
32+
data[:duration] # FIXME
33+
when "RRULE"
34+
data[:rrules] ||= []
35+
data[:rrules] += [rule_from_ical(value)]
36+
when "BEGIN"
37+
return :parsing_event if value.chomp == "VEVENT"
38+
end
39+
40+
:parsing_calendar
41+
end
42+
43+
def self.parse_event(data, property, value)
44+
case property
45+
when "DTSTART"
46+
data[:rtimes] ||= []
47+
data[:rtimes] += value.split(",").map { |v| TimeUtil.deserialize_time(v) }
48+
when "END"
49+
return :parsing_calendar if value.chomp == "VEVENT"
50+
end
51+
52+
:parsing_event
53+
end
54+
2955
def self.rule_from_ical(ical)
3056
raise ArgumentError, "empty ical rule" if ical.nil?
3157

spec/examples/from_ical_spec.rb

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

118+
ical_string_with_events = <<-ICAL.gsub(/^\s*/, "")
119+
BEGIN:VEVENT
120+
DTSTART;VALUE=DATE:20120406
121+
DTEND;VALUE=DATE:20120407
122+
END:VEVENT
123+
BEGIN:VEVENT
124+
DTSTART;VALUE=DATE:20120409
125+
DTEND;VALUE=DATE:20120410
126+
END:VEVENT
127+
ICAL
128+
118129
def sorted_ical(ical)
119130
ical.split("\n").sort.map { |field|
120131
k, v = field.split(":")
@@ -378,13 +389,44 @@ def sorted_ical(ical)
378389
end
379390
end
380391

392+
describe "one-off dates" do
393+
it "handles a specific day as a recurrence time" do
394+
start_time = Time.now
395+
396+
schedule = IceCube::Schedule.new(start_time)
397+
schedule.add_recurrence_time(start_time)
398+
399+
ical = schedule.to_ical
400+
expect(sorted_ical(IceCube::Schedule.from_ical(ical).to_ical)).to eq(sorted_ical(ical))
401+
end
402+
403+
it "converts events to recurrence times" do
404+
start_time = Time.utc(2019, 1, 16)
405+
406+
schedule = IceCube::Schedule.new(start_time)
407+
schedule.add_recurrence_time(start_time)
408+
409+
event_ical = "DTSTART:20190116T000000Z\nBEGIN:VEVENT\nDTSTART:20190116T000000Z\nDTEND:20190117T000000Z\nEND:VEVENT"
410+
411+
ical = schedule.to_ical
412+
expect(sorted_ical(IceCube::Schedule.from_ical(event_ical).to_ical)).to eq(sorted_ical(ical))
413+
end
414+
end
415+
381416
describe "multiple rules" do
382417
it "handles multiple recurrence rules" do
383418
schedule = IceCube::Schedule.from_ical ical_string_with_multiple_rules
384419
expect(schedule.recurrence_rules.count).to eq(2)
385420
end
386421
end
387422

423+
describe "events" do
424+
it "converts each event into it's own recurrence time" do
425+
schedule = IceCube::Schedule.from_ical ical_string_with_events
426+
expect(schedule.recurrence_times.count).to eq(2)
427+
end
428+
end
429+
388430
describe "invalid ical data" do
389431
shared_examples_for("an invalid ical string") do
390432
it do

0 commit comments

Comments
 (0)