ri_cal 0.5.3 → 0.6.0

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.
@@ -1,3 +1,14 @@
1
+ === 0.6.0 - 5 June 2009
2
+ Time for a minor version bump.
3
+ * Improved overall enumeration performance bypassing most of the effects of the poor performance of Ruby's DateTime class.
4
+ * Added a framework for performance monitoring during development.
5
+ - New performance directory with subdirs for code to be monitored for performance (subjects)
6
+ - New script files:
7
+ script/benchmark_subject runs a ruby benchmark on one performance subject
8
+ script/profile_subject runs ruby-prof for a subject and puts a kcachegrind compatible calltree in the performance_data directory.
9
+ - New rake tasks:
10
+ performance:benchmark runs benchmarks against all subjects in the performance directory and produces a consolidated output file in performance_data/benchmarks.out
11
+ performance:profile runs script/profile_subject against all subjects in the performance directory.
1
12
  === 0.5.3 - 1 June, 2009
2
13
  * Improved performance of time zone enumeration, TimeZonePeriod now caches occurrences
3
14
  * Added a profiling directory which contains ruby programs which benchmark and/or profile performance
@@ -46,6 +46,7 @@ lib/ri_cal/core_extensions/time/calculations.rb
46
46
  lib/ri_cal/core_extensions/time/conversions.rb
47
47
  lib/ri_cal/core_extensions/time/tzid_access.rb
48
48
  lib/ri_cal/core_extensions/time/week_day_predicates.rb
49
+ lib/ri_cal/fast_date_time.rb
49
50
  lib/ri_cal/floating_timezone.rb
50
51
  lib/ri_cal/invalid_property_value.rb
51
52
  lib/ri_cal/invalid_timezone_identifer.rb
@@ -89,15 +90,18 @@ lib/ri_cal/property_value/text.rb
89
90
  lib/ri_cal/property_value/uri.rb
90
91
  lib/ri_cal/property_value/utc_offset.rb
91
92
  lib/ri_cal/required_timezones.rb
92
- profiling/ical_files/profile3.ics
93
- profiling/profile1.rb
94
- profiling/profile2.rb
95
- profiling/profile3.rb
93
+ performance/paris_eastern/subject.rb
94
+ performance/penultimate_weekday/subject.rb
95
+ performance/psm_big_enum/ical.ics
96
+ performance/psm_big_enum/subject.rb
97
+ performance/utah_cycling/subject.rb
96
98
  ri_cal.gemspec
97
99
  sample_ical_files/from_ical_dot_app/test1.ics
100
+ script/benchmark_subject
98
101
  script/console
99
102
  script/destroy
100
103
  script/generate
104
+ script/profile_subject
101
105
  script/txt2html
102
106
  spec/ri_cal/component/alarm_spec.rb
103
107
  spec/ri_cal/component/calendar_spec.rb
@@ -111,6 +115,7 @@ spec/ri_cal/component_spec.rb
111
115
  spec/ri_cal/core_extensions/string/conversions_spec.rb
112
116
  spec/ri_cal/core_extensions/time/calculations_spec.rb
113
117
  spec/ri_cal/core_extensions/time/week_day_predicates_spec.rb
118
+ spec/ri_cal/fast_date_time_spec.rb
114
119
  spec/ri_cal/occurrence_enumerator_spec.rb
115
120
  spec/ri_cal/parser_spec.rb
116
121
  spec/ri_cal/property_value/date_spec.rb
data/README.txt CHANGED
@@ -348,12 +348,9 @@ or by a recent(>= 2.2) version of the ActiveSupport gem which is part of Ruby on
348
348
 
349
349
  === From github
350
350
 
351
- #TODO: publish to github
352
-
353
351
  ==== As a Gem
354
352
 
355
- #TODO: add the gem source info for github
356
- sudo gem install ????? --source http://github.com/????
353
+ sudo gem install rubyredrick-ri_cal --source http://gems.github.com/
357
354
 
358
355
  ==== From source
359
356
 
@@ -14,7 +14,7 @@ module RiCal
14
14
  autoload :OccurrenceEnumerator, "#{my_dir}/ri_cal/occurrence_enumerator.rb"
15
15
 
16
16
  # :stopdoc:
17
- VERSION = '0.5.3'
17
+ VERSION = '0.6.0'
18
18
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
19
19
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
20
20
 
@@ -140,7 +140,6 @@ module RiCal
140
140
 
141
141
 
142
142
  def rational_utc_offset(local)
143
- # 86400 is the number of seconds in a day
144
143
  RiCal.RationalOffset[tzinfo.period_for_local(local, true).utc_total_offset]
145
144
  end
146
145
 
@@ -47,17 +47,7 @@ module RiCal
47
47
  raise "Invalid value for occurrence list #{self.inspect}"
48
48
  end
49
49
  end
50
-
51
- # code stolen from ActiveSupport Gem
52
- unless ::String.instance_methods.include?("camelize")
53
- # Convert the receiver to camelized form
54
- # This method duplicates the method provided by ActiveSupport, and will only be defined
55
- # by the RiCal gem if it is not already defined.
56
- def camelize
57
- self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
58
- end
59
- end
60
- end
50
+ end
61
51
  end
62
52
  end
63
53
  end
@@ -27,23 +27,6 @@ module RiCal
27
27
  end
28
28
 
29
29
  unless defined? ActiveSupport
30
- # Converts a Time object to a Date, dropping hour, minute, and second precision.
31
- #
32
- # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007
33
- # my_time.to_date # => Mon, 12 Nov 2007
34
- #
35
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
36
- # your_time.to_date # => Tue, 13 Jan 2009
37
- def to_date
38
- ::Date.new(year, month, day)
39
- end
40
-
41
- # A method to keep Time, Date and DateTime instances interchangeable on conversions.
42
- # In this case, it simply returns +self+.
43
- def to_time
44
- self
45
- end
46
-
47
30
  # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
48
31
  #
49
32
  # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007
@@ -8,39 +8,6 @@ module RiCal
8
8
  # This module is included by Time, Date, and DateTime
9
9
  module WeekDayPredicates
10
10
 
11
- # Determine the equivalent time on the day which falls on a particular weekday of the same year as the receiver
12
- #
13
- # == Parameters
14
- # n:: the ordinal number being requested
15
- # which_wday:: the weekday using Ruby time conventions, i.e. 0 => Sunday, 1 => Monday, ...
16
-
17
- # e.g. to obtain the 2nd Monday of the receivers year use
18
- #
19
- # time.nth_wday_in_year(2, 1)
20
- def nth_wday_in_year(n, which_wday, for_time = self)
21
- if n > 0
22
- first_of_year = for_time.to_ri_cal_property_value.change(:month => 1, :day => 1)
23
- first_in_year = first_of_year.advance(:days => (which_wday - first_of_year.wday + 7) % 7)
24
- first_in_year.advance(:days => (7*(n - 1)))
25
- else
26
- december25 = for_time.to_ri_cal_property_value.change(:month => 12, :day => 25)
27
- last_in_year = december25.advance(:days => (which_wday - december25.wday + 7) % 7)
28
- last_in_year.advance(:days => (7 * (n + 1)))
29
- end
30
- end
31
-
32
- # A predicate to determine whether or not the receiver falls on a particular weekday of its year.
33
- #
34
- # See #nth_wday_in_year
35
- #
36
- # == Parameters
37
- # n:: the ordinal number being requested
38
- # which_wday:: the weekday using Ruby time conventions, i.e. 0 => Sunday, 1 => Monday, ...
39
- def nth_wday_in_year?(n, which_wday)
40
- target = nth_wday_in_year(n, which_wday)
41
- [self.year, self.mon, self.day] == [target.year, target.mon, target.day]
42
- end
43
-
44
11
  # Determine the day which falls on a particular weekday of the same month as the receiver
45
12
  #
46
13
  # == Parameters
@@ -0,0 +1,214 @@
1
+ module RiCal
2
+ #- ©2009 Rick DeNatale
3
+ #- All rights reserved. Refer to the file README.txt for the license
4
+ #
5
+ # FastDateTime mimics the Ruby Standard library DateTime class but avoids the use of Rational
6
+ # Instead of using a Rational for the utc offset, FastDateTime uses an integer seconds value
7
+ class FastDateTime
8
+ attr_accessor :date, :hour, :min, :sec, :offset, :secs_since_bod
9
+
10
+ SECONDS_IN_A_DAY = 60*60*24 unless defined? SECONDS_IN_A_DAY
11
+
12
+ include Comparable
13
+
14
+ def initialize(year, month, day, hour, min, sec, offset_seconds)
15
+ @date = Date.civil(year, month, day)
16
+ @secs_since_bod = hms_to_seconds(hour, min, sec)
17
+ @hour, @min, @sec, @offset = hour, min, sec, offset_seconds
18
+ end
19
+
20
+ def self.from_date_time(date_time)
21
+ new(date_time.year, date_time.month, date_time.day, date_time.hour, date_time.min, date_time.sec, (date_time.offset * SECONDS_IN_A_DAY).to_i)
22
+ end
23
+
24
+ alias_method :utc_offset_seconds, :offset
25
+
26
+ def ical_str
27
+ "%04d%02d%02dT%02d%02d%02d" % [year, month, day, hour, min, sec]
28
+ end
29
+
30
+ def ical_date_str
31
+ "%04d%02d%02d" % [year, month, day]
32
+ end
33
+
34
+ def year
35
+ @date.year
36
+ end
37
+
38
+ def month
39
+ @date.month
40
+ end
41
+
42
+ alias_method :mon, :month
43
+
44
+ def day
45
+ @date.day
46
+ end
47
+
48
+ def wday
49
+ @date.wday
50
+ end
51
+
52
+ def to_datetime
53
+ DateTime.civil(year, month, day, hour, min, sec, RiCal.RationalOffset[utc_offset_seconds])
54
+ end
55
+
56
+ def ==(other)
57
+ [date, secs_since_bod, offset] == [other.date, other.secs_since_bod, other.offset]
58
+ end
59
+
60
+ def <=> (other)
61
+ if FastDateTime === other
62
+ [date, secs_since_bod] <=> [other.date, other.secs_since_bod]
63
+ else
64
+ [year, month, day, hour, min, sec] <=> [other.year, other.month, other.day, other.hour, other.min, other.sec]
65
+ end
66
+ end
67
+
68
+ def to_s
69
+ "#{year}/#{month}/#{day} #{hour}:#{min}:#{sec} #{offset}"
70
+ end
71
+
72
+ # def jd
73
+ # date.jd
74
+ # end
75
+ #
76
+ def days_in_month
77
+ date.days_in_month
78
+ end
79
+
80
+ alias_method :inspect, :to_s
81
+
82
+ # Return a new FastDateTime based on the receiver but with changes specified by the options
83
+ def change(options)
84
+ FastDateTime.new(
85
+ options[:year] || year,
86
+ options[:month] || month,
87
+ options[:day] || day,
88
+ options[:hour] || hour,
89
+ options[:min] || (options[:hour] ? 0 : min),
90
+ options[:sec] || ((options[:hour] || options[:min]) ? 0 : sec),
91
+ options[:offset] || offset
92
+ )
93
+ end
94
+
95
+ # def new_offset(ofst)
96
+ # if ofst == offset
97
+ # self
98
+ # else
99
+ # advance(:seconds => offset - ofset, :offset => ofst)
100
+ # end
101
+ # end
102
+
103
+ def hms_to_seconds(hours, minutes, seconds)
104
+ seconds + 60 *(minutes + (60 * hours))
105
+ end
106
+
107
+ def seconds_to_hms(total_seconds)
108
+ sign = total_seconds <=> 0
109
+ remaining = total_seconds.abs
110
+ seconds = sign * (remaining % 60)
111
+ remaining = remaining / 60
112
+ minutes = sign * (remaining % 60)
113
+ [remaining / 60, minutes, seconds]
114
+ end
115
+
116
+ def adjust_day_delta(day_delta, new_secs_since_bod)
117
+ if new_secs_since_bod == 0
118
+ [day_delta, new_secs_since_bod]
119
+ elsif new_secs_since_bod > 0
120
+ [day_delta + (new_secs_since_bod / SECONDS_IN_A_DAY), new_secs_since_bod % SECONDS_IN_A_DAY]
121
+ else new_secs_since_bod
122
+ [day_delta - (1 + new_secs_since_bod.abs / SECONDS_IN_A_DAY),
123
+ SECONDS_IN_A_DAY - (new_secs_since_bod.abs % SECONDS_IN_A_DAY)]
124
+ end
125
+ end
126
+
127
+
128
+ def advance(options) # :nodoc:
129
+ new_date = @date
130
+ new_offset = options[:offset] || offset
131
+ month_delta = (options[:years] || 0) * 12 + (options[:months] || 0)
132
+ day_delta = (options[:weeks] || 0) * 7 + (options[:days] || 0)
133
+ sec_delta = hms_to_seconds((options[:hours] || 0), (options[:minutes] || 0), (options[:seconds] || 0))
134
+ day_delta, new_secs_since_bod = *adjust_day_delta(day_delta, secs_since_bod + sec_delta)
135
+ new_hour, new_min, new_sec = *seconds_to_hms(new_secs_since_bod)
136
+ new_date = new_date >> month_delta unless month_delta == 0
137
+ new_date += day_delta unless day_delta == 0
138
+ FastDateTime.new(new_date.year, new_date.month, new_date.day, new_hour, new_min, new_sec, new_offset)
139
+ end
140
+
141
+ # Determine the day which falls on a particular weekday of the same month as the receiver
142
+ #
143
+ # == Parameters
144
+ # n:: the ordinal number being requested
145
+ # which_wday:: the weekday using Ruby time conventions, i.e. 0 => Sunday, 1 => Monday, ...
146
+
147
+ # e.g. to obtain the 3nd Tuesday of the receivers month use
148
+ #
149
+ # time.nth_wday_in_month(2, 2)
150
+ def nth_wday_in_month(n, which_wday)
151
+ first_of_month = change(:day => 1)
152
+ first_in_month = first_of_month.advance(:days => (which_wday - first_of_month.wday))
153
+ first_in_month = first_in_month.advance(:days => 7) if first_in_month.month != first_of_month.month
154
+ if n > 0
155
+ first_in_month.advance(:days => (7*(n - 1)))
156
+ else
157
+ possible = first_in_month.advance(:days => 21)
158
+ possible = possible.advance(:days => 7) while possible.month == first_in_month.month
159
+ last_in_month = possible.advance(:days => - 7)
160
+ (last_in_month.advance(:days => - (7*(n.abs - 1))))
161
+ end
162
+ end
163
+
164
+ # Determine the equivalent time on the day which falls on a particular weekday of the same year as the receiver
165
+ #
166
+ # == Parameters
167
+ # n:: the ordinal number being requested
168
+ # which_wday:: the weekday using Ruby time conventions, i.e. 0 => Sunday, 1 => Monday, ...
169
+
170
+ # e.g. to obtain the 2nd Monday of the receivers year use
171
+ #
172
+ # time.nth_wday_in_year(2, 1)
173
+ def nth_wday_in_year(n, which_wday)
174
+ if n > 0
175
+ first_of_year = change(:month => 1, :day => 1)
176
+ first_in_year = first_of_year.advance(:days => (which_wday - first_of_year.wday + 7) % 7)
177
+ first_in_year.advance(:days => (7*(n - 1)))
178
+ else
179
+ december25 = change(:month => 12, :day => 25)
180
+ last_in_year = december25.advance(:days => (which_wday - december25.wday + 7) % 7)
181
+ last_in_year.advance(:days => (7 * (n + 1)))
182
+ end
183
+ end
184
+
185
+
186
+ # Return a DateTime which is the beginning of the first day on or before the receiver
187
+ # with the specified wday
188
+ def start_of_week_with_wkst(wkst)
189
+ wkst ||= 1
190
+ date = @date
191
+ date -= 1 while date.wday != wkst
192
+ date
193
+ end
194
+
195
+ def iso_weeks_in_year(wkst)
196
+ @date.iso_weeks_in_year(wkst)
197
+ end
198
+
199
+ def iso_year_start(wkst)
200
+ @date.iso_year_start(wkst)
201
+ end
202
+
203
+ def iso_year_and_week_one_start(wkst)
204
+ @date.iso_year_and_week_one_start(wkst)
205
+ end
206
+
207
+ def cmp_fast_date_time_value(other)
208
+ other <=> self
209
+ end
210
+
211
+
212
+ end
213
+
214
+ end
@@ -222,10 +222,10 @@ module RiCal
222
222
  @exdate_property = nil
223
223
  @recurrence_id_property = occurrence_start
224
224
  if @dtend_property && !occurrence_end
225
- occurrence_end = occurrence_start + (@dtend_property - @dtstart_property)
225
+ occurrence_end = occurrence_start + (@dtend_property - @dtstart_property)
226
226
  end
227
- @dtstart_property = dtstart_property.for_occurrence(occurrence_start)
228
- @dtend_property = dtend_property.for_occurrence(occurrence_end) if @dtend_property
227
+ @dtstart_property = @dtstart_property.for_occurrence(occurrence_start)
228
+ @dtend_property = (@dtend_property || @dtstart_property).for_occurrence(occurrence_end) if occurrence_end
229
229
  self
230
230
  end
231
231
 
@@ -15,7 +15,7 @@ module RiCal
15
15
  # Returns the value of the reciever as an RFC 2445 iCalendar string
16
16
  def value
17
17
  if @date_time_value
18
- @date_time_value.strftime("%Y%m%d")
18
+ @date_time_value.ical_date_str
19
19
  else
20
20
  nil
21
21
  end
@@ -34,9 +34,11 @@ module RiCal
34
34
  when nil
35
35
  @date_time_value = nil
36
36
  when String
37
- @date_time_value = ::DateTime.parse(::DateTime.parse(val).strftime("%Y%m%d"))
37
+ @date_time_value = FastDateTime.from_date_time(::DateTime.parse(::DateTime.parse(val).strftime("%Y%m%d")))
38
38
  when ::Time, ::Date, ::DateTime
39
- @date_time_value = ::DateTime.parse(val.strftime("%Y%m%d"))
39
+ @date_time_value = FastDateTime.from_date_time(::DateTime.parse(val.strftime("%Y%m%d")))
40
+ when FastDateTime
41
+ @date_time_value = val
40
42
  end
41
43
  end
42
44
 
@@ -69,7 +71,7 @@ module RiCal
69
71
 
70
72
  # Returns the ruby representation a ::Date
71
73
  def ruby_value
72
- ::Date.parse(@date_time_value.strftime("%Y%m%d"))
74
+ @date_time_value.date
73
75
  end
74
76
 
75
77
  alias_method :to_ri_cal_ruby_value, :ruby_value
@@ -89,24 +91,12 @@ module RiCal
89
91
  self
90
92
  end
91
93
 
92
- def compute_change(d, options) #:nodoc:
93
- ::Date.civil((options[:year] || d.year), (options[:month] || d.month), (options[:day] || d.day))
94
- end
95
-
96
- def compute_advance(d, options) #:nodoc:
97
- d = d >> options[:years] * 12 if options[:years]
98
- d = d >> options[:months] if options[:months]
99
- d = d + options[:weeks] * 7 if options[:weeks]
100
- d = d + options[:days] if options[:days]
101
- compute_change(@date_time_value, :year => d.year, :month => d.month, :day => d.day)
102
- end
103
-
104
94
  def advance(options) #:nodoc:
105
- PropertyValue::Date.new(timezone_finder, :value => compute_advance(@date_time_value, options), :params =>(params ? params.dup : nil) )
95
+ PropertyValue::Date.new(timezone_finder, :value => @date_time_value.advance(options), :params =>(params ? params.dup : nil) )
106
96
  end
107
97
 
108
98
  def change(options) #:nodoc:
109
- PropertyValue::Date.new(timezone_finder,:value => compute_change(@date_time_value, options), :params => (params ? params.dup : nil) )
99
+ PropertyValue::Date.new(timezone_finder,:value => @date_time_value.change(options), :params => (params ? params.dup : nil) )
110
100
  end
111
101
 
112
102
  def add_date_times_to(required_timezones) #:nodoc:
@@ -146,26 +136,30 @@ module RiCal
146
136
  date_time = self.to_ri_cal_date_time_value
147
137
  RiCal::OccurrencePeriod.new(date_time, date_time.advance(:hours => 24, :seconds => -1))
148
138
  end
149
-
139
+
150
140
  def start_of_day?
151
141
  true
152
142
  end
153
-
143
+
144
+ def to_floating_date_time_property
145
+ PropertyValue::DateTime.new(timezone_finder, :value => @date_time_value.ical_str)
146
+ end
147
+
154
148
  def to_zulu_occurrence_range_start_time
155
- to_ri_cal_date_time_value.to_zulu_occurrence_range_start_time
149
+ to_floating_date_time_property.to_zulu_occurrence_range_start_time
156
150
  end
157
-
151
+
158
152
  def to_zulu_occurrence_range_finish_time
159
153
  to_ri_cal_date_time_value.end_of_day.to_zulu_occurrence_range_finish_time
160
154
  end
161
-
155
+
162
156
  def to_finish_time
163
157
  to_ri_cal_date_time_value.end_of_day.to_datetime
164
158
  end
165
-
159
+
166
160
  def for_occurrence(occurrence)
167
161
  if occurrence.start_of_day?
168
- occurrence.to_ri_cal_date_value(timezone_finder)
162
+ occurrence.to_ri_cal_date_value(timezone_finder)
169
163
  else
170
164
  occurrence.for_parent(timezone_finder)
171
165
  end