class Mhc::Converter::IcalendarImporter

Public Class Methods

parse_ics(ics) click to toggle source
# File lib/mhc/converter.rb, line 182
def self.parse_ics(ics)
  # * 3.8.1.  Descriptive Component Properties
  # ** CATEGORIES  3.8.1.2.  Categories
  # ** DESCRIPTION  3.8.1.5.  Description
  # ** LOCATION  3.8.1.7.  Location
  # ** SUMMARY  3.8.1.12. Summary
  # * 3.8.2.  Date and Time Component Properties
  # ** DTEND  3.8.2.2.  Date-Time End
  # ** DTSTART  3.8.2.4.  Date-Time Start
  # ** DURATION  3.8.2.5.  Duration
  # * 3.8.4.  Relationship Component Properties
  # ** RECURRENCE-ID  3.8.4.4.  Recurrence ID
  # * 3.8.5.  Recurrence Component Properties
  # ** EXDATE  3.8.5.1.  Exception Date-Times
  # ** RDATE  3.8.5.2.  Recurrence Date-Times
  # ** RRULE  3.8.5.3.  Recurrence Rule
  # * 3.8.7.  Change Management Component Properties
  # ** SEQUENCE  3.8.7.4.  Sequence Number
  # * 3.8.8.  Miscellaneous Component Properties
  # ** X-FIELD  3.8.8.2.  Non-Standard Properties

  # DTSTART:
  #   Date part => X-SC-Duration: .first
  #   Time part => X-SC-Time: .first
  # DTEND
  #   Date part =>
  #     DTEND - DTSTART = 1day
  #     DTEND - DTSTART > 1days
  #       Day:
  # RRULE:
  #  X-SC-Cond:
  # UNTIL:
  #  X-SC-Duration: .last
  # RDATES:
  #  X-SC-Day:
  # EXDATES:
  #   X-SC-Day: !YYYYMMDD
  #
  ical = RiCal.parse_string(ics).first
  return nil unless ical

  iev = ical.events.first
  allday = !iev.dtstart.respond_to?(:hour)
  recurring = !iev.rrule.empty?

  # X-SC-Day: (from DTSTART, DTEND)
  # for recurring event, DTSTSRT is a start part of X-SC-Duration:
  dates = []
  unless recurring
    date = tz_convert(iev.dtstart).strftime("%Y%m%d")
    if allday && (iev.dtend - iev.dtstart).to_i > 1
      date += "-" + (iev.dtend - 1).to_time.strftime("%Y%m%d")
    end
    dates << date
  end

  # X-SC-Day: (from RDATE, EXDATE)
  dates += iev.rdate.flatten.map{|d| d.to_time.strftime("%Y%m%d")}
  exdates = iev.exdate.flatten.map{|d| d.to_time.strftime("!%Y%m%d")}

  # X-SC-Time:
  unless allday
    time = tz_convert(iev.dtstart).strftime("%H:%M")
    if iev.dtend
      time += "-" + tz_convert(iev.dtend).strftime("%H:%M")
    end
  end

  ev = Mhc::Event.parse "X-SC-Subject: #{iev.summary}\n"  +
    "X-SC-Location: #{iev.location}\n"         +
    "X-SC-Day: #{(dates + exdates).join(' ')}\n" +
    "X-SC-Time: #{time}\n"           +
    "X-SC-Category: #{iev.categories.to_a.join(' ')}\n"       +
    "X-SC-Mission-Tag: #{iev.x_sc_mission_tag.first}\n" +
    "X-SC-Recurrence-Tag: #{iev.x_sc_recurrence_tag.first}\n" +
    "X-SC-Cond: \n" +
    "X-SC-Duration: \n"         +
    "X-SC-Alarm: \n"               +
    "X-SC-Record-Id: #{iev.uid}\n"       +
    "X-SC-Sequence: #{iev.sequence.to_i}\n\n" + iev.description.to_s +
    if $MHC_DEBUG_FOR_DEVELOPER # FIXME: should introduce good logger and debug scheme
      ical.to_s.force_encoding("ASCII-8BIT").gsub(/\r\n/, "\n")
    else
      ""
    end

  # X-SC-Cond:
  ev.recurrence_condition.set_from_ics(iev.rrule.first, tz_convert(iev.dtstart))

  # X-SC-Duration: is only for recurring articles
  if recurring
    duration_string = tz_convert(iev.dtstart).strftime("%Y%m%d") + "-"
    if iev.rrule.first.to_s.match(/until=([^;]+)/i)
      duration_string += parse_ical_datetime($1).strftime("%Y%m%d")
    end
    ev.duration = duration_string
  end

  return ev
end

Private Class Methods

parse_ical_datetime(datetime_string, dst_tzid = nil) click to toggle source
# File lib/mhc/converter.rb, line 309
def self.parse_ical_datetime(datetime_string, dst_tzid = nil)
  src_tzid = case datetime_string
             when /TZID=([^;]+)/
               $1
             when /\d{8}T\d{6}Z/
               "UTC"
             else
               Mhc.default_tzid
             end

  dst_tzid ||= Mhc.default_tzid

  if /^(\d{4})(\d\d)(\d\d)(?:T(\d\d)(\d\d)(\d\d)Z?)?$/ =~ datetime_string
    time = Time.utc($1, $2, $3, $4, $5, $6)
    return tz_convert(time, src_tzid: src_tzid, dst_tzid: dst_tzid)
  else
    raise ArgumentError
  end
end
tz_convert(datetime, src_tzid: nil, dst_tzid: nil) click to toggle source
# File lib/mhc/converter.rb, line 285
def self.tz_convert(datetime, src_tzid: nil, dst_tzid: nil)
  return datetime unless datetime.respond_to?(:hour)

  dst_tzid ||= Mhc.default_tzid
  src_tzid ||= if datetime.respond_to?(:tzid) and datetime.tzid
                 datetime.tzid
               else
                 Mhc.default_tzid
               end
  dst_tz = TZInfo::Timezone.get(dst_tzid)
  src_tz = TZInfo::Timezone.get(src_tzid)

  utc = Time.utc(datetime.year, datetime.month, datetime.day,
                 datetime.hour, datetime.min, datetime.sec)

  time1 = src_tz.local_to_utc(utc)
  time1.tzid = src_tzid if time1.respond_to?(:tzid)

  time = dst_tz.utc_to_local(time1)
  time.tzid = dst_tzid if time.respond_to?(:tzid)

  return time
end