class Mhc::PropertyValue::RecurrenceCondition
Public Class Methods
# File lib/mhc/property_value/recurrence_condition.rb, line 33 def initialize @cond_mon, @cond_ord, @cond_wek, @cond_num = [], [], [], [] end
Public Instance Methods
# File lib/mhc/property_value/recurrence_condition.rb, line 28 def cond_mon; return @cond_mon; end
# File lib/mhc/property_value/recurrence_condition.rb, line 31 def cond_num; return @cond_num; end
# File lib/mhc/property_value/recurrence_condition.rb, line 29 def cond_ord; return @cond_ord; end
# File lib/mhc/property_value/recurrence_condition.rb, line 30 def cond_wek; return @cond_wek; end
# File lib/mhc/property_value/recurrence_condition.rb, line 74 def daily? false end
# File lib/mhc/property_value/recurrence_condition.rb, line 94 def empty? [@cond_mon, @cond_ord, @cond_wek, @cond_num].all?{|cond| cond.empty?} end
# File lib/mhc/property_value/recurrence_condition.rb, line 66 def frequency return :none if empty? return :daily if daily? return :weekly if weekly? return :monthly if monthly? return :yearly if yearly? end
# File lib/mhc/property_value/recurrence_condition.rb, line 82 def monthly? !yearly? && (!cond_num.empty? || !cond_ord.empty?) end
# File lib/mhc/property_value/recurrence_condition.rb, line 37 def parse(string) o = self string.split.grep(MON_REGEXP) {|mon| o.cond_mon << MON_L2V[mon.capitalize]} string.split.grep(ORD_REGEXP) {|ord| o.cond_ord << ORD_L2V[ord.capitalize]} string.split.grep(WEK_REGEXP) {|wek| o.cond_wek << WEK_L2V[wek.capitalize]} string.split.grep(NUM_REGEXP) {|num| o.cond_num << num.to_i} return o end
# File lib/mhc/property_value/recurrence_condition.rb, line 161 def set_from_ics(rrule, dtstart) if (errno = validate_rrule(rrule)) != true raise "Unsupported RRULE string (errno=#{errno}): #{rrule}" end ################ ## BYMONTH (cond_mon) cond_mon = [] if rrule =~ /BYMONTH=([^;]+)/ $1.split(",").each do |mon| cond_mon << mon.to_i end end ################ ## BYDAY (cond_ord, cond_wek) cond_ord = [] cond_wek = [] week = {} if rrule =~ /BYDAY=([^;]+)/ $1.scan(/(1|2|3|4|-1)?(MO|TU|WE|TH|FR|SA|SU)/).each do |o,w| week[w] ||= [] week[w] << o.to_i # unpefixed week is replaced as 0 end # Every week should have the same number-prefix set: return 9 unless week.values.all?{|orders| orders.sort == week.values.first.sort} order = week.values.first.sort # * Number-prefixed week cannot coexist with unprefixed week # WE,SU is OK => Wed Sun # WE,3SU is NG return 10 if order.length > 1 and order.member?(0) # 0 means non-numberd prefix order.delete(0) cond_ord = order week.each do |w, o| cond_wek << WEK_V2I.invert[w] end end ################ ## BYMONTHDAY (cond_num) cond_num = [] if rrule =~ /BYMONTHDAY=([^;]+)/i $1.split(",").each do |n| cond_num << n.to_i end end ################ # Special cases interval = (rrule =~ /INTERVAL=(\d+)/i) ? $1.to_i : 1 # special case of yearly: repeat with 12 months interval # BYMONTH should be taken from DTSTART if interval == 12 and rrule =~ /FREQ=MONTHLY/i and cond_mon.empty? cond_mon << dtstart.month end # if RRULE has only FREQ=YEARLY phrase, # BYMONTH and BYMONTHDAY should be taken from DTSTART # if rrule =~ /FREQ=YEARLY/i cond_mon << dtstart.month if cond_mon.empty? cond_num << dtstart.day if cond_num.empty? and cond_wek.empty? end @cond_mon, @cond_ord, @cond_wek, @cond_num = cond_mon, cond_ord, cond_wek, cond_num return self end
# File lib/mhc/property_value/recurrence_condition.rb, line 243 def to_ics(dtstart = nil, until_date = nil) return nil unless valid? ord_wek = (cond_ord.empty? ? [""] : cond_ord).product(cond_wek) day = ord_wek.map {|o,w| o.to_s + WEK_V2I[w] }.join(',') if until_date if dtstart.respond_to?(:hour) tz = TZInfo::Timezone.get(ENV["MHC_TZID"] || 'UTC') localtime = Mhc::PropertyValue::Time.new.parse(dtstart.strftime("%H:%M")).to_datetime(until_date).to_time until_str = tz.local_to_utc(localtime).strftime("%Y%m%dT%H%M%SZ") # puts "until_str local (tz=#{tz.name}) : #{localtime.strftime("%Y%m%dT%H%M%S")} utc: #{until_str}" else until_str = until_date.strftime("%Y%m%d") end end ics = "FREQ=#{frequency.to_s.upcase};INTERVAL=1;WKST=MO" ics += ";BYMONTH=#{cond_mon.join(',')}" unless cond_mon.empty? ics += ";BYDAY=#{day}" unless day.empty? ics += ";BYMONTHDAY=#{cond_num.join(',')}" unless cond_num.empty? ics += ";UNTIL=#{until_str}" unless until_date.nil? return ics end
# File lib/mhc/property_value/recurrence_condition.rb, line 235 def to_mhc_string return (cond_mon.map{|mon| MON_V2L[mon]} + cond_ord.map{|ord| ORD_V2L[ord]} + cond_wek.map{|wek| WEK_V2L[wek]} + cond_num.map{|num| num.to_s} ).join(" ") end
# File lib/mhc/property_value/recurrence_condition.rb, line 90 def valid? frequency != :none end
convert RRULE to X-SC-Cond:
Due to the over-killing complexity of iCalendar (RFC5545) format, converting RRULE to X-SC-* format has some restrictions:
-
Not allowed elements:
-
BYSECOND
-
BYMINUTE
-
BYHOUR
-
COUNT
-
BYYEARDAY (-366 to 366)
-
BYWEEKNO (-53 to 53)
-
BYSETPOS (-366 to 366)
-
Recurrence-ID (not part of RRULE)
-
-
Restricted elements:
-
INTERVAL:
-
it should be 1
-
-
BYMONTHDAY:
-
it should be (1..31)
-
-
WKST:
-
it should be MO
-
-
FREQ:
-
should be one of WEEKLY, MONTHLY, YEARLY
-
should be MONTHLY if BYDAY has (1|2|3|4|-1)
-
should be WEEKLY if BYDAY does not have (1|2|3|4|-1)
-
-
BYDAY:
-
should be a list of (1|2|3|4|-1)?(MO|TU|WE|TH|FR|SA|SU)
-
Every week should have the same number-prefix set: WE,SU is OK => Wed Sun 3WE,3SU is OK => 3rd Wed Sun 2WE,3WE,2SU,3SU is OK => 2nd 3rd Sun Wed 3WE,2SU is NG 3WE,SU is NG
-
-
-
Fully converted elements:
-
UNTIL
-
YYYYMMDD should goes to X-SC-Duration: -YYYYMMDD
-
-
BYMONTH
-
(1..12)* => (Jan|Feb|Mar|Jul|Aug|Sep|Oct|Nov|Dec)*
-
-
# File lib/mhc/property_value/recurrence_condition.rb, line 147 def validate_rrule(rrule) interval = (rrule =~ /INTERVAL=(\d+)/i) ? $1.to_i : 1 return true if rrule.to_s == "" return 1 if rrule =~ /(BYSECOND|BYMINUTE|BYHOUR|COUNT|BYYEARDAY|BYWEEKNO|BYSETPOS)/i return 2 unless (rrule =~ /FREQ=MONTHLY/i and interval == 12) || interval == 1 return 3 if rrule =~ /BYMONTHDAY=([^;]+)/i and $1.split(",").map(&:to_i).any?{|i| i < 1 or i > 31} return 4 if rrule =~ /WKST=([^;]+)/i and $1 !~ /MO/ return 5 if rrule =~ /FREQ=([^;]+)/i and $1 !~ /WEEKLY|MONTHLY|YEARLY/i return 6 if rrule =~ /BYDAY=([^;]+)/i and $1 =~ /\d/ and rrule !~ /FREQ=MONTHLY/i return 7 if rrule =~ /BYDAY=([^;]+)/i and $1 !~ /\d/ and rrule !~ /FREQ=WEEKLY/i return 8 if rrule =~ /BYDAY=([^;]+)/i and $1 !~ /((1|2|3|4|-1)?(MO|TU|WE|TH|FR|SA|SU))+/i return true end
# File lib/mhc/property_value/recurrence_condition.rb, line 78 def weekly? !yearly? && !monthly? && !cond_wek.empty? end
# File lib/mhc/property_value/recurrence_condition.rb, line 86 def yearly? !cond_mon.empty? end