Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Time zone parsing support from iCal #555

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Indonesian translations. ([#505](https://github.com/seejohnrun/ice_cube/pull/505)) by [@achmiral](https://github.com/achmiral)
- The `IceCube::IcalParser` finds the time zones matching an iCal schedule's date/time strings, accomodating for times with daylight savings ([#555](https://github.com/ice-cube-ruby/ice_cube/pull/555)) by [@jankeesvw](https://github.com/jankeesvw) and [@epologee](https://github.com/epologee)

### Changed
- Removed use of `delegate` method added in [66f1d797](https://github.com/ice-cube-ruby/ice_cube/commit/66f1d797092734563bfabd2132c024c7d087f683) , reverting to previous implementation. ([#522](https://github.com/ice-cube-ruby/ice_cube/pull/522)) by [@pacso](https://github.com/pacso)
Expand Down
30 changes: 27 additions & 3 deletions lib/ice_cube/parsers/ical_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,27 @@ def self.schedule_from_ical(ical_string, options = {})
data = {}
ical_string.each_line do |line|
(property, value) = line.split(":")
(property, _tzid) = property.split(";")
(property, tzid) = property.split(";")
zone = find_zone(tzid, value) if tzid
case property
when "DTSTART"
value = { time: value, zone: zone } if zone
data[:start_time] = TimeUtil.deserialize_time(value)
when "DTEND"
value = { time: value, zone: zone } if zone
data[:end_time] = TimeUtil.deserialize_time(value)
when "RDATE"
data[:rtimes] ||= []
data[:rtimes] += value.split(",").map { |v| TimeUtil.deserialize_time(v) }
data[:rtimes] += value.split(",").map do |v|
v = {time: v, zone: zone} if zone
TimeUtil.deserialize_time(v)
end
when "EXDATE"
data[:extimes] ||= []
data[:extimes] += value.split(",").map { |v| TimeUtil.deserialize_time(v) }
data[:extimes] += value.split(",").map do |v|
v = {time: v, zone: zone} if zone
TimeUtil.deserialize_time(v)
end
when "DURATION"
data[:duration] # FIXME
when "RRULE"
Expand Down Expand Up @@ -83,5 +92,20 @@ def self.rule_from_ical(ical)

Rule.from_hash(params)
end

private_class_method def self.find_zone(tzid, time_string)
(_, zone) = tzid&.split("=")
begin
Time.find_zone!(zone) if zone
rescue ArgumentError
(rails_zone, _tzinfo_id) = ActiveSupport::TimeZone::MAPPING.find do |(k, _)|
time = Time.parse(time_string)

Time.find_zone!(k).local(time.year, time.month, time.day, time.hour, time.min).strftime("%Z") == zone
end

Time.find_zone(rails_zone)
end
end
end
end
63 changes: 63 additions & 0 deletions spec/examples/from_ical_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,69 @@ def sorted_ical(ical)
end
end

describe "time zone support" do
it "parses start time with the correct time zone" do
schedule = IceCube::Schedule.from_ical ical_string_with_multiple_rules

expect(schedule.start_time).to eq Time.find_zone!("America/Chicago").local(2015, 10, 5, 19, 55, 41)
end

it "parses time zones correctly" do
schedule = IceCube::Schedule.from_ical ical_string_with_multiple_exdates_and_rdates

utc_times = [
schedule.recurrence_rules.map(&:until_time)
].flatten

denver_times = [
schedule.start_time,
schedule.end_time,
schedule.exception_times,
schedule.rtimes
].flatten

utc_times.each do |t|
expect(t.zone).to eq "UTC"
end

denver_times.each do |t|
expect(t.zone).to eq "MDT"
end
end

it "round trips from and to ical with time zones in the summer (MDT)" do
original = <<-ICAL.gsub(/^\s*/, "").strip
DTSTART;TZID=MDT:20130731T143000
RRULE:FREQ=WEEKLY;UNTIL=20140730T203000Z;BYDAY=MO,WE,FR
RDATE;TZID=MDT:20150812T143000
RDATE;TZID=MDT:20150807T143000
EXDATE;TZID=MDT:20130823T143000
EXDATE;TZID=MDT:20130812T143000
EXDATE;TZID=MDT:20130807T143000
DTEND;TZID=MDT:20130731T153000
ICAL

schedule_from_ical = IceCube::Schedule.from_ical original
expect(schedule_from_ical.to_ical).to eq original
end

it "round trips from and to ical with time zones in the winter (MST)" do
original = <<-ICAL.gsub(/^\s*/, "").strip
DTSTART;TZID=MST:20130131T143000
RRULE:FREQ=WEEKLY;UNTIL=20140130T203000Z;BYDAY=MO,WE,FR
RDATE;TZID=MST:20150212T143000
RDATE;TZID=MST:20150207T143000
EXDATE;TZID=MST:20130223T143000
EXDATE;TZID=MST:20130212T143000
EXDATE;TZID=MST:20130207T143000
DTEND;TZID=MST:20130131T153000
ICAL

schedule_from_ical = IceCube::Schedule.from_ical original
expect(schedule_from_ical.to_ical).to eq original
end
end

describe "exceptions" do
it "handles single EXDATE lines, single RDATE lines" do
start_time = Time.now
Expand Down