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.
- data/History.txt +402 -0
- data/Manifest.txt +161 -0
- data/README.txt +410 -0
- data/Rakefile +69 -0
- data/VERSION +1 -0
- data/bin/ri_cal +8 -0
- data/component_attributes/alarm.yml +10 -0
- data/component_attributes/calendar.yml +4 -0
- data/component_attributes/component_property_defs.yml +180 -0
- data/component_attributes/event.yml +45 -0
- data/component_attributes/freebusy.yml +16 -0
- data/component_attributes/journal.yml +35 -0
- data/component_attributes/timezone.yml +3 -0
- data/component_attributes/timezone_period.yml +11 -0
- data/component_attributes/todo.yml +46 -0
- data/copyrights.txt +1 -0
- data/docs/draft-ietf-calsify-2446bis-08.txt +7280 -0
- data/docs/draft-ietf-calsify-rfc2445bis-09.txt +10416 -0
- data/docs/incrementers.txt +7 -0
- data/docs/rfc2445.pdf +0 -0
- data/lib/ri_cal.rb +187 -0
- data/lib/ri_cal/component.rb +256 -0
- data/lib/ri_cal/component/alarm.rb +19 -0
- data/lib/ri_cal/component/calendar.rb +257 -0
- data/lib/ri_cal/component/event.rb +58 -0
- data/lib/ri_cal/component/freebusy.rb +16 -0
- data/lib/ri_cal/component/journal.rb +27 -0
- data/lib/ri_cal/component/non_standard.rb +33 -0
- data/lib/ri_cal/component/t_z_info_timezone.rb +153 -0
- data/lib/ri_cal/component/timezone.rb +197 -0
- data/lib/ri_cal/component/timezone/daylight_period.rb +25 -0
- data/lib/ri_cal/component/timezone/standard_period.rb +23 -0
- data/lib/ri_cal/component/timezone/timezone_period.rb +76 -0
- data/lib/ri_cal/component/todo.rb +42 -0
- data/lib/ri_cal/core_extensions.rb +11 -0
- data/lib/ri_cal/core_extensions/array.rb +7 -0
- data/lib/ri_cal/core_extensions/array/conversions.rb +15 -0
- data/lib/ri_cal/core_extensions/date.rb +13 -0
- data/lib/ri_cal/core_extensions/date/conversions.rb +56 -0
- data/lib/ri_cal/core_extensions/date_time.rb +15 -0
- data/lib/ri_cal/core_extensions/date_time/conversions.rb +50 -0
- data/lib/ri_cal/core_extensions/object.rb +8 -0
- data/lib/ri_cal/core_extensions/object/conversions.rb +20 -0
- data/lib/ri_cal/core_extensions/string.rb +8 -0
- data/lib/ri_cal/core_extensions/string/conversions.rb +57 -0
- data/lib/ri_cal/core_extensions/time.rb +14 -0
- data/lib/ri_cal/core_extensions/time/calculations.rb +153 -0
- data/lib/ri_cal/core_extensions/time/conversions.rb +42 -0
- data/lib/ri_cal/core_extensions/time/tzid_access.rb +50 -0
- data/lib/ri_cal/core_extensions/time/week_day_predicates.rb +55 -0
- data/lib/ri_cal/fast_date_time.rb +234 -0
- data/lib/ri_cal/floating_timezone.rb +32 -0
- data/lib/ri_cal/invalid_property_value.rb +8 -0
- data/lib/ri_cal/invalid_timezone_identifier.rb +20 -0
- data/lib/ri_cal/occurrence_enumerator.rb +265 -0
- data/lib/ri_cal/occurrence_period.rb +17 -0
- data/lib/ri_cal/parser.rb +145 -0
- data/lib/ri_cal/properties.rb +12 -0
- data/lib/ri_cal/properties/alarm.rb +390 -0
- data/lib/ri_cal/properties/calendar.rb +164 -0
- data/lib/ri_cal/properties/event.rb +1523 -0
- data/lib/ri_cal/properties/freebusy.rb +593 -0
- data/lib/ri_cal/properties/journal.rb +1237 -0
- data/lib/ri_cal/properties/timezone.rb +150 -0
- data/lib/ri_cal/properties/timezone_period.rb +416 -0
- data/lib/ri_cal/properties/todo.rb +1559 -0
- data/lib/ri_cal/property_value.rb +159 -0
- data/lib/ri_cal/property_value/array.rb +27 -0
- data/lib/ri_cal/property_value/cal_address.rb +11 -0
- data/lib/ri_cal/property_value/date.rb +184 -0
- data/lib/ri_cal/property_value/date_time.rb +359 -0
- data/lib/ri_cal/property_value/date_time/additive_methods.rb +44 -0
- data/lib/ri_cal/property_value/date_time/time_machine.rb +159 -0
- data/lib/ri_cal/property_value/date_time/timezone_support.rb +100 -0
- data/lib/ri_cal/property_value/duration.rb +110 -0
- data/lib/ri_cal/property_value/geo.rb +11 -0
- data/lib/ri_cal/property_value/integer.rb +12 -0
- data/lib/ri_cal/property_value/occurrence_list.rb +144 -0
- data/lib/ri_cal/property_value/period.rb +86 -0
- data/lib/ri_cal/property_value/recurrence_rule.rb +154 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumeration_support_methods.rb +100 -0
- data/lib/ri_cal/property_value/recurrence_rule/enumerator.rb +79 -0
- data/lib/ri_cal/property_value/recurrence_rule/initialization_methods.rb +148 -0
- data/lib/ri_cal/property_value/recurrence_rule/negative_setpos_enumerator.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/numbered_span.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer.rb +135 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_day_incrementer.rb +86 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_hour_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_minute_incrementer.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_month_incrementer.rb +52 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_monthday_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_numbered_day_incrementer.rb +38 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_second_incrementer.rb +32 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_weekno_incrementer.rb +69 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/by_yearday_incrementer.rb +31 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/daily_incrementer.rb +28 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/frequency_incrementer.rb +80 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/hourly_incrementer.rb +23 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/list_incrementer.rb +106 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/minutely_incrementer.rb +23 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/monthly_incrementer.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/null_sub_cycle_incrementer.rb +43 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/secondly_incrementer.rb +28 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/weekly_incrementer.rb +37 -0
- data/lib/ri_cal/property_value/recurrence_rule/occurrence_incrementer/yearly_incrementer.rb +57 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_day.rb +131 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_month_day.rb +64 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_numbered_week.rb +33 -0
- data/lib/ri_cal/property_value/recurrence_rule/recurring_year_day.rb +53 -0
- data/lib/ri_cal/property_value/recurrence_rule/time_manipulation.rb +42 -0
- data/lib/ri_cal/property_value/recurrence_rule/validations.rb +125 -0
- data/lib/ri_cal/property_value/text.rb +44 -0
- data/lib/ri_cal/property_value/uri.rb +11 -0
- data/lib/ri_cal/property_value/utc_offset.rb +33 -0
- data/lib/ri_cal/property_value/zulu_date_time.rb +34 -0
- data/lib/ri_cal/required_timezones.rb +55 -0
- data/parked_specs/ri_cal/claudio_a_bug_spec.rb +100 -0
- data/performance/empty_propval/subject.rb +43 -0
- data/performance/paris_eastern/subject.rb +90 -0
- data/performance/penultimate_weekday/subject.rb +15 -0
- data/performance/psm_big_enum/ical.ics +3171 -0
- data/performance/psm_big_enum/subject.rb +16 -0
- data/performance/utah_cycling/subject.rb +55 -0
- data/ri_cal.gemspec +244 -0
- data/script/benchmark_subject +23 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/profile_subject +29 -0
- data/script/txt2html +71 -0
- data/spec/ri_cal/bugreports_spec.rb +276 -0
- data/spec/ri_cal/component/alarm_spec.rb +12 -0
- data/spec/ri_cal/component/calendar_spec.rb +88 -0
- data/spec/ri_cal/component/event_spec.rb +735 -0
- data/spec/ri_cal/component/freebusy_spec.rb +12 -0
- data/spec/ri_cal/component/journal_spec.rb +37 -0
- data/spec/ri_cal/component/t_z_info_timezone_spec.rb +60 -0
- data/spec/ri_cal/component/timezone_spec.rb +236 -0
- data/spec/ri_cal/component/todo_spec.rb +112 -0
- data/spec/ri_cal/component_spec.rb +224 -0
- data/spec/ri_cal/core_extensions/string/conversions_spec.rb +78 -0
- data/spec/ri_cal/core_extensions/time/calculations_spec.rb +188 -0
- data/spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb +45 -0
- data/spec/ri_cal/fast_date_time_spec.rb +77 -0
- data/spec/ri_cal/inf_loop_spec.rb +78 -0
- data/spec/ri_cal/occurrence_enumerator_spec.rb +611 -0
- data/spec/ri_cal/parser_spec.rb +337 -0
- data/spec/ri_cal/property_value/date_spec.rb +53 -0
- data/spec/ri_cal/property_value/date_time_spec.rb +383 -0
- data/spec/ri_cal/property_value/duration_spec.rb +126 -0
- data/spec/ri_cal/property_value/occurrence_list_spec.rb +72 -0
- data/spec/ri_cal/property_value/period_spec.rb +63 -0
- data/spec/ri_cal/property_value/recurrence_rule/recurring_year_day_spec.rb +21 -0
- data/spec/ri_cal/property_value/recurrence_rule_spec.rb +1814 -0
- data/spec/ri_cal/property_value/text_spec.rb +25 -0
- data/spec/ri_cal/property_value/utc_offset_spec.rb +48 -0
- data/spec/ri_cal/property_value_spec.rb +125 -0
- data/spec/ri_cal/required_timezones_spec.rb +67 -0
- data/spec/ri_cal_spec.rb +53 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +50 -0
- data/tasks/gem_loader/load_active_support.rb +3 -0
- data/tasks/gem_loader/load_tzinfo_gem.rb +2 -0
- data/tasks/ri_cal.rake +412 -0
- data/tasks/spec.rake +102 -0
- metadata +255 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module RiCal
|
|
2
|
+
class PropertyValue
|
|
3
|
+
class RecurrenceRule < PropertyValue
|
|
4
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
5
|
+
#
|
|
6
|
+
module TimeManipulation #:nodoc:
|
|
7
|
+
|
|
8
|
+
def advance_day(date_time)
|
|
9
|
+
date_time.advance(:days => 1)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def first_hour_of_day(date_time)
|
|
13
|
+
date_time.change(:hour => 0)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def advance_week(date_time)
|
|
17
|
+
date_time.advance(:days => 7)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def first_day_of_week(wkst_day, date_time)
|
|
21
|
+
date_time.at_start_of_week_with_wkst(wkst_day)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def advance_month(date_time)
|
|
25
|
+
date_time.advance(:months => 1)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def first_day_of_month(date_time)
|
|
29
|
+
date_time.change(:day => 1)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def advance_year(date_time)
|
|
33
|
+
date_time.advance(:years => 1)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def first_day_of_year(date_time)
|
|
37
|
+
date_time.change(:month => 1, :day => 1)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
module RiCal
|
|
2
|
+
class PropertyValue
|
|
3
|
+
class RecurrenceRule < PropertyValue
|
|
4
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
5
|
+
#
|
|
6
|
+
module Validations #:nodoc:
|
|
7
|
+
# Validate that the parameters of the reciever conform to RFC 2445
|
|
8
|
+
# If errors are found they will be added to the receivers errors
|
|
9
|
+
#
|
|
10
|
+
# Whenever any of the parameters are set, e.g. with:
|
|
11
|
+
# recurrence_rule.count = 2
|
|
12
|
+
# the errors will be reset
|
|
13
|
+
def valid?
|
|
14
|
+
validate if @errors.nil?
|
|
15
|
+
errors.empty?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Return any errors found during validation
|
|
19
|
+
# See #valid?
|
|
20
|
+
def errors
|
|
21
|
+
@errors ||= []
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def reset_errors # :nodoc:
|
|
25
|
+
@errors = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Used by #valid? to validate that the parameters of the reciever conform to RFC 2445
|
|
29
|
+
# If errors are found they will be added to the receivers errors
|
|
30
|
+
#
|
|
31
|
+
# Whenever any of the parameters are set, e.g. with:
|
|
32
|
+
# recurrence_rule.count = 2
|
|
33
|
+
# the errors will be reset
|
|
34
|
+
def validate
|
|
35
|
+
@errors = []
|
|
36
|
+
validate_termination
|
|
37
|
+
validate_freq
|
|
38
|
+
validate_interval
|
|
39
|
+
validate_int_by_list(:bysecond, (0..59))
|
|
40
|
+
validate_int_by_list(:byminute, (0..59))
|
|
41
|
+
validate_int_by_list(:byhour, (0..23))
|
|
42
|
+
validate_int_by_list(:bymonth, (1..12))
|
|
43
|
+
validate_bysetpos
|
|
44
|
+
validate_byday_list
|
|
45
|
+
validate_bymonthday_list
|
|
46
|
+
validate_byyearday_list
|
|
47
|
+
validate_byweekno_list
|
|
48
|
+
validate_wkst
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def validate_termination
|
|
52
|
+
errors << "COUNT and UNTIL cannot both be specified" if @count && @until
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def validate_freq
|
|
56
|
+
if @freq
|
|
57
|
+
unless %w{
|
|
58
|
+
SECONDLY MINUTELY HOURLY DAILY
|
|
59
|
+
WEEKLY MONTHLY YEARLY
|
|
60
|
+
}.include?(@freq.upcase)
|
|
61
|
+
errors << "Invalid frequency '#{@freq}'"
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
errors << "RecurrenceRule must have a value for FREQ"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def validate_interval
|
|
69
|
+
if @interval
|
|
70
|
+
errors << "interval must be a positive integer" unless @interval > 0
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def validate_wkst
|
|
75
|
+
errors << "#{wkst.inspect} is invalid for wkst" unless %w{MO TU WE TH FR SA SU}.include?(wkst)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def validate_int_by_list(which, test)
|
|
79
|
+
vals = by_list[which] || []
|
|
80
|
+
vals.each do |val|
|
|
81
|
+
errors << "#{val} is invalid for #{which}" unless test === val
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def validate_bysetpos
|
|
86
|
+
vals = by_list[:bysetpos] || []
|
|
87
|
+
vals.each do |val|
|
|
88
|
+
errors << "#{val} is invalid for bysetpos" unless (-366..-1) === val || (1..366) === val
|
|
89
|
+
end
|
|
90
|
+
unless vals.empty?
|
|
91
|
+
errors << "bysetpos cannot be used without another by_xxx rule part" unless by_list.length > 1
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def validate_byday_list
|
|
96
|
+
days = by_list[:byday] || []
|
|
97
|
+
days.each do |day|
|
|
98
|
+
errors << "#{day.source.inspect} is not a valid day" unless day.valid?
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def validate_bymonthday_list
|
|
103
|
+
days = by_list[:bymonthday] || []
|
|
104
|
+
days.each do |day|
|
|
105
|
+
errors << "#{day.source.inspect} is not a valid month day" unless day.valid?
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def validate_byyearday_list
|
|
110
|
+
days = by_list[:byyearday] || []
|
|
111
|
+
days.each do |day|
|
|
112
|
+
errors << "#{day.source.inspect} is not a valid year day" unless day.valid?
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def validate_byweekno_list
|
|
117
|
+
days = by_list[:byweekno] || []
|
|
118
|
+
days.each do |day|
|
|
119
|
+
errors << "#{day.source.inspect} is not a valid week number" unless day.valid?
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
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::Text represents an icalendar Text property value
|
|
6
|
+
# which is defined in
|
|
7
|
+
# rfc 2445 section 4.3.11 pp 45-46
|
|
8
|
+
class Text < PropertyValue
|
|
9
|
+
|
|
10
|
+
# Return the string value of the receiver
|
|
11
|
+
def ruby_value
|
|
12
|
+
if value
|
|
13
|
+
value.gsub(/\\[;,nN\\]/) {|match|
|
|
14
|
+
case match[1,1]
|
|
15
|
+
when /[,;\\]/
|
|
16
|
+
match[1,1]
|
|
17
|
+
when 'n', 'N'
|
|
18
|
+
"\n"
|
|
19
|
+
else
|
|
20
|
+
match
|
|
21
|
+
end
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def to_ri_cal_text_property
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.convert(parent, string) #:nodoc:
|
|
33
|
+
ical_str = string.gsub(/\n\r?|\r\n?|,|;|\\/) {|match|
|
|
34
|
+
if ["\n", "\r", "\n\r", "\r\n"].include?(match)
|
|
35
|
+
'\\n'
|
|
36
|
+
else
|
|
37
|
+
"\\#{match}"
|
|
38
|
+
end
|
|
39
|
+
}
|
|
40
|
+
self.new(parent, :value => ical_str)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
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::Uri represents an icalendar Uri property value
|
|
6
|
+
# which is defined in
|
|
7
|
+
# rfc 2445 section 4.8.4.6 p 110
|
|
8
|
+
class Uri < PropertyValue
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
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 UtcOffset < PropertyValue # :nodoc:
|
|
6
|
+
attr_accessor :sign, :hours, :minutes, :seconds
|
|
7
|
+
|
|
8
|
+
def value=(string)
|
|
9
|
+
@value = string
|
|
10
|
+
parse_match = /([+-])(\d\d)(\d\d)(\d\d)?/.match(string)
|
|
11
|
+
if parse_match
|
|
12
|
+
@sign = parse_match[1] == "+" ? 1 : -1
|
|
13
|
+
@hours = parse_match[2].to_i
|
|
14
|
+
@minutes = parse_match[3].to_i
|
|
15
|
+
@seconds = parse_match[4].to_i || 0
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_seconds
|
|
20
|
+
@sign * ((((hours*60) + minutes) * 60) + seconds)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def add_to_date_time_value(date_time_value)
|
|
24
|
+
date_time_value.advance(:hours => sign * hours, :minutes => sign * minutes, :seconds => sign * minutes)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def subtract_from_date_time_value(date_time_value)
|
|
28
|
+
signum = -1 * sign
|
|
29
|
+
date_time_value.advance(:hours => signum * hours, :minutes => signum * minutes, :seconds => signum * minutes)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
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 ZuluDateTime < PropertyValue::DateTime
|
|
9
|
+
|
|
10
|
+
def tzid
|
|
11
|
+
"UTC"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def value=(val) # :nodoc:
|
|
15
|
+
if DateTime === val
|
|
16
|
+
@date_time_value = val
|
|
17
|
+
else
|
|
18
|
+
super(val)
|
|
19
|
+
end
|
|
20
|
+
@date_time_value = @date_time_value.utc if @date_time_value
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_ri_cal_zulu_date_time
|
|
24
|
+
self
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.convert(timezone_finder, ruby_object) # :nodoc:
|
|
28
|
+
result = super
|
|
29
|
+
result.to_ri_cal_zulu_date_time
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module RiCal
|
|
2
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
3
|
+
#
|
|
4
|
+
# RequireTimezones collects the timezones used by a given calendar component or set of calendar components
|
|
5
|
+
# For each timezone we collect it's id, and the earliest and latest times which reference the zone
|
|
6
|
+
class RequiredTimezones #:nodoc:
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# A required timezone represents a single timezone and the earliest and latest times which reference it.
|
|
10
|
+
class RequiredTimezone #:nodoc:
|
|
11
|
+
|
|
12
|
+
attr_reader :first_time, :last_time, :timezone
|
|
13
|
+
|
|
14
|
+
def initialize(tzid)
|
|
15
|
+
@timezone = RiCal::Component::TZInfoTimezone.new(TZInfo::Timezone.get(tzid))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def tzid
|
|
19
|
+
@timezone.identifier
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def add_datetime(date_time)
|
|
23
|
+
if @first_time
|
|
24
|
+
@first_time = date_time if date_time < @first_time
|
|
25
|
+
else
|
|
26
|
+
@first_time = date_time
|
|
27
|
+
end
|
|
28
|
+
if @last_time
|
|
29
|
+
@last_time = date_time if date_time > @last_time
|
|
30
|
+
else
|
|
31
|
+
@last_time = date_time
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def required_timezones
|
|
37
|
+
@required_zones ||= {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def required_zones
|
|
41
|
+
required_timezones.values
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def export_to(export_stream)
|
|
45
|
+
required_zones.each do |z|
|
|
46
|
+
tzinfo_timezone =z.timezone
|
|
47
|
+
tzinfo_timezone.export_local_to(export_stream, z.first_time, z.last_time)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def add_datetime(date_time, tzid)
|
|
52
|
+
(required_timezones[tzid] ||= RequiredTimezone.new(tzid)).add_datetime(date_time)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#- ©2009 Rick DeNatale, All rights reserved. Refer to the file README.txt for the license
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require File.join(File.dirname(__FILE__), %w[.. spec_helper])
|
|
5
|
+
|
|
6
|
+
describe "RiCal TimeZone problem" do
|
|
7
|
+
it "should keep the original calendar timezone intact" do
|
|
8
|
+
original_calendar = RiCal.parse_string(original_calendar_contents).first
|
|
9
|
+
puts "*"*40
|
|
10
|
+
puts original_calendar
|
|
11
|
+
puts "*"*40
|
|
12
|
+
reply_calendar = reply original_calendar
|
|
13
|
+
#reply_calendar = RiCal.parse_string(reply_calendar_contents).first
|
|
14
|
+
puts reply_calendar
|
|
15
|
+
puts "*"*40
|
|
16
|
+
|
|
17
|
+
original_event = original_calendar.events.first
|
|
18
|
+
reply_event = reply_calendar.events.first
|
|
19
|
+
reply_event.start_time.to_s.should == original_event.start_time.to_s
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# This is the calendar contents generated when we send the message >>#reply to the original calendar.
|
|
26
|
+
def reply_calendar_contents
|
|
27
|
+
"BEGIN:VCALENDAR
|
|
28
|
+
PRODID;X-RICAL-TZSOURCE=TZINFO:-//com.denhaven2/NONSGML ri_cal gem//EN
|
|
29
|
+
CALSCALE:GREGORIAN
|
|
30
|
+
VERSION:2.0
|
|
31
|
+
METHOD:REPLY
|
|
32
|
+
BEGIN:VTIMEZONE
|
|
33
|
+
TZID;X-RICAL-TZSOURCE=TZINFO:America/Argentina/Buenos_Aires
|
|
34
|
+
BEGIN:STANDARD
|
|
35
|
+
DTSTART:20090314T230000
|
|
36
|
+
RDATE:20090314T230000
|
|
37
|
+
TZOFFSETFROM:-0200
|
|
38
|
+
TZOFFSETTO:-0300
|
|
39
|
+
TZNAME:ART
|
|
40
|
+
END:STANDARD
|
|
41
|
+
END:VTIMEZONE
|
|
42
|
+
BEGIN:VEVENT
|
|
43
|
+
CREATED;VALUE=DATE-TIME:20091217T155557Z
|
|
44
|
+
DTEND;TZID=America/Argentina/Buenos_Aires;VALUE=DATE-TIME:20091227T010000
|
|
45
|
+
STATUS:CONFIRMED
|
|
46
|
+
LAST-MODIFIED;TZID=America/Argentina/Buenos_Aires;VALUE=DATE-TIME:2009121
|
|
47
|
+
7T173400
|
|
48
|
+
DTSTART;TZID=America/Argentina/Buenos_Aires;VALUE=DATE-TIME:20091227T0000
|
|
49
|
+
00
|
|
50
|
+
ATTENDEE;CN=PoketyPoke;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPANT:barbarazopp
|
|
51
|
+
o@gmail.com
|
|
52
|
+
UID:1693CA3B-C528-4E5A-87FB-CDFAEC0EC662
|
|
53
|
+
ORGANIZER:mailto:nasif.lucas@gmail.com
|
|
54
|
+
DESCRIPTION:
|
|
55
|
+
SUMMARY:testing.. Event
|
|
56
|
+
SEQUENCE:3
|
|
57
|
+
LOCATION:
|
|
58
|
+
END:VEVENT
|
|
59
|
+
END:VCALENDAR".strip
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def original_calendar_contents
|
|
63
|
+
"BEGIN:VCALENDAR\nPRODID:-//Apple Inc.//iCal 4.0.1//EN\nCALSCALE:GREGORIAN\nVERSION:2.0\nMETHOD:REQUEST\nBEGIN:VEVENT\nTRANSP:TRANSPARENT\nDTSTAMP;VALUE=DATE-TIME:20091217T155800Z\nCREATED;VALUE=DATE-TIME:20091217T155557Z\nDTEND;TZID=America/Argentina/Buenos_Aires;VALUE=DATE-TIME:20091227T010000\nDTSTART;TZID=America/Argentina/Buenos_Aires;VALUE=DATE-TIME:20091227T0000\n 00\nATTENDEE;CN=Lucas Nasif;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:nasif.\n lucas@gmail.com\nATTENDEE;CN=barbarazoppo@gmail.com;CUTYPE=INDIVIDUAL;EMAIL=barbarazoppo@g\n mail.com;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;RSVP=TRUE:mailto:bar\n barazoppo@gmail.com\nUID:1693CA3B-C528-4E5A-87FB-CDFAEC0EC662\nORGANIZER;CN=Lucas Nasif:mailto:nasif.lucas@gmail.com\nSUMMARY:testing.. Event\nSEQUENCE:3\nEND:VEVENT\nBEGIN:VTIMEZONE\nTZID:America/Argentina/Buenos_Aires\nBEGIN:STANDARD\nTZOFFSETTO:-0300\nDTSTART;VALUE=DATE-TIME:20080316T000000\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=3SU\nTZOFFSETFROM:-0200\nTZNAME:GMT-03:00\nEND:STANDARD\nBEGIN:DAYLIGHT\nTZOFFSETTO:-0200\nDTSTART;VALUE=DATE-TIME:20081019T000000\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=3SU\nTZOFFSETFROM:-0300\nTZNAME:GMT-02:00\nEND:DAYLIGHT\nEND:VTIMEZONE\nEND:VCALENDAR\n".strip
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def reply(original_calendar)
|
|
67
|
+
icalendar = RiCal.Calendar do | calendar |
|
|
68
|
+
|
|
69
|
+
calendar.icalendar_method = "REPLY"
|
|
70
|
+
calendar.default_tzid = original_calendar.default_tzid
|
|
71
|
+
|
|
72
|
+
calendar.event do | event |
|
|
73
|
+
|
|
74
|
+
original_event = original_calendar.events.first
|
|
75
|
+
event.created = original_event.created
|
|
76
|
+
event.dtstart = original_event.start_time
|
|
77
|
+
event.dtend = original_event.finish_time
|
|
78
|
+
event.organizer = original_event.organizer
|
|
79
|
+
event.location = original_event.location.to_s
|
|
80
|
+
event.uid_property = original_event.uid_property
|
|
81
|
+
|
|
82
|
+
options = {'ROLE' => 'REQ-PARTICIPANT',
|
|
83
|
+
'PARTSTAT' => 'ACCEPTED',
|
|
84
|
+
'CN' => 'SomeName...'}
|
|
85
|
+
|
|
86
|
+
attendee_property = RiCal::PropertyValue::CalAddress.new(nil,
|
|
87
|
+
:value => @attendant,
|
|
88
|
+
:params => options)
|
|
89
|
+
|
|
90
|
+
event.attendee_property = attendee_property
|
|
91
|
+
event.description = original_event.description.to_s
|
|
92
|
+
event.summary = original_event.summary.to_s
|
|
93
|
+
|
|
94
|
+
event.sequence_property = original_event.sequence_property
|
|
95
|
+
event.status = "CONFIRMED"
|
|
96
|
+
event.last_modified = DateTime.parse(Time.now.to_s)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
icalendar
|
|
100
|
+
end
|