Skip to content

Commit

Permalink
feat: add timezone parsing support with tests from @epologee
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus-deans committed Jun 22, 2024
1 parent ab36aba commit 3c96a6f
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 3 deletions.
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.present?
case property
when "DTSTART"
value = { time: value, zone: zone } if zone.present?
data[:start_time] = TimeUtil.deserialize_time(value)
when "DTEND"
value = { time: value, zone: zone } if zone.present?
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.present?
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.present?
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.present?
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

t "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

0 comments on commit 3c96a6f

Please sign in to comment.