micahwedemeyer-ri_cal 0.8.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (173) hide show
  1. data/.rvmrc +2 -0
  2. data/Gemfile +7 -0
  3. data/Gemfile.lock +21 -0
  4. data/History.txt +402 -0
  5. data/Manifest.txt +161 -0
  6. data/README.txt +410 -0
  7. data/Rakefile +66 -0
  8. data/VERSION +1 -0
  9. data/bin/ri_cal +8 -0
  10. data/component_attributes/alarm.yml +10 -0
  11. data/component_attributes/calendar.yml +4 -0
  12. data/component_attributes/component_property_defs.yml +180 -0
  13. data/component_attributes/event.yml +45 -0
  14. data/component_attributes/freebusy.yml +16 -0
  15. data/component_attributes/journal.yml +35 -0
  16. data/component_attributes/timezone.yml +3 -0
  17. data/component_attributes/timezone_period.yml +11 -0
  18. data/component_attributes/todo.yml +46 -0
  19. data/copyrights.txt +1 -0
  20. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  21. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  22. data/docs/incrementers.txt +7 -0
  23. data/docs/rfc2445.pdf +0 -0
  24. data/lib/ri_cal/component/alarm.rb +19 -0
  25. data/lib/ri_cal/component/calendar.rb +257 -0
  26. data/lib/ri_cal/component/event.rb +58 -0
  27. data/lib/ri_cal/component/freebusy.rb +16 -0
  28. data/lib/ri_cal/component/journal.rb +27 -0
  29. data/lib/ri_cal/component/non_standard.rb +33 -0
  30. data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
  31. data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
  32. data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
  33. data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
  34. data/lib/ri_cal/component/timezone.rb +197 -0
  35. data/lib/ri_cal/component/todo.rb +42 -0
  36. data/lib/ri_cal/component.rb +256 -0
  37. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  38. data/lib/ri_cal/core_extensions/array.rb +7 -0
  39. data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
  40. data/lib/ri_cal/core_extensions/date.rb +13 -0
  41. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  42. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  43. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  44. data/lib/ri_cal/core_extensions/object.rb +8 -0
  45. data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
  46. data/lib/ri_cal/core_extensions/string.rb +8 -0
  47. data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
  48. data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
  49. data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
  50. data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
  51. data/lib/ri_cal/core_extensions/time.rb +14 -0
  52. data/lib/ri_cal/core_extensions.rb +11 -0
  53. data/lib/ri_cal/fast_date_time.rb +234 -0
  54. data/lib/ri_cal/floating_timezone.rb +32 -0
  55. data/lib/ri_cal/invalid_property_value.rb +8 -0
  56. data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
  57. data/lib/ri_cal/occurrence_enumerator.rb +265 -0
  58. data/lib/ri_cal/occurrence_period.rb +17 -0
  59. data/lib/ri_cal/parser.rb +145 -0
  60. data/lib/ri_cal/properties/alarm.rb +390 -0
  61. data/lib/ri_cal/properties/calendar.rb +164 -0
  62. data/lib/ri_cal/properties/event.rb +1523 -0
  63. data/lib/ri_cal/properties/freebusy.rb +593 -0
  64. data/lib/ri_cal/properties/journal.rb +1237 -0
  65. data/lib/ri_cal/properties/timezone.rb +150 -0
  66. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  67. data/lib/ri_cal/properties/todo.rb +1559 -0
  68. data/lib/ri_cal/properties.rb +12 -0
  69. data/lib/ri_cal/property_value/array.rb +27 -0
  70. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  71. data/lib/ri_cal/property_value/date.rb +184 -0
  72. data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
  73. data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
  74. data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
  75. data/lib/ri_cal/property_value/date_time.rb +359 -0
  76. data/lib/ri_cal/property_value/duration.rb +110 -0
  77. data/lib/ri_cal/property_value/geo.rb +11 -0
  78. data/lib/ri_cal/property_value/integer.rb +12 -0
  79. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  80. data/lib/ri_cal/property_value/period.rb +86 -0
  81. data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
  82. data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
  83. data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
  84. data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
  85. data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
  86. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
  88. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
  89. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
  90. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
  91. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
  92. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
  93. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
  94. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
  95. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
  96. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
  97. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
  98. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
  99. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
  100. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
  101. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
  102. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
  103. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
  104. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
  105. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
  106. data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
  107. data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
  108. data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
  109. data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
  110. data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
  111. data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
  112. data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
  113. data/lib/ri_cal/property_value/text.rb +44 -0
  114. data/lib/ri_cal/property_value/uri.rb +11 -0
  115. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  116. data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
  117. data/lib/ri_cal/property_value.rb +159 -0
  118. data/lib/ri_cal/required_timezones.rb +55 -0
  119. data/lib/ri_cal.rb +187 -0
  120. data/micahwedemeyer-ri_cal.gemspec +227 -0
  121. data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
  122. data/performance/empty_propval/subject.rb +43 -0
  123. data/performance/paris_eastern/subject.rb +90 -0
  124. data/performance/penultimate_weekday/subject.rb +15 -0
  125. data/performance/psm_big_enum/ical.ics +3171 -0
  126. data/performance/psm_big_enum/subject.rb +16 -0
  127. data/performance/utah_cycling/subject.rb +55 -0
  128. data/sample_ical_files/from_ical_dot_app/test1.ics +38 -0
  129. data/script/benchmark_subject +23 -0
  130. data/script/console +10 -0
  131. data/script/destroy +14 -0
  132. data/script/generate +14 -0
  133. data/script/profile_subject +29 -0
  134. data/script/txt2html +71 -0
  135. data/spec/ri_cal/bugreports_spec.rb +287 -0
  136. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  137. data/spec/ri_cal/component/calendar_spec.rb +88 -0
  138. data/spec/ri_cal/component/event_spec.rb +735 -0
  139. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  140. data/spec/ri_cal/component/journal_spec.rb +37 -0
  141. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
  142. data/spec/ri_cal/component/timezone_spec.rb +236 -0
  143. data/spec/ri_cal/component/todo_spec.rb +112 -0
  144. data/spec/ri_cal/component_spec.rb +224 -0
  145. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  146. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  147. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  148. data/spec/ri_cal/fast_date_time_spec.rb +77 -0
  149. data/spec/ri_cal/inf_loop_spec.rb +78 -0
  150. data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
  151. data/spec/ri_cal/parser_spec.rb +337 -0
  152. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  153. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  154. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  155. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  156. data/spec/ri_cal/property_value/period_spec.rb +63 -0
  157. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  158. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  159. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  160. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  161. data/spec/ri_cal/property_value_spec.rb +125 -0
  162. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  163. data/spec/ri_cal_spec.rb +53 -0
  164. data/spec/spec.opts +4 -0
  165. data/spec/spec_helper.rb +50 -0
  166. data/tasks/gem_loader/load_active_support.rb +3 -0
  167. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  168. data/tasks/ri_cal.rake +412 -0
  169. data/tasks/spec.rake +102 -0
  170. data/website/images/rubytrends.png +0 -0
  171. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  172. data/website/stylesheets/screen.css +159 -0
  173. metadata +307 -0
@@ -0,0 +1,7 @@
1
+ Seconds - Freq="SECONDLY", BYSECOND
2
+ Minutes - Freq="MINUTELY", BYMINUTE
3
+ Hours - Freq="HOURLY", BYHOUR
4
+ Days - Freq="DAILY", BYDAY, BYMONTHDAY, BYYEARDAY
5
+ Weeks - Freq="WEEKLY", BYWEEKNO
6
+ Months - Freq="MONTHLY", BYMONTH
7
+ Years - Freq="YEARLY"
data/docs/rfc2445.pdf ADDED
Binary file
@@ -0,0 +1,19 @@
1
+ module RiCal
2
+
3
+ class Component
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # An Alarm component groups properties defining a reminder or alarm associated with an event or to-do
7
+ # TODO: The Alarm component has complex cardinality restrictions depending on the value of the action property
8
+ # i.e. audio, display, email, and proc alarms, this is currently not checked or enforced
9
+ #
10
+ # to see the property accessing methods for this class see the RiCal::Properties::Alarm module
11
+ class Alarm < Component
12
+ include RiCal::Properties::Alarm
13
+
14
+ def self.entity_name #:nodoc:
15
+ "VALARM"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,257 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # to see the property accessing methods for this class see the RiCal::Properties::Calendar module
6
+ class Calendar < Component
7
+ include RiCal::Properties::Calendar
8
+ attr_reader :tz_source #:nodoc:
9
+
10
+ def initialize(parent=nil,entity_name = nil, &init_block) #:nodoc:
11
+ @tz_source = 'TZINFO' # Until otherwise told
12
+ super
13
+ end
14
+
15
+ def self.entity_name #:nodoc:
16
+ "VCALENDAR"
17
+ end
18
+
19
+ def tz_info_source? #:nodoc:
20
+ @tz_source == 'TZINFO'
21
+ end
22
+
23
+ def required_timezones # :nodoc:
24
+ @required_timezones ||= RequiredTimezones.new
25
+ end
26
+
27
+ def subcomponent_class # :nodoc:
28
+ {
29
+ :event => Event,
30
+ :todo => Todo,
31
+ :journal => Journal,
32
+ :freebusy => Freebusy,
33
+ :timezone => Timezone,
34
+ }
35
+ end
36
+
37
+ def export_properties_to(export_stream) # :nodoc:
38
+ prodid_property.params["X-RICAL-TZSOURCE"] = @tz_source if @tz_source
39
+ export_prop_to(export_stream, "PRODID", prodid_property)
40
+ export_prop_to(export_stream, "CALSCALE", calscale_property)
41
+ export_prop_to(export_stream, "VERSION", version_property)
42
+ export_prop_to(export_stream, "METHOD", method_property)
43
+ end
44
+
45
+ def prodid_property_from_string(line) # :nodoc:
46
+ result = super
47
+ @tz_source = prodid_property.params["X-RICAL-TZSOURCE"]
48
+ result
49
+ end
50
+
51
+ # Return the default time zone identifier for this calendar
52
+ def default_tzid
53
+ @default_tzid || PropertyValue::DateTime.default_tzid
54
+ end
55
+
56
+ # Set the default time zone identifier for this calendar
57
+ # To set the default to floating times use a value of :floating
58
+ def default_tzid=(value)
59
+ @default_tzid=value
60
+ end
61
+
62
+ # return an array of event components contained within this Calendar
63
+ def events
64
+ subcomponents["VEVENT"]
65
+ end
66
+
67
+ # add an event to the calendar
68
+ def add_subcomponent(component)
69
+ super(component)
70
+ component.add_date_times_to(required_timezones) if tz_info_source?
71
+ end
72
+
73
+ # return an array of todo components contained within this Calendar
74
+ def todos
75
+ subcomponents["VTODO"]
76
+ end
77
+
78
+ # return an array of journal components contained within this Calendar
79
+ def journals
80
+ subcomponents["VJOURNAL"]
81
+ end
82
+
83
+ # return an array of freebusy components contained within this Calendar
84
+ def freebusys
85
+ subcomponents["VFREEBUSY"]
86
+ end
87
+
88
+ class TimezoneID #:nodoc:
89
+ attr_reader :identifier, :calendar
90
+ def initialize(identifier, calendar)
91
+ self.identifier, self.calendar = identifier, calendar
92
+ end
93
+
94
+ def tzinfo_timezone
95
+ nil
96
+ end
97
+
98
+ def resolved
99
+ calendar.find_timezone(identifier)
100
+ end
101
+
102
+ def local_to_utc(local)
103
+ resolved.local_to_utc(date_time_prop)
104
+ end
105
+ end
106
+
107
+ # return an array of timezone components contained within this calendar
108
+ def timezones
109
+ subcomponents["VTIMEZONE"]
110
+ end
111
+
112
+ class TZInfoWrapper #:nodoc:
113
+ attr_reader :tzinfo, :calendar #:nodoc:
114
+ def initialize(tzinfo, calendar) #:nodoc:
115
+ @tzinfo = tzinfo
116
+ @calendar = calendar
117
+ end
118
+
119
+ def identifier #:nodoc:
120
+ tzinfo.identifier
121
+ end
122
+
123
+ def local_date_time(ruby_time, tzid) #:nodoc:
124
+ RiCal::PropertyValue::DateTime.new(calendar, :value => ruby_time.strftime("%Y%m%dT%H%M%S"), :params => {'TZID' => tzid})
125
+ end
126
+
127
+ def utc_date_time(ruby_time) #:nodoc
128
+ RiCal::PropertyValue::DateTime.new(calendar, :value => ruby_time.strftime("%Y%m%dT%H%M%SZ"))
129
+ end
130
+
131
+ def local_to_utc(utc) #:nodoc:
132
+ utc_date_time(tzinfo.local_to_utc(utc.to_ri_cal_ruby_value))
133
+ end
134
+
135
+ def utc_to_local(local) #:nodoc:
136
+ local_date_time(tzinfo.utc_to_local(local.to_ri_cal_ruby_value), tzinfo.identifier)
137
+ end
138
+
139
+
140
+ def rational_utc_offset(local)
141
+ RiCal.RationalOffset[tzinfo.period_for_local(local, true).utc_total_offset]
142
+ end
143
+
144
+ end
145
+
146
+ def find_timezone(identifier) #:nodoc:
147
+ if tz_info_source?
148
+ begin
149
+ TZInfoWrapper.new(TZInfo::Timezone.get(identifier), self)
150
+ rescue ::TZInfo::InvalidTimezoneIdentifier => ex
151
+ raise RiCal::InvalidTimezoneIdentifier.invalid_tzinfo_identifier(identifier)
152
+ end
153
+ else
154
+ result = timezones.find {|tz| tz.tzid == identifier}
155
+ raise RiCal::InvalidTimezoneIdentifier.not_found_in_calendar(identifier) unless result
156
+ result
157
+ end
158
+ end
159
+
160
+ def export_required_timezones(export_stream) # :nodoc:
161
+ required_timezones.export_to(export_stream)
162
+ end
163
+
164
+ class FoldingStream #:nodoc:
165
+ attr_reader :stream #:nodoc:
166
+ def initialize(stream) #:nodoc:
167
+ @stream = stream || StringIO.new
168
+ end
169
+
170
+ def string #:nodoc:
171
+ stream.string
172
+ end
173
+
174
+ if RUBY_VERSION =~ /^1\.9/
175
+ def utf8_safe_split(string, n)
176
+ if string.bytesize <= n
177
+ [string, nil]
178
+ else
179
+ bytes = string.bytes.to_a
180
+ while (128..191).include?(bytes[n])
181
+ n = n - 1
182
+ end
183
+ before = bytes[0,n]
184
+ after = bytes[n..-1]
185
+ [before.pack("C*").force_encoding("utf-8"), after.empty? ? nil : after.pack("C*").force_encoding("utf-8")]
186
+ end
187
+ end
188
+ else
189
+ def valid_utf8?(string)
190
+ string.unpack("U") rescue nil
191
+ end
192
+
193
+ def utf8_safe_split(string, n)
194
+ if string.length <= n
195
+ [string, nil]
196
+ else
197
+ before = string[0, n]
198
+ after = string[n..-1]
199
+ until valid_utf8?(after)
200
+ n = n - 1
201
+ before = string[0, n]
202
+ after = string[n..-1]
203
+ end
204
+ [before, after.empty? ? nil : after]
205
+ end
206
+ end
207
+ end
208
+
209
+ def fold(string) #:nodoc:
210
+ line, remainder = *utf8_safe_split(string, 73)
211
+ stream.puts(line)
212
+ while remainder
213
+ line, remainder = *utf8_safe_split(remainder, 72)
214
+ stream.puts(" #{line}")
215
+ end
216
+ end
217
+
218
+ def puts(*strings) #:nodoc:
219
+ strings.each do |string|
220
+ string.split("\n").each do |line|
221
+ fold(line)
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ # Export this calendar as an iCalendar file.
228
+ # if to is nil (the default) then this method will return a string,
229
+ # otherwise to should be an IO to which the iCalendar file contents will be written
230
+ def export(to=nil)
231
+ export_stream = FoldingStream.new(to)
232
+ export_stream.puts("BEGIN:VCALENDAR")
233
+ export_properties_to(export_stream)
234
+ export_x_properties_to(export_stream)
235
+ export_required_timezones(export_stream)
236
+ export_subcomponent_to(export_stream, events)
237
+ export_subcomponent_to(export_stream, todos)
238
+ export_subcomponent_to(export_stream, journals)
239
+ export_subcomponent_to(export_stream, freebusys)
240
+ subcomponents.each do |key, value|
241
+ unless %{VEVENT VTODO VJOURNAL VFREEBUSYS}.include?(key)
242
+ export_subcomponent_to(export_stream, value)
243
+ end
244
+ end
245
+ export_stream.puts("END:VCALENDAR")
246
+ if to
247
+ nil
248
+ else
249
+ export_stream.string
250
+ end
251
+ end
252
+
253
+ alias_method :export_to, :export
254
+
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,58 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # An Event (VEVENT) calendar component groups properties describing a scheduled event.
6
+ # Events may have multiple occurrences
7
+ #
8
+ # Events may also contain one or more ALARM subcomponents
9
+ #
10
+ # to see the property accessing methods for this class see the RiCal::Properties::Event module
11
+ # to see the methods for enumerating occurrences of recurring events see the RiCal::OccurrenceEnumerator module
12
+ class Event < Component
13
+ include OccurrenceEnumerator
14
+
15
+ include RiCal::Properties::Event
16
+
17
+ def subcomponent_class #:nodoc:
18
+ {:alarm => Alarm }
19
+ end
20
+
21
+ def self.entity_name #:nodoc:
22
+ "VEVENT"
23
+ end
24
+
25
+ # Return a date_time representing the time at which the event starts
26
+ def start_time
27
+ dtstart_property ? dtstart.to_datetime : nil
28
+ end
29
+
30
+ # Return a date_time_property representing the time at which the event ends
31
+ def finish_property
32
+ if dtend_property
33
+ dtend_property
34
+ elsif duration_property
35
+ (dtstart_property + duration_property)
36
+ else
37
+ dtstart_property
38
+ end
39
+ end
40
+
41
+ # Return a date_time representing the time at which the event starts
42
+ def finish_time
43
+ prop = finish_property
44
+ prop ? prop.to_finish_time : nil
45
+ end
46
+
47
+ def zulu_occurrence_range_start_time
48
+ dtstart_property ? dtstart_property.to_zulu_occurrence_range_start_time : nil
49
+ end
50
+
51
+ def zulu_occurrence_range_finish_time
52
+ prop = finish_property
53
+ prop ? prop.to_zulu_occurrence_range_finish_time : nil
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,16 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # A Freebusy (VFREEBUSY) calendar component groups properties describing either a request for free/busy time,
6
+ # a response to a request for free/busy time, or a published set of busy time.
7
+ # to see the property accessing methods for this class see the RiCal::Properties::Freebusy module
8
+ class Freebusy < Component
9
+ include RiCal::Properties::Freebusy
10
+
11
+ def self.entity_name #:nodoc:
12
+ "VFREEBUSY"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ module RiCal
2
+ class Component
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # A Journal (VJOURNAL) calendar component groups properties describing a journal entry.
6
+ # Journals may have multiple occurrences
7
+ # to see the property accessing methods for this class see the RiCal::Properties::Journal module
8
+ # to see the methods for enumerating occurrences of recurring journals see the RiCal::OccurrenceEnumerator module
9
+ class Journal < Component
10
+ include RiCal::Properties::Journal
11
+ include RiCal::OccurrenceEnumerator
12
+
13
+ def self.entity_name #:nodoc:
14
+ "VJOURNAL"
15
+ end
16
+
17
+ # Return a date_time representing the time at which the event starts
18
+ def start_time
19
+ dtstart.to_datetime
20
+ end
21
+
22
+ # Journals take up no calendar time, so the finish time is always the same as the start_time
23
+ alias_method :finish_time, :start_time
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ module RiCal
2
+
3
+ class Component
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # An NonStandard component represents a component (or subcomponent) not listed in RFC2445.
7
+ # For example some icalendar data contains VVENUE components, a proposed extension to RFC2445
8
+ # which was dropped.
9
+ class NonStandard < Component
10
+ attr_reader :entity_name
11
+
12
+ def initialize(parent, entity_name)
13
+ super(parent)
14
+ @entity_name = entity_name
15
+ @source_lines = []
16
+ end
17
+
18
+ def process_line(parser, line) #:nodoc:
19
+ if line[:name] == "BEGIN"
20
+ parse_subcomponent(parser, line)
21
+ else
22
+ @source_lines << parser.last_line_str
23
+ end
24
+ end
25
+
26
+ def export_properties_to(stream)
27
+ @source_lines.each do |line|
28
+ stream.puts(line)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,153 @@
1
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
2
+ #
3
+ # A wrapper class for a Timezone implemented by the TZInfo Gem
4
+ # (or by Rails)
5
+ #
6
+
7
+ class RiCal::Component::TZInfoTimezone < RiCal::Component::Timezone
8
+
9
+ class Period #:nodoc: all
10
+
11
+ def initialize(which, this_period, prev_period)
12
+ @which = which
13
+ if prev_period
14
+ @onset = period_local_end(prev_period)
15
+ @offset_from = format_rfc2445_offset(prev_period.utc_total_offset)
16
+ else
17
+ @onset = period_local_start(this_period)
18
+ @offset_from = format_rfc2445_offset(this_period.utc_total_offset)
19
+ end
20
+ @offset_to = format_rfc2445_offset(this_period.utc_total_offset)
21
+ @abbreviation = this_period.abbreviation
22
+ @rdates = []
23
+ end
24
+
25
+ def daylight?
26
+ @which == "DAYLIGHT"
27
+ end
28
+
29
+ def period_local_end(period)
30
+ (period.local_end || DateTime.parse("99990101T000000")).strftime("%Y%m%dT%H%M%S")
31
+ end
32
+
33
+ # This assumes a 1 hour shift which is why we use the previous period local end when
34
+ # possible
35
+ def period_local_start(period)
36
+ shift = daylight? ? Rational(-1, 24) : Rational(1, 24)
37
+ ((period.local_start || DateTime.parse("16010101T000000")) + shift).strftime("%Y%m%dT%H%M%S")
38
+ end
39
+
40
+ def add_period(this_period)
41
+ @rdates << period_local_start(this_period)
42
+ end
43
+
44
+
45
+ def format_rfc2445_offset(seconds) #:nodoc:
46
+ abs_seconds = seconds.abs
47
+ h = (abs_seconds/3600).floor
48
+ m = (abs_seconds - (h * 3600))/60
49
+ h *= -1 if seconds < 0
50
+ sprintf("%+03d%02d", h, m)
51
+ end
52
+
53
+ def export_to(export_stream)
54
+ export_stream.puts "BEGIN:#{@which}"
55
+ export_stream.puts "DTSTART:#{@onset}"
56
+ @rdates.each do |rdate|
57
+ export_stream.puts "RDATE:#{rdate}"
58
+ end
59
+ export_stream.puts "TZOFFSETFROM:#{@offset_from}"
60
+ export_stream.puts "TZOFFSETTO:#{@offset_to}"
61
+ export_stream.puts "TZNAME:#{@abbreviation}"
62
+ export_stream.puts "END:#{@which}"
63
+ end
64
+ end
65
+
66
+ class Periods #:nodoc: all
67
+
68
+ def initialize
69
+ @dst_period = @std_period = @previous_period = nil
70
+ end
71
+
72
+ def empty?
73
+ @periods.nil? || @periods.empty?
74
+ end
75
+
76
+ def daylight_period(this_period, previous_period)
77
+ @daylight_period ||= Period.new("DAYLIGHT", this_period, previous_period)
78
+ end
79
+
80
+ def standard_period(this_period, previous_period)
81
+ @standard_period ||= Period.new("STANDARD", this_period, previous_period)
82
+ end
83
+
84
+ def log_period(period)
85
+ @periods ||= []
86
+ @periods << period unless @periods.include?(period)
87
+ end
88
+
89
+ def add_period(this_period, force=false)
90
+ if @previous_period || force
91
+ if this_period.dst?
92
+ period = daylight_period(this_period, @previous_period)
93
+ else
94
+ period = standard_period(this_period, @previous_period)
95
+ end
96
+ period.add_period(this_period)
97
+ log_period(period)
98
+ end
99
+ @previous_period = this_period
100
+ end
101
+
102
+ def export_to(export_stream)
103
+ @periods.each {|period| period.export_to(export_stream)}
104
+ end
105
+ end
106
+
107
+ attr_reader :tzinfo_timezone #:nodoc:
108
+
109
+ def initialize(tzinfo_timezone) #:nodoc:
110
+ @tzinfo_timezone = tzinfo_timezone
111
+ end
112
+
113
+ # convert time from this time zone to utc time
114
+ def local_to_utc(time)
115
+ @tzinfo_timezone.local_to_utc(time.to_ri_cal_ruby_value)
116
+ end
117
+
118
+ # convert time from utc time to this time zone
119
+ def utc_to_local(time)
120
+ @tzinfo_timezone.utc_to_local(time.to_ri_cal_ruby_value)
121
+ end
122
+
123
+ # return the time zone identifier
124
+ def identifier
125
+ @tzinfo_timezone.identifier
126
+ end
127
+
128
+ def export_local_to(export_stream, local_start, local_end) #:nodoc:
129
+ export_utc_to(export_stream, local_to_utc(local_start.to_ri_cal_ruby_value), local_to_utc(local_end.to_ri_cal_ruby_value))
130
+ end
131
+
132
+ def to_rfc2445_string(utc_start, utc_end) #:nodoc:
133
+ export_stream = StringIO.new
134
+ export_utc_to(export_stream, utc_start, utc_end)
135
+ export_stream.string
136
+ end
137
+
138
+ def export_utc_to(export_stream, utc_start, utc_end) #:nodoc:
139
+ export_stream.puts "BEGIN:VTIMEZONE","TZID;X-RICAL-TZSOURCE=TZINFO:#{identifier}"
140
+ periods = Periods.new
141
+ period = initial_period = tzinfo_timezone.period_for_utc(utc_start)
142
+ #start with the period before the one containing utc_start
143
+ prev_period = period.utc_start && tzinfo_timezone.period_for_utc(period.utc_start - 1)
144
+ period = prev_period if prev_period
145
+ while period && period.utc_start && period.utc_start < utc_end
146
+ periods.add_period(period)
147
+ period = period.utc_end && tzinfo_timezone.period_for_utc(period.utc_end + 1)
148
+ end
149
+ periods.add_period(initial_period, :force) if periods.empty?
150
+ periods.export_to(export_stream)
151
+ export_stream.puts "END:VTIMEZONE\n"
152
+ end
153
+ end
@@ -0,0 +1,25 @@
1
+ module RiCal
2
+ class Component
3
+ class Timezone
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # A DaylightPeriod is a TimezonePeriod during which daylight saving time *is* in effect
7
+ class DaylightPeriod < TimezonePeriod #:nodoc: all
8
+
9
+ def self.entity_name #:nodoc:
10
+ "DAYLIGHT"
11
+ end
12
+
13
+ def dst?
14
+ true
15
+ end
16
+
17
+ def swallows_local?(local, std_candidate)
18
+ ([local.year, local.month, local.day] == [dtstart.year,dtstart.month, dtstart.day]) &&
19
+ local >= dtstart_property &&
20
+ local.advance(:seconds => (std_candidate.utc_total_offset - utc_total_offset)) < dtstart_property
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module RiCal
2
+ class Component
3
+ class Timezone
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # A StandardPeriod is a TimezonePeriod during which daylight saving time is *not* in effect
7
+ class StandardPeriod < TimezonePeriod #:nodoc: all
8
+
9
+ def self.entity_name #:nodoc:
10
+ "STANDARD"
11
+ end
12
+
13
+ def dst?
14
+ false
15
+ end
16
+
17
+ def ambiguous_local?(time)
18
+ [time.year, time.month, time.day] == [dtstart.year, dtstart.month, dtstart.day]
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end