Permalink
Browse files

Added Schedule#to_ical

  • Loading branch information...
1 parent d214d7f commit e4620ce9ea4234ed2048af842a6fd12fec47a00a John Crepezzi committed Jul 27, 2010
Showing with 137 additions and 21 deletions.
  1. +20 −20 lib/ice_cube/schedule.rb
  2. +18 −0 lib/ice_cube/time_util.rb
  3. +1 −1 lib/ice_cube/version.rb
  4. +98 −0 spec/examples/to_ical_spec.rb
View
@@ -60,34 +60,34 @@ def self.from_yaml(str)
TIME_FORMAT = '%B %e, %Y'
SEPARATOR = ' / '
+ NEWLINE = "\n"
# use with caution
# incomplete and not entirely tested - no time representation in dates
# there's a lot that can happen here
def to_s
- representation = ''
+ representation_pieces = []
inc_dates = (@rdates - @exdates).uniq
- if inc_dates && !inc_dates.empty?
- representation << inc_dates.sort.map { |d| d.strftime(TIME_FORMAT) }.join(SEPARATOR)
- end
- if @rrule_occurrence_heads && !@rrule_occurrence_heads.empty?
- representation << SEPARATOR unless representation.empty?
- representation << @rrule_occurrence_heads.map{ |r| r.rule.to_s }.join(SEPARATOR)
- end
- if @exrule_occurrence_heads && !@exrule_occurrence_heads.empty?
- representation << SEPARATOR unless representation.empty?
- representation << @exrule_occurrence_heads.map { |r| 'not ' << r.to_s }.join(SEPARATOR)
- end
- if @exdates && !@exdates.empty?
- representation << SEPARATOR unless representation.empty?
- representation << @exdates.uniq.sort.map { |d| 'not on ' << d.strftime(TIME_FORMAT) }.join(SEPARATOR)
- end
- if @end_time
- representation << "until #{end_time.strftime(TIME_FORMAT)}"
- end
- representation
+ representation_pieces.concat inc_dates.sort.map { |d| d.strftime(TIME_FORMAT) } unless inc_dates.empty?
+ representation_pieces.concat @rrule_occurrence_heads.map{ |r| r.rule.to_s } if @rrule_occurrence_heads
+ representation_pieces.concat @exrule_occurrence_heads.map { |r| 'not ' << r.rule.to_s } if @exrule_occurrence_heads
+ representation_pieces.concat @exdates.uniq.sort.map { |d| 'not on ' << d.strftime(TIME_FORMAT) } if @exdates
+ representation_pieces << "until #{end_time.strftime(TIME_FORMAT)}" if @end_time
+ representation_pieces.join(SEPARATOR)
end
+ def to_ical
+ representation_pieces = ["DTSTART#{TimeUtil.ical_format(@start_date)}"]
+ representation_pieces << "DURATION:#{TimeUtil.ical_duration(@duration)}" if @duration
+ inc_dates = (@rdates - @exdates).uniq
+ representation_pieces.concat inc_dates.sort.map { |d| "RDATE#{TimeUtil.ical_format(d)}" } if inc_dates.any?
+ representation_pieces.concat @exdates.uniq.sort.map { |d| "EXDATE#{TimeUtil.ical_format(d)}" } if @exdates
+ representation_pieces.concat @rrule_occurrence_heads.map { |r| "RRULE:#{r.rule.to_ical}" } if @rrule_occurrence_heads
+ representation_pieces.concat @exrule_occurrence_heads.map { |r| "EXRULE:#{r.rule.to_ical}" } if @exrule_occurrence_heads
+ representation_pieces << "DTEND#{TimeUtil.ical_format(@end_time)}" if @end_time
+ representation_pieces.join(NEWLINE)
+ end
+
def occurring_at?(time)
return false if @exdates.include?(time)
return true if @rdates.include?(time)
View
@@ -27,5 +27,23 @@ def self.days_in_year(date)
def self.days_in_month(date)
is_leap?(date) ? LeapYearMonthDays[date.month - 1] : CommonYearMonthDays[date.month - 1]
end
+
+ def self.ical_format(time)
+ if time.utc?
+ ":#{time.strftime('%Y%m%dT%H%M%SZ')}" # utc time
+ else
+ ";TZID=#{time.strftime('%Z:%Y%m%dT%H%M%S')}" # local time specified
+ end
+ end
+
+ def self.ical_duration(duration)
+ hours = duration / 3600; duration %= 3600
+ minutes = duration / 60; duration %= 60
+ repr = ''
+ repr << "#{hours}H" if hours > 0
+ repr << "#{minutes}M" if minutes > 0
+ repr << "#{duration}S" if duration > 0
+ "PT#{repr}"
+ end
end
View
@@ -1,5 +1,5 @@
module IceCube
- VERSION = "0.4.2"
+ VERSION = '0.4.3'
end
@@ -86,5 +86,103 @@
rule = Rule.daily.day_of_week(:monday => [1, -1], :tuesday => [2]).day(:wednesday)
['FREQ=DAILY;BYDAY=WE;BYDAY=1MO,-1MO,2TU', 'FREQ=DAILY;BYDAY=1MO,-1MO,2TU;BYDAY=WE'].include?(rule.to_ical).should be(true)
end
+
+ it 'should be able to serialize a base schedule to ical in local time' do
+ Time.zone = "Eastern Time (US & Canada)"
+ schedule = Schedule.new(Time.zone.local(2010, 5, 10, 9, 0, 0))
+ schedule.to_ical.should == "DTSTART;TZID=EDT:20100510T090000"
+ end
+
+ it 'should be able to serialize a base schedule to ical in UTC time' do
+ schedule = Schedule.new(Time.utc(2010, 5, 10, 9, 0, 0))
+ schedule.to_ical.should == "DTSTART:20100510T090000Z"
+ end
+
+ it 'should be able to serialize a schedule with one rrule' do
+ Time.zone = 'Pacific Time (US & Canada)'
+ schedule = Schedule.new(Time.zone.local(2010, 5, 10, 9, 0, 0))
+ schedule.add_recurrence_rule Rule.weekly
+ # test equality
+ expectation = "DTSTART;TZID=PDT:20100510T090000\n"
+ expectation << 'RRULE:FREQ=WEEKLY'
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should be able to serialize a schedule with multiple rrules' do
+ Time.zone = 'Eastern Time (US & Canada)'
+ schedule = Schedule.new(Time.zone.local(2010, 10, 20, 4, 30, 0))
+ schedule.add_recurrence_rule Rule.weekly.day_of_week(:monday => [2, -1])
+ schedule.add_recurrence_rule Rule.hourly
+ expectation = "DTSTART;TZID=EDT:20101020T043000\n"
+ expectation << "RRULE:FREQ=WEEKLY;BYDAY=2MO,-1MO\n"
+ expectation << "RRULE:FREQ=HOURLY"
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should be able to serialize a schedule with one exrule' do
+ Time.zone ='Pacific Time (US & Canada)'
+ schedule = Schedule.new(Time.zone.local(2010, 5, 10, 9, 0, 0))
+ schedule.add_exception_rule Rule.weekly
+ # test equality
+ expectation= "DTSTART;TZID=PDT:20100510T090000\n"
+ expectation<< 'EXRULE:FREQ=WEEKLY'
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should be able to serialize a schedule with multiple exrules' do
+ Time.zone ='Eastern Time (US & Canada)'
+ schedule = Schedule.new(Time.zone.local(2010, 10, 20, 4, 30, 0))
+ schedule.add_exception_rule Rule.weekly.day_of_week(:monday => [2, -1])
+ schedule.add_exception_rule Rule.hourly
+ expectation = "DTSTART;TZID=EDT:20101020T043000\n"
+ expectation<< "EXRULE:FREQ=WEEKLY;BYDAY=2MO,-1MO\n"
+ expectation<< "EXRULE:FREQ=HOURLY"
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should be able to serialize a schedule with an rdate' do
+ schedule = Schedule.new(Time.utc(2010, 5, 10, 10, 0, 0))
+ schedule.add_recurrence_date Time.utc(2010, 6, 20, 5, 0, 0)
+ # test equality
+ expectation = "DTSTART:20100510T100000Z\n"
+ expectation << "RDATE:20100620T050000Z"
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should be able to serialize a schedule with an exdate' do
+ schedule = Schedule.new(Time.utc(2010, 5, 10, 10, 0, 0))
+ schedule.add_exception_date Time.utc(2010, 6, 20, 5, 0, 0)
+ # test equality
+ expectation = "DTSTART:20100510T100000Z\n"
+ expectation << "EXDATE:20100620T050000Z"
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should be able to serialize a schedule with a duration' do
+ schedule = Schedule.new(Time.utc(2010, 5, 10, 10), :duration => 3600)
+ expectation = "DTSTART:20100510T100000Z\n"
+ expectation << 'DURATION:PT1H'
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should be able to serialize a schedule with a duration - more odd duration' do
+ schedule = Schedule.new(Time.utc(2010, 5, 10, 10), :duration => 3665)
+ expectation = "DTSTART:20100510T100000Z\n"
+ expectation << 'DURATION:PT1H1M5S'
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should be able to serialize a schedule with an end time' do
+ schedule = Schedule.new(Time.utc(2010, 5, 10, 10), :end_time => Time.utc(2010, 5, 10, 20))
+ expectation = "DTSTART:20100510T100000Z\n"
+ expectation << "DTEND:20100510T200000Z"
+ schedule.to_ical.should == expectation
+ end
+
+ it 'should not modify the duration when running to_ical' do
+ schedule = Schedule.new(Time.now, :duration => 3600)
+ schedule.to_ical
+ schedule.duration.should == 3600
+ end
end

0 comments on commit e4620ce

Please sign in to comment.