Skip to content

Commit 804e132

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

File tree

3 files changed

+84
-16
lines changed

3 files changed

+84
-16
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: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,55 @@ module IceCube
22
class IcalParser
33
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+
case current_state
11+
when :parsing_calendar then current_state = parse_calendar(data, property, value)
12+
when :parsing_event then current_state = parse_event(data, property, value)
2413
end
2514
end
2615
Schedule.from_hash data
2716
end
2817

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

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)