awallis-ri_cal 0.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. data/History.txt +402 -0
  2. data/Manifest.txt +161 -0
  3. data/README.txt +410 -0
  4. data/Rakefile +69 -0
  5. data/VERSION +1 -0
  6. data/bin/ri_cal +8 -0
  7. data/component_attributes/alarm.yml +10 -0
  8. data/component_attributes/calendar.yml +4 -0
  9. data/component_attributes/component_property_defs.yml +180 -0
  10. data/component_attributes/event.yml +45 -0
  11. data/component_attributes/freebusy.yml +16 -0
  12. data/component_attributes/journal.yml +35 -0
  13. data/component_attributes/timezone.yml +3 -0
  14. data/component_attributes/timezone_period.yml +11 -0
  15. data/component_attributes/todo.yml +46 -0
  16. data/copyrights.txt +1 -0
  17. data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
  18. data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
  19. data/docs/incrementers.txt +7 -0
  20. data/docs/rfc2445.pdf +0 -0
  21. data/lib/ri_cal.rb +187 -0
  22. data/lib/ri_cal/component.rb +256 -0
  23. data/lib/ri_cal/component/alarm.rb +19 -0
  24. data/lib/ri_cal/component/calendar.rb +257 -0
  25. data/lib/ri_cal/component/event.rb +58 -0
  26. data/lib/ri_cal/component/freebusy.rb +16 -0
  27. data/lib/ri_cal/component/journal.rb +27 -0
  28. data/lib/ri_cal/component/non_standard.rb +33 -0
  29. data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
  30. data/lib/ri_cal/component/timezone.rb +197 -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/todo.rb +42 -0
  35. data/lib/ri_cal/core_extensions.rb +11 -0
  36. data/lib/ri_cal/core_extensions/array.rb +7 -0
  37. data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
  38. data/lib/ri_cal/core_extensions/date.rb +13 -0
  39. data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
  40. data/lib/ri_cal/core_extensions/date_time.rb +15 -0
  41. data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
  42. data/lib/ri_cal/core_extensions/object.rb +8 -0
  43. data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
  44. data/lib/ri_cal/core_extensions/string.rb +8 -0
  45. data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
  46. data/lib/ri_cal/core_extensions/time.rb +14 -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/fast_date_time.rb +234 -0
  52. data/lib/ri_cal/floating_timezone.rb +32 -0
  53. data/lib/ri_cal/invalid_property_value.rb +8 -0
  54. data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
  55. data/lib/ri_cal/occurrence_enumerator.rb +265 -0
  56. data/lib/ri_cal/occurrence_period.rb +17 -0
  57. data/lib/ri_cal/parser.rb +145 -0
  58. data/lib/ri_cal/properties.rb +12 -0
  59. data/lib/ri_cal/properties/alarm.rb +390 -0
  60. data/lib/ri_cal/properties/calendar.rb +164 -0
  61. data/lib/ri_cal/properties/event.rb +1523 -0
  62. data/lib/ri_cal/properties/freebusy.rb +593 -0
  63. data/lib/ri_cal/properties/journal.rb +1237 -0
  64. data/lib/ri_cal/properties/timezone.rb +150 -0
  65. data/lib/ri_cal/properties/timezone_period.rb +416 -0
  66. data/lib/ri_cal/properties/todo.rb +1559 -0
  67. data/lib/ri_cal/property_value.rb +159 -0
  68. data/lib/ri_cal/property_value/array.rb +27 -0
  69. data/lib/ri_cal/property_value/cal_address.rb +11 -0
  70. data/lib/ri_cal/property_value/date.rb +184 -0
  71. data/lib/ri_cal/property_value/date_time.rb +359 -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/duration.rb +110 -0
  76. data/lib/ri_cal/property_value/geo.rb +11 -0
  77. data/lib/ri_cal/property_value/integer.rb +12 -0
  78. data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
  79. data/lib/ri_cal/property_value/period.rb +86 -0
  80. data/lib/ri_cal/property_value/recurrence_rule.rb +154 -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.rb +135 -0
  87. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
  88. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
  89. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
  90. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
  91. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
  92. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
  93. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
  94. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
  95. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
  96. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
  97. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
  98. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
  99. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
  100. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
  101. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
  102. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
  103. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
  104. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
  105. data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -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/text.rb +44 -0
  113. data/lib/ri_cal/property_value/uri.rb +11 -0
  114. data/lib/ri_cal/property_value/utc_offset.rb +33 -0
  115. data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
  116. data/lib/ri_cal/required_timezones.rb +55 -0
  117. data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
  118. data/performance/empty_propval/subject.rb +43 -0
  119. data/performance/paris_eastern/subject.rb +90 -0
  120. data/performance/penultimate_weekday/subject.rb +15 -0
  121. data/performance/psm_big_enum/ical.ics +3171 -0
  122. data/performance/psm_big_enum/subject.rb +16 -0
  123. data/performance/utah_cycling/subject.rb +55 -0
  124. data/ri_cal.gemspec +244 -0
  125. data/script/benchmark_subject +23 -0
  126. data/script/console +10 -0
  127. data/script/destroy +14 -0
  128. data/script/generate +14 -0
  129. data/script/profile_subject +29 -0
  130. data/script/txt2html +71 -0
  131. data/spec/ri_cal/bugreports_spec.rb +276 -0
  132. data/spec/ri_cal/component/alarm_spec.rb +12 -0
  133. data/spec/ri_cal/component/calendar_spec.rb +88 -0
  134. data/spec/ri_cal/component/event_spec.rb +735 -0
  135. data/spec/ri_cal/component/freebusy_spec.rb +12 -0
  136. data/spec/ri_cal/component/journal_spec.rb +37 -0
  137. data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
  138. data/spec/ri_cal/component/timezone_spec.rb +236 -0
  139. data/spec/ri_cal/component/todo_spec.rb +112 -0
  140. data/spec/ri_cal/component_spec.rb +224 -0
  141. data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
  142. data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
  143. data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
  144. data/spec/ri_cal/fast_date_time_spec.rb +77 -0
  145. data/spec/ri_cal/inf_loop_spec.rb +78 -0
  146. data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
  147. data/spec/ri_cal/parser_spec.rb +337 -0
  148. data/spec/ri_cal/property_value/date_spec.rb +53 -0
  149. data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
  150. data/spec/ri_cal/property_value/duration_spec.rb +126 -0
  151. data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
  152. data/spec/ri_cal/property_value/period_spec.rb +63 -0
  153. data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
  154. data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
  155. data/spec/ri_cal/property_value/text_spec.rb +25 -0
  156. data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
  157. data/spec/ri_cal/property_value_spec.rb +125 -0
  158. data/spec/ri_cal/required_timezones_spec.rb +67 -0
  159. data/spec/ri_cal_spec.rb +53 -0
  160. data/spec/spec.opts +4 -0
  161. data/spec/spec_helper.rb +50 -0
  162. data/tasks/gem_loader/load_active_support.rb +3 -0
  163. data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
  164. data/tasks/ri_cal.rake +412 -0
  165. data/tasks/spec.rake +102 -0
  166. metadata +255 -0
@@ -0,0 +1,159 @@
1
+ module RiCal
2
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
3
+ #
4
+ # PropertyValue provides common implementation of various RFC 2445 property value types
5
+ class PropertyValue
6
+
7
+ autoload :Array, "ri_cal/property_value/array.rb"
8
+ autoload :CalAddress, "ri_cal/property_value/cal_address.rb"
9
+ autoload :Date, "ri_cal/property_value/date.rb"
10
+ autoload :DateTime, "ri_cal/property_value/date_time.rb"
11
+ autoload :Duration, "ri_cal/property_value/duration.rb"
12
+ autoload :Geo, "ri_cal/property_value/geo.rb"
13
+ autoload :Integer, "ri_cal/property_value/integer.rb"
14
+ autoload :OccurrenceList, "ri_cal/property_value/occurrence_list.rb"
15
+ autoload :Period, "ri_cal/property_value/period.rb"
16
+ autoload :RecurrenceRule, "ri_cal/property_value/recurrence_rule.rb"
17
+ autoload :Text, "ri_cal/property_value/text.rb"
18
+ autoload :Uri, "ri_cal/property_value/uri.rb"
19
+ autoload :UtcOffset, "ri_cal/property_value/utc_offset.rb"
20
+ autoload :ZuluDateTime, "ri_cal/property_value/zulu_date_time.rb"
21
+
22
+ attr_writer :params, :value #:nodoc:
23
+ attr_reader :timezone_finder #:nodoc:
24
+ def initialize(timezone_finder, options={}) # :nodoc:
25
+ @timezone_finder = timezone_finder
26
+ validate_value(options)
27
+ ({:params => {}}).merge(options).each do |attribute, val|
28
+ unless attribute == :name
29
+ setter = :"#{attribute.to_s.downcase}="
30
+ send(setter, val)
31
+ end
32
+ end
33
+ end
34
+
35
+ def self.if_valid_string(timezone_finder, string) #:nodoc:
36
+ if valid_string?(string)
37
+ new(timezone_finder, :value => string)
38
+ else
39
+ nil
40
+ end
41
+ end
42
+
43
+ def validate_value(options) #:nodoc:
44
+ val = options[:value]
45
+ raise "Invalid property value #{val.inspect}" if val.kind_of?(String) && /^;/.match(val)
46
+ end
47
+
48
+ # return a hash containing the parameters and values, if any
49
+ def params
50
+ @params ||= {}
51
+ end
52
+
53
+ def to_options_hash #:nodoc:
54
+ options_hash = {:value => value}
55
+ options_hash[:params] = params unless params.empty?
56
+ end
57
+
58
+ def self.date_or_date_time(timezone_finder, separated_line) # :nodoc:
59
+ match = separated_line[:value].match(/(\d\d\d\d)(\d\d)(\d\d)((T?)((\d\d)(\d\d)(\d\d))(Z?))?/)
60
+ raise Exception.new("Invalid date") unless match
61
+ if match[5] == "T" # date-time
62
+ time = Time.utc(match[1].to_i, match[2].to_i, match[3].to_i, match[7].to_i, match[8].to_i, match[9].to_i)
63
+ parms = (separated_line[:params] ||{}).dup
64
+ if match[10] == "Z"
65
+ raise Exception.new("Invalid time, cannot combine Zulu with timezone reference") if parms[:tzid]
66
+ parms['TZID'] = "UTC"
67
+ end
68
+ PropertyValue::DateTime.new(timezone_finder, separated_line.merge(:params => parms))
69
+ else
70
+ PropertyValue::Date.new(timezone_finder, separated_line)
71
+ end
72
+ end
73
+
74
+ def self.date_or_date_time_or_period(timezone_finder, separated_line) #:nodoc:
75
+ if separated_line[:value].include?("/")
76
+ PropertyValue::Period.new(timezone_finder, separated_line)
77
+ else
78
+ date_or_date_time(timezone_finder, separated_line)
79
+ end
80
+ end
81
+
82
+ # def self.from_string(string) # :nodoc:
83
+ # new(nil, :value => string)
84
+ # end
85
+
86
+ def self.convert(timezone_finder, value) #:nodoc:
87
+ new(timezone_finder, :value => value)
88
+ end
89
+
90
+ # Determine if another object is equivalent to the receiver.
91
+ def ==(o)
92
+ if o.class == self.class
93
+ equality_value == o.equality_value
94
+ else
95
+ super
96
+ end
97
+ end
98
+
99
+ # Return the string value
100
+ def value
101
+ @value
102
+ end
103
+
104
+ def equality_value #:nodoc:
105
+ value
106
+ end
107
+
108
+ def visible_params # :nodoc:
109
+ params
110
+ end
111
+
112
+ def parms_string #:nodoc:
113
+ if (vp = visible_params) && !vp.empty?
114
+ # We only sort for testability reasons
115
+ vp.keys.sort.map {|key| ";#{key}=#{vp[key]}"}.join
116
+ else
117
+ ""
118
+ end
119
+ end
120
+
121
+ # Return a string representing the receiver in RFC 2445 format
122
+ def to_s #:nodoc:
123
+ "#{parms_string}:#{value}"
124
+ end
125
+
126
+ # return the ruby value
127
+ def ruby_value
128
+ self.value
129
+ end
130
+
131
+ def to_ri_cal_property_value #:nodoc:
132
+ self
133
+ end
134
+
135
+ def find_timezone(timezone_identifier) #:nodoc:
136
+ if timezone_finder
137
+ timezone_finder.find_timezone(timezone_identifier)
138
+ else
139
+ raise "Unable to find timezone with tzid #{timezone_identifier}"
140
+ end
141
+ end
142
+
143
+ def default_tzid #:nodoc:
144
+ if timezone_finder
145
+ timezone_finder.default_tzid
146
+ else
147
+ PropertyValue::DateTime.default_tzid
148
+ end
149
+ end
150
+
151
+ def tz_info_source? #:nodoc:
152
+ if timezone_finder
153
+ timezone_finder.tz_info_source?
154
+ else
155
+ true
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,27 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ class Array < PropertyValue # :nodoc:
6
+
7
+ def value=(val)
8
+ case val
9
+ when String
10
+ @value = val.split(",")
11
+ else
12
+ @value = val
13
+ end
14
+ end
15
+
16
+ def value
17
+ @value.join(",")
18
+ end
19
+
20
+ def self.convert(timezone_finder, ruby_object) # :nodoc:
21
+ self.new(timezone_finder, :value => ruby_object)
22
+ end
23
+
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,11 @@
1
+ module RiCal
2
+ class PropertyValue
3
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # RiCal::PropertyValue::CalAddress represents an icalendar CalAddress property value
6
+ # which is defined in
7
+ # RFC 2445 section 4.3.3 p 34
8
+ class CalAddress < PropertyValue
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,184 @@
1
+ require 'date'
2
+ module RiCal
3
+ class PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::CalAddress represents an icalendar Date property value
7
+ # which is defined in
8
+ # RFC 2445 section 4.3.4 p 34
9
+ class Date < PropertyValue
10
+
11
+ def self.valid_string?(string) #:nodoc:
12
+ string =~ /^\d{8}$/
13
+ end
14
+
15
+ # Returns the value of the reciever as an RFC 2445 iCalendar string
16
+ def value
17
+ if @date_time_value
18
+ @date_time_value.ical_date_str
19
+ else
20
+ nil
21
+ end
22
+ end
23
+
24
+ def to_ri_cal_zulu_date_time
25
+ self.to_ri_cal_date_time_value.to_ri_cal_zulu_date_time
26
+ end
27
+
28
+ # Set the value of the property to val
29
+ #
30
+ # val may be either:
31
+ #
32
+ # * A string which can be parsed as a DateTime
33
+ # * A Time instance
34
+ # * A Date instance
35
+ # * A DateTime instance
36
+ def value=(val)
37
+ case val
38
+ when nil
39
+ @date_time_value = nil
40
+ when String
41
+ @date_time_value = FastDateTime.from_date_time(::DateTime.parse(::DateTime.parse(val).strftime("%Y%m%d")))
42
+ when ::Time, ::Date, ::DateTime
43
+ @date_time_value = FastDateTime.from_date_time(::DateTime.parse(val.strftime("%Y%m%d")))
44
+ when FastDateTime
45
+ @date_time_value = val
46
+ end
47
+ end
48
+
49
+ # Nop to allow occurrence list to try to set it
50
+ def tzid=(val)#:nodoc:
51
+ end
52
+
53
+ def tzid #:nodoc:
54
+ nil
55
+ end
56
+
57
+ def visible_params #:nodoc:
58
+ {"VALUE" => "DATE"}.merge(params)
59
+ end
60
+
61
+ # Returns the year (including the century)
62
+ def year
63
+ @date_time_value.year
64
+ end
65
+
66
+ # Returns the month of the year (1..12)
67
+ def month
68
+ @date_time_value.month
69
+ end
70
+
71
+ # Returns the day of the month
72
+ def day
73
+ @date_time_value.day
74
+ end
75
+
76
+ # Returns the ruby representation a ::Date
77
+ def ruby_value
78
+ @date_time_value.date
79
+ end
80
+
81
+ alias_method :to_ri_cal_ruby_value, :ruby_value
82
+
83
+ # Return an instance of RiCal::PropertyValue::DateTime representing the start of this date
84
+ def to_ri_cal_date_time_value
85
+ PropertyValue::DateTime.new(timezone_finder, :value => @date_time_value)
86
+ end
87
+
88
+ # Return this date property
89
+ def to_ri_cal_date_value(timezone_finder = nil)
90
+ self
91
+ end
92
+
93
+ # Return the "Natural' property value for the date_property, in this case the date property itself."
94
+ def to_ri_cal_date_or_date_time_value
95
+ self
96
+ end
97
+
98
+ def for_parent(parent) #:nodoc:
99
+ if timezone_finder.nil?
100
+ @timezone_finder = parent
101
+ self
102
+ elsif parent == timezone_finder
103
+ self
104
+ else
105
+ Date.new(parent, :value => @date_time_value)
106
+ end
107
+ end
108
+
109
+ def advance(options) #:nodoc:
110
+ PropertyValue::Date.new(timezone_finder, :value => @date_time_value.advance(options), :params =>(params ? params.dup : nil) )
111
+ end
112
+
113
+ def change(options) #:nodoc:
114
+ PropertyValue::Date.new(timezone_finder,:value => @date_time_value.change(options), :params => (params ? params.dup : nil) )
115
+ end
116
+
117
+ def add_date_times_to(required_timezones) #:nodoc:
118
+ # Do nothing since dates don't have a timezone
119
+ end
120
+
121
+ # Return the difference between the receiver and other
122
+ #
123
+ # The parameter other should be either a RiCal::PropertyValue::Duration or a RiCal::PropertyValue::DateTime
124
+ #
125
+ # If other is a Duration, the result will be a DateTime, if it is a DateTime the result will be a Duration
126
+ def -(other)
127
+ other.subtract_from_date_time_value(to_ri_cal_date_time_value)
128
+ end
129
+
130
+ def subtract_from_date_time_value(date_time)
131
+ to_ri_cal_date_time_value.subtract_from_date_time_value(date_time)
132
+ end
133
+
134
+ # Return the sum of the receiver and duration
135
+ #
136
+ # The parameter other duration should be a RiCal::PropertyValue::Duration
137
+ #
138
+ # The result will be an RiCal::PropertyValue::DateTime
139
+ def +(duration)
140
+ duration.add_to_date_time_value(to_ri_cal_date_time_value)
141
+ end
142
+
143
+ # Delegate unknown messages to the wrappered Date instance.
144
+ # TODO: Is this really necessary?
145
+ def method_missing(selector, *args) #:nodoc:
146
+ @date_time_value.send(selector, *args)
147
+ end
148
+
149
+ # TODO: consider if this should be a period rather than a hash
150
+ def occurrence_period(default_duration) #:nodoc:
151
+ date_time = self.to_ri_cal_date_time_value
152
+ RiCal::OccurrencePeriod.new(date_time, date_time.advance(:hours => 24, :seconds => -1))
153
+ end
154
+
155
+ def start_of_day?
156
+ true
157
+ end
158
+
159
+ def to_floating_date_time_property
160
+ PropertyValue::DateTime.new(timezone_finder, :value => @date_time_value.ical_str)
161
+ end
162
+
163
+ def to_zulu_occurrence_range_start_time
164
+ to_floating_date_time_property.to_zulu_occurrence_range_start_time
165
+ end
166
+
167
+ def to_zulu_occurrence_range_finish_time
168
+ to_ri_cal_date_time_value.end_of_day.to_zulu_occurrence_range_finish_time
169
+ end
170
+
171
+ def to_finish_time
172
+ to_ri_cal_date_time_value.end_of_day.to_datetime
173
+ end
174
+
175
+ def for_occurrence(occurrence)
176
+ if occurrence.start_of_day?
177
+ occurrence.to_ri_cal_date_value(timezone_finder)
178
+ else
179
+ occurrence.for_parent(timezone_finder)
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,359 @@
1
+ require 'date'
2
+ module RiCal
3
+ class PropertyValue
4
+ #- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
5
+ #
6
+ # RiCal::PropertyValue::CalAddress represents an icalendar CalAddress property value
7
+ # which is defined in RFC 2445 section 4.3.5 pp 35-37
8
+ class DateTime < PropertyValue
9
+
10
+ Dir[File.dirname(__FILE__) + "/date_time/*.rb"].sort.each do |path|
11
+ require path
12
+ end
13
+
14
+ include Comparable
15
+ include AdditiveMethods
16
+ include TimezoneSupport
17
+ include TimeMachine
18
+
19
+ def self.or_date(parent, line) # :nodoc:
20
+ if /T/.match(line[:value] || "")
21
+ new(parent, line)
22
+ else
23
+ PropertyValue::Date.new(parent, line)
24
+ end
25
+ end
26
+
27
+ def self.valid_string?(string) #:nodoc:
28
+ string =~ /^\d{8}T\d{6}Z?$/
29
+ end
30
+
31
+ def self.default_tzid # :nodoc:
32
+ @default_tzid ||= "UTC"
33
+ end
34
+
35
+ def self.params_for_tzid(tzid) #:nodoc:
36
+ if tzid == :floating
37
+ {}
38
+ else
39
+ {'TZID' => tzid}
40
+ end
41
+ end
42
+
43
+ # Set the default tzid to be used when instantiating an instance from a ruby object
44
+ # see RiCal::PropertyValue::DateTime.from_time
45
+ #
46
+ # The parameter tzid is a string value to be used for the default tzid, a value of :floating will cause
47
+ # values with NO timezone to be produced, which will be interpreted by iCalendar as floating times
48
+ # i.e. they are interpreted in the timezone of each client. Floating times are typically used
49
+ # to represent events which are 'repeated' in the various time zones, like the first hour of the year.
50
+ def self.default_tzid=(tzid)
51
+ @default_tzid = tzid
52
+ end
53
+
54
+ def self.default_tzid_hash # :nodoc:
55
+ if default_tzid.to_s == 'none'
56
+ {}
57
+ else
58
+ {'TZID' => default_tzid}
59
+ end
60
+ end
61
+
62
+ def inspect # :nodoc:
63
+ "#{@date_time_value}:#{tzid}"
64
+ end
65
+
66
+ # Returns the value of the receiver as an RFC 2445 iCalendar string
67
+ def value
68
+ if @date_time_value
69
+ "#{@date_time_value.ical_str}#{tzid == "UTC" ? "Z" : ""}"
70
+ else
71
+ nil
72
+ end
73
+ end
74
+
75
+ def to_ri_cal_zulu_date_time
76
+ ZuluDateTime.new(nil, :value => self.utc.fast_date_tme)
77
+ end
78
+
79
+ def fast_date_tme # :nodoc:
80
+ @date_time_value
81
+ end
82
+
83
+ # Set the value of the property to val
84
+ #
85
+ # val may be either:
86
+ #
87
+ # * A string which can be parsed as a DateTime
88
+ # * A Time instance
89
+ # * A Date instance
90
+ # * A DateTime instance
91
+ def value=(val) # :nodoc:
92
+ case val
93
+ when nil
94
+ @date_time_value = nil
95
+ when String
96
+ @date_time_value = FastDateTime.from_date_time(::DateTime.parse(val))
97
+ if val =~/Z/
98
+ self.tzid = 'UTC'
99
+ else
100
+ @tzid ||= :floating
101
+ end
102
+ when FastDateTime
103
+ @date_time_value = val
104
+ when ::DateTime
105
+ @date_time_value = FastDateTime.from_date_time(val)
106
+ when ::Date, ::Time
107
+ @date_time_value = FastDateTime.from_date_time(::DateTime.parse(val.to_s))
108
+ end
109
+ reset_cached_values
110
+ end
111
+
112
+ # Extract the time and timezone identifier from an object used to set the value of a DATETIME property.
113
+ #
114
+ # If the object is a string it should be of the form [TZID=identifier:]
115
+ #
116
+ # Otherwise determine if the object acts like an activesupport enhanced time, and extract its timezone
117
+ # idenfifier if it has one.
118
+ #
119
+ def self.time_and_parameters(object)
120
+ parameters = {}
121
+ if ::String === object
122
+ object, parameters = self.time_and_parameters_from_string(object)
123
+ else
124
+ identifier = object.tzid rescue nil
125
+ parameters["TZID"] = identifier if identifier
126
+ end
127
+ [object, parameters]
128
+ end
129
+
130
+
131
+ def self.convert(timezone_finder, ruby_object) # :nodoc:
132
+ ruby_object.to_ri_cal_date_or_date_time_value(timezone_finder)
133
+ end
134
+
135
+ def self.from_string(string) # :nodoc:
136
+ if string.match(/Z$/)
137
+ new(nil, :value => string, :tzid => 'UTC')
138
+ else
139
+ new(nil, :value => string)
140
+ end
141
+ end
142
+
143
+ def for_parent(parent) #:nodoc:
144
+ if timezone_finder.nil?
145
+ @timezone_finder = parent
146
+ self
147
+ elsif parent == timezone_finder
148
+ self
149
+ else
150
+ DateTime.new(parent, :value => @date_time_value, :params => params, :tzid => tzid)
151
+ end
152
+ end
153
+
154
+ def visible_params # :nodoc:
155
+ result = {"VALUE" => "DATE-TIME"}.merge(params)
156
+ if has_local_timezone?
157
+ result['TZID'] = tzid
158
+ else
159
+ result.delete('TZID')
160
+ end
161
+ result
162
+ end
163
+
164
+ def params=(value) #:nodoc:
165
+ @params = value.dup
166
+ if params_timezone = @params['TZID']
167
+ self.tzid = @params['TZID']
168
+ end
169
+ end
170
+
171
+ # Return a Hash representing this properties parameters
172
+ def params
173
+ result = @params.dup
174
+ case tzid
175
+ when :floating, nil, "UTC"
176
+ result.delete('TZID')
177
+ else
178
+ result['TZID'] = tzid
179
+ end
180
+ result
181
+ end
182
+
183
+ # Compare the receiver with another object which must respond to the to_datetime message
184
+ # The comparison is done using the Ruby DateTime representations of the two objects
185
+ def <=>(other)
186
+ other.cmp_fast_date_time_value(@date_time_value)
187
+ end
188
+
189
+ def cmp_fast_date_time_value(other)
190
+ other <=> @date_time_value
191
+ end
192
+
193
+ # Determine if the receiver and other are in the same month
194
+ def in_same_month_as?(other)
195
+ [other.year, other.month] == [year, month]
196
+ end
197
+
198
+ def with_date_time_value(date_time_value)
199
+ PropertyValue::DateTime.new(
200
+ timezone_finder,
201
+ :value => date_time_value,
202
+ :params => (params),
203
+ :tzid => tzid
204
+ )
205
+ end
206
+
207
+ def nth_wday_in_month(n, which_wday) #:nodoc:
208
+ with_date_time_value(@date_time_value.nth_wday_in_month(n, which_wday))
209
+ end
210
+
211
+ def nth_wday_in_year(n, which_wday) #:nodoc:
212
+ with_date_time_value(@date_time_value.nth_wday_in_year(n, which_wday))
213
+ end
214
+
215
+ def self.civil(year, month, day, hour, min, sec, offset, start, params) #:nodoc:
216
+ PropertyValue::DateTime.new(
217
+ :value => ::DateTime.civil(year, month, day, hour, min, sec, offset, start),
218
+ :params =>(params ? params.dup : nil)
219
+ )
220
+ end
221
+
222
+ # Return the number of days in the month containing the receiver
223
+ def days_in_month
224
+ @date_time_value.days_in_month
225
+ end
226
+
227
+ # Determine if the receiver and another object are equivalent RiCal::PropertyValue::DateTime instances
228
+ def ==(other)
229
+ if self.class === other
230
+ self.value == other.value && self.visible_params == other.visible_params && self.tzid == other.tzid
231
+ else
232
+ super
233
+ end
234
+ end
235
+
236
+ # TODO: consider if this should be a period rather than a hash
237
+ def occurrence_period(default_duration) # :nodoc:
238
+ RiCal::OccurrencePeriod.new(self, (default_duration ? self + default_duration : nil))
239
+ end
240
+
241
+ # Return the year (including the century)
242
+ def year
243
+ @date_time_value.year
244
+ end
245
+
246
+ # Return the month of the year (1..12)
247
+ def month
248
+ @date_time_value.month
249
+ end
250
+
251
+ # Return the day of the month
252
+ def day
253
+ @date_time_value.day
254
+ end
255
+
256
+ alias_method :mday, :day
257
+
258
+ # Return the day of the week
259
+ def wday
260
+ @date_time_value.wday
261
+ end
262
+
263
+ # Return the hour
264
+ def hour
265
+ @date_time_value.hour
266
+ end
267
+
268
+ # Return the minute
269
+ def min
270
+ @date_time_value.min
271
+ end
272
+
273
+ # Return the second
274
+ def sec
275
+ @date_time_value.sec
276
+ end
277
+
278
+
279
+ # Return an RiCal::PropertyValue::DateTime representing the receiver.
280
+ def to_ri_cal_date_time_value(timezone=nil)
281
+ for_parent(timezone)
282
+ end
283
+
284
+ def iso_year_and_week_one_start(wkst) #:nodoc:
285
+ @date_time_value.iso_year_and_week_one_start(wkst)
286
+ end
287
+
288
+ def iso_weeks_in_year(wkst) #:nodoc:
289
+ @date_time_value.iso_weeks_in_year(wkst) #:nodoc:
290
+ end
291
+
292
+ # Return the "Natural' property value for the receover, in this case the receiver itself."
293
+ def to_ri_cal_date_or_date_time_value(timezone_finder = nil) #:nodoc:
294
+ self.for_parent(timezone_finder)
295
+ end
296
+
297
+ # Return a Date property for this DateTime
298
+ def to_ri_cal_date_value(timezone_finder=nil)
299
+ PropertyValue::Date.new(timezone_finder, :value => @date_time_value.ical_date_str)
300
+ end
301
+
302
+ # Return the Ruby DateTime representation of the receiver
303
+ def to_datetime #:nodoc:
304
+ @date_time_value.to_datetime
305
+ end
306
+
307
+ # Returns a ruby DateTime object representing the receiver.
308
+ def ruby_value
309
+ if has_valid_tzinfo_tzid? && RiCal::TimeWithZone && tz_info_source?
310
+ RiCal::TimeWithZone.new(utc.to_datetime, ::Time.__send__(:get_zone, @tzid))
311
+ else
312
+ ::DateTime.civil(year, month, day, hour, min, sec, rational_tz_offset).set_tzid(@tzid)
313
+ end
314
+ end
315
+
316
+ alias_method :to_ri_cal_ruby_value, :to_datetime
317
+ alias_method :to_finish_time, :ruby_value
318
+
319
+ def to_zulu_time
320
+ utc.to_datetime
321
+ end
322
+
323
+ # If a time is floating, then the utc of it's start time may actually be as early
324
+ # as 12 hours earlier if the occurrence is being viewed in a time zone just west
325
+ # of the International Date Line
326
+ def to_zulu_occurrence_range_start_time
327
+ if floating?
328
+ @date_time_value.advance(:hours => -12, :offset => 0).to_datetime
329
+ else
330
+ to_zulu_time
331
+ end
332
+ end
333
+
334
+
335
+ # If a time is floating, then the utc of it's start time may actually be as early
336
+ # as 12 hours later if the occurrence is being viewed in a time zone just east
337
+ # of the International Date Line
338
+ def to_zulu_occurrence_range_finish_time
339
+ if floating?
340
+ utc.advance(:hours => 12).to_datetime
341
+ else
342
+ to_zulu_time
343
+ end
344
+ end
345
+
346
+ def add_date_times_to(required_timezones) #:nodoc:
347
+ required_timezones.add_datetime(self, tzid) if has_local_timezone?
348
+ end
349
+
350
+ def start_of_day?
351
+ [hour, min, sec] == [0,0,0]
352
+ end
353
+
354
+ def for_occurrence(occurrence)
355
+ occurrence.to_ri_cal_date_time_value(timezone_finder)
356
+ end
357
+ end
358
+ end
359
+ end