ri_cal 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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