seamusabshere-date-performance 0.0.1

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/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ rtomayko = Ryan Tomayko <rtomayko@gmail.com>
data/BENCHMARKS ADDED
@@ -0,0 +1,30 @@
1
+ == WITH EXTENSION ==============================
2
+ comparison
3
+ 0.030000 0.000000 0.030000 ( 0.027561)
4
+ greater_than
5
+ 0.030000 0.000000 0.030000 ( 0.026667)
6
+ new_civil
7
+ 0.060000 0.000000 0.060000 ( 0.063149)
8
+ new_jd
9
+ 0.060000 0.000000 0.060000 ( 0.058935)
10
+ strftime
11
+ 0.020000 0.000000 0.020000 ( 0.018389)
12
+ strftime_after_new
13
+ 0.080000 0.000000 0.080000 ( 0.082086)
14
+ strptime
15
+ 0.070000 0.000000 0.070000 ( 0.068235)
16
+ == WITHOUT EXTENSION ===========================
17
+ comparison
18
+ 0.030000 0.000000 0.030000 ( 0.027135)
19
+ greater_than
20
+ 0.030000 0.000000 0.030000 ( 0.026128)
21
+ new_civil
22
+ 0.300000 0.000000 0.300000 ( 0.303737)
23
+ new_jd
24
+ 0.110000 0.000000 0.110000 ( 0.110033)
25
+ strftime
26
+ 0.230000 0.000000 0.230000 ( 0.231178)
27
+ strftime_after_new
28
+ 0.850000 0.000000 0.850000 ( 0.843894)
29
+ strptime
30
+ 3.000000 0.030000 3.030000 ( 3.033587)
data/README.md ADDED
@@ -0,0 +1,134 @@
1
+ Date::Performance
2
+ =================
3
+
4
+ This package adds some semblance of performance to Ruby's core Date class
5
+ using a combination of different techniques:
6
+
7
+ 1. Implements various core *Date* methods in C. This is nowhere near a
8
+ complete rewrite of all *Date* features but many of the hot spots have
9
+ been replaced with machine code.
10
+
11
+ 2. Provide alternate implementations of `strftime` and `strptime` in C. The stock
12
+ date formatting and parsing methods are extremely slow compared to their
13
+ libc counterparts. *Date#sys_strftime* and *Date::sys_strptime* are light
14
+ facades on top of the system's `strftime(2)` and `strptime(2)`. The system
15
+ methods run 10x and 50x (yes, _fifty-ecks_) faster than their Ruby based counterparts,
16
+ respectively. Unfortunately, `strftime(2)` and `strptime(2)` implementations vary from
17
+ system to system and have various limitations not found in the core Date
18
+ implementation so can not safely be used as replacements for the core methods.
19
+
20
+ 3. Memoization. The *Date::Memoize* module can be used to speed certain
21
+ types of repetitive date processing significantly. This file must be
22
+ required separately.
23
+
24
+ Synopsis
25
+ --------
26
+
27
+ This package is mostly transparent after an initial require:
28
+
29
+ require 'date/performance'
30
+ Date.new 1912, 6, 23
31
+ # Wow! That was fast!
32
+
33
+ *Date::Performance* is not used directly but automatically replaces core *Date*
34
+ methods when required.
35
+
36
+ In addition to the C extension, the *Date::Memoization* module can be used to
37
+ speed things up even further in some cases by making a trade off between space
38
+ and time:
39
+
40
+ require 'date/memoize'
41
+ Date.new 1912, 6, 23
42
+ Date.parse '1912-06-23'
43
+
44
+ Requiring the file automatically replaces *Date::new* / *Date::civil*, *Date::parse*,
45
+ and *Date::jd* methods with memoized versions.
46
+
47
+ Installation / Hacking
48
+ ----------------------
49
+
50
+ This package has been tested on the following platforms:
51
+
52
+ * FreeBSD 5.4 (x86) and 6.1 (AMD64)
53
+ * Linux / Fedora Core 6 (x86)
54
+ * MacOS X (Intel)
55
+
56
+ The easiest way to install the package is to use RubyGems:
57
+
58
+ $ gem install date-performance --source=http://tomayko.com
59
+
60
+ Old versions and other dist formats are available at:
61
+
62
+ http://tomayko.com/dist/date-performance/
63
+
64
+ A git repository is also available:
65
+
66
+ $ git clone git://github.com/rtomayko/date-performance.git
67
+
68
+ Background
69
+ ----------
70
+
71
+ The *Date* class is often the cause of poor performance in Ruby programs. A frequent
72
+ suggestion is to use the *Time* class, which is much faster, but that solution has
73
+ correctness problems in a wide range of data typing cases. It is often the case that
74
+ you want separate *Date*, *Time*, and *DateTime* types.
75
+
76
+ There are a couple of reasons why *Date* runs slowly when compared with
77
+ *Time*. The common assumption is that this is mostly due to *Time* being
78
+ written in C and *Date* being written in Ruby. While that clearly has an
79
+ impact, I would argue that the reason *Date* is slow is because it's not
80
+ designed to be fast. The code opts for readability over performance in almost
81
+ every case. _This is a feature_.
82
+
83
+ Have you read the *date.rb* documentation [1]? The implementation is pretty
84
+ hard core; it can handle a lot of weird cases that *Time* [2] does not and
85
+ would appear to be a correct implementation of date handling, which has the
86
+ usual side-effect of being slow.
87
+
88
+ The *Date* implementation uses a single Astronomical Julian Day (AJD) number
89
+ to represent dates internally. In fact, *Date#initialize* takes a
90
+ single `ajd` argument, which means that all date forms that are commonly used
91
+ (UNIX timestamp, Civil, etc.) must be converted to an AJD before we can even
92
+ instantiate the thing.
93
+
94
+ The real performance hit seems to come from the various rational number
95
+ operations performed on the way from a civil, ordinal, and julian date to
96
+ an AJD.
97
+
98
+ When I began writing *Date::Performance*, I was getting pretty big (3x - 4x)
99
+ performance boosts in many places simply by optimizing the Ruby code a bit.
100
+ These boosts came at the expense of readability, however, and so the decision
101
+ was made to go for _maximum unreadability_ and implement the boosts in C.
102
+
103
+ There's a nice balance here: the Ruby implementation reads like a spec,
104
+ while the C version implements it for quickness.
105
+
106
+ Memoization
107
+ -----------
108
+
109
+ In addition to the C extension, this package includes a separate *Date::Memoize*
110
+ module that can further speed up date processing in situations where the range
111
+ of dates being manipulated is fairly dense and the same dates are being
112
+ created repeatedly. Working with databases and flat files are two examples
113
+ where memoization may help significantly.
114
+
115
+ The *Date::Memoize* module replaces various Date constructor methods (`new`,
116
+ `civil`, and `parse`) with memoized[http://en.wikipedia.org/wiki/Memoization]
117
+ versions (see *Date::Memoization* for details). The best way to determine
118
+ whether memoization is right for you is to add it to your project and see
119
+ what happens.
120
+
121
+ License
122
+ -------
123
+
124
+ MIT. See the COPYING file included in the distribution for more
125
+ information.
126
+
127
+ See Also
128
+ --------
129
+
130
+ * [1] Ruby *Date* Implementation Notes and Documentation:
131
+ http://www.ruby-doc.org/docs/rdoc/1.9/files/_/lib/date_rb.html
132
+
133
+ * [2] Ruby *Time* documentation
134
+ http://www.ruby-doc.org/docs/rdoc/1.9/classes/Time.html
data/Rakefile ADDED
@@ -0,0 +1,105 @@
1
+ load 'misc/asciidoc.rake'
2
+ load 'misc/project.rake'
3
+
4
+ Project.new "Date::Performance" do |p|
5
+ p.package_name = 'date-performance'
6
+ p.version_file = 'lib/date/performance.rb'
7
+ p.summary = "Adds some semblance of performance to Ruby's core Date class."
8
+ p.project_url = "http://tomayko.com/src/date-performance/"
9
+ p.extra_files.include "ext/**/*.{rb,c,h}", "AUTHORS", "BENCHMARKS"
10
+ p.configure_package {|spec| spec.extensions = FileList["ext/**/extconf.rb"].to_a }
11
+ p.author = 'Ryan Tomayko <r@tomayko.com>'
12
+ p.rubyforge_project = 'wink'
13
+
14
+ p.remote_dist_location = "gus@tomayko.com:/dist/#{p.package_name}"
15
+ p.remote_branch_location = "gus@tomayko.com:/src/#{p.package_name}.git"
16
+ p.remote_doc_location = "gus@tomayko.com:/src/#{p.package_name}"
17
+ end
18
+
19
+ begin
20
+ require 'jeweler'
21
+ Jeweler::Tasks.new do |gemspec|
22
+ gemspec.name = 'date-performance'
23
+ gemspec.summary = "Adds some semblance of performance to Ruby's core Date class."
24
+ gemspec.email = 'Ryan Tomayko <r@tomayko.com>'
25
+ gemspec.homepage = "http://tomayko.com/src/date-performance/"
26
+ gemspec.description = "Adds some semblance of performance to Ruby's core Date class."
27
+ gemspec.authors = [ 'Ryan Tomayko', 'Alexander Dymo' ]
28
+ gemspec.files.include "ext/**/*.{rb,c,h}", "AUTHORS", "BENCHMARKS"
29
+ end
30
+ rescue LoadError
31
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
32
+ end
33
+
34
+ task :default => [ :compile, :test ]
35
+
36
+ file 'doc/index.txt' => 'README.md' do |f|
37
+ cp 'README.md', f.name
38
+ end
39
+
40
+ CLEAN.include [ "ext/*.{o,bundle}", "lib/*.{bundle,so,dll}" ]
41
+ CLOBBER.include [ "ext/Makefile" ]
42
+
43
+ if File.exist? 'misc/date-1.8.5'
44
+ desc "Run unit tests with Date from Ruby 1.8.5"
45
+ Rake::TestTask.new 'test:ruby185' do |t|
46
+ t.libs << "test"
47
+ t.libs << "misc/date-1.8.5"
48
+ t.test_files = FileList['test/*_test.rb']
49
+ t.verbose = true
50
+ end
51
+ end
52
+
53
+ task 'benchmark.without' do |t|
54
+ verbose false do
55
+ puts '== WITHOUT EXTENSION ==========================='
56
+ ruby "-Ilib test/benchmarks.rb"
57
+ end
58
+ end
59
+
60
+ task 'benchmark.with' do |t|
61
+ verbose false do
62
+ puts '== WITH EXTENSION =============================='
63
+ ruby "-Ilib -rdate/performance test/benchmarks.rb"
64
+ end
65
+ end
66
+
67
+ desc "Run benchmarks"
68
+ task :benchmark => [ 'benchmark.without', 'benchmark.with' ]
69
+
70
+ # Extension =================================================================
71
+
72
+ DLEXT = Config::CONFIG['DLEXT']
73
+
74
+ directory "lib"
75
+
76
+ desc "Build the extension"
77
+ task "date_performance" => [ "lib/date_performance.#{DLEXT}" ]
78
+
79
+ file "ext/Makefile" => FileList[ "ext/*.{c,h,rb}" ] do
80
+ Dir.chdir("ext") { ruby "extconf.rb" }
81
+ end
82
+
83
+ file "ext/date_performance.#{DLEXT}" => FileList["ext/*.c", "ext/Makefile"] do |f|
84
+ Dir.chdir("ext") { sh "make" }
85
+ end
86
+
87
+ file "lib/date_performance.#{DLEXT}" => [ "ext/date_performance.#{DLEXT}" ] do |t|
88
+ cp "ext/date_performance.#{DLEXT}", t.name
89
+ end
90
+
91
+ desc "Compiles all extensions"
92
+ task :compile => [ "lib/date_performance.#{DLEXT}" ]
93
+
94
+ # Rubyforge =================================================================
95
+
96
+ VERS = Project.current.version
97
+ PKGNAME = "dist/date-performance-#{VERS}"
98
+
99
+ desc 'Publish new release to rubyforge'
100
+ task :release => [ "#{PKGNAME}.gem", "#{PKGNAME}.tar.gz" ] do |t|
101
+ sh <<-end
102
+ rubyforge add_release wink date-performance #{VERS} #{PKGNAME}.gem &&
103
+ rubyforge add_file wink date-performance #{VERS} #{PKGNAME}.tar.gz
104
+ end
105
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 0
4
+ :patch: 1
@@ -0,0 +1,440 @@
1
+
2
+ /*
3
+ * Enable C99 extensions to enable fast float rounding stuff in math.h.
4
+ */
5
+ #define _ISOC9X_SOURCE 1
6
+ #define _ISOC99_SOURCE 1
7
+
8
+ #include <math.h>
9
+ #include <ruby.h>
10
+ #include <time.h>
11
+
12
+ #define FLOOR(a) lrintf(floorf(a))
13
+ #define FLOAT(a) (float)a
14
+
15
+ static VALUE rb_cDate; /* class Date */
16
+ static VALUE rb_cRational; /* class Rational */
17
+
18
+ static ID id_subtract; /* :- */
19
+ static ID id_add; /* :+ */
20
+ static ID id_divmod; /* :divmod */
21
+ static ID id_new; /* :new */
22
+ static ID id_new_bang; /* :new! */
23
+ static ID id_ivar_civil; /* :@__civil__ */
24
+ static ID id_jd; /* :jd */
25
+ static ID id_ivar_ajd; /* :@ajd */
26
+ static ID id_jd_to_civil; /* :jd_to_civil */
27
+ static ID id_ivar_sg; /* :@sg */
28
+ static ID id_numerator; /* :numerator */
29
+ static ID id_denominator; /* :denominator */
30
+
31
+ static ID id_strptime_without_performance;
32
+ static ID id_strftime_without_performance;
33
+
34
+ static VALUE JULIAN; /* Date::JULIAN */
35
+ static VALUE GREGORIAN; /* Date::GREGORIAN */
36
+ static VALUE ITALY; /* Date::ITALY */
37
+
38
+ static VALUE ra_one_half; /* Rational(1, 2) */
39
+ static VALUE DEFAULT_FORMAT; /* "%F" */
40
+
41
+ static int initialized = 0;
42
+
43
+ static inline int
44
+ civil_to_jd(int y, int m, int d, VALUE sg)
45
+ {
46
+ int a, b, jd;
47
+ if ( m <= 2 ) {
48
+ y-= 1;
49
+ m+= 12;
50
+ }
51
+ a = y / 100;
52
+ b = 2 - a + (a / 4);
53
+ jd = FLOOR(365.25 * (y + 4716)) +
54
+ FLOOR(30.6001 * (m + 1)) +
55
+ d + b - 1524;
56
+ if ( sg == JULIAN || (sg != GREGORIAN && jd < FIX2INT(sg)) )
57
+ jd -= b;
58
+ return jd;
59
+ }
60
+
61
+
62
+ /*
63
+ * Date::civil_to_jd(year, month, day, sg=Date::GREGORIAN)
64
+ */
65
+ static VALUE
66
+ rb_date_civil_to_jd(int argc, VALUE* argv, VALUE self)
67
+ {
68
+ int y = FIX2INT(argv[0]),
69
+ m = FIX2INT(argv[1]),
70
+ d = FIX2INT(argv[2]);
71
+ VALUE sg = (argc == 4 ? argv[3] : GREGORIAN);
72
+ return INT2FIX(civil_to_jd(y, m, d, sg));
73
+ }
74
+
75
+
76
+ struct mini_tm {
77
+ int y;
78
+ int m;
79
+ int d;
80
+ };
81
+
82
+
83
+ static inline void
84
+ jd_to_civil(int jd, VALUE sg, struct mini_tm * t)
85
+ {
86
+ int a, b, c, d, e;
87
+ if ( sg == JULIAN || (sg != GREGORIAN && jd < FIX2INT(sg)) ) { /* julian? */
88
+ a = jd;
89
+ }else{
90
+ int x = FLOOR((jd - 1867216.25) / 36524.25);
91
+ a = jd + 1 + x - FLOOR(x / 4.0);
92
+ }
93
+ b = a + 1524;
94
+ c = FLOOR((b - 122.1) / 365.25);
95
+ d = FLOOR(365.25 * c);
96
+ e = FLOOR((b - d) / 30.6001);
97
+ t->d = b - d - FLOOR(30.6001 * e);
98
+ if ( e <= 13 ) {
99
+ t->m = e - 1;
100
+ t->y = c - 4716;
101
+ }else{
102
+ t->m = e - 13;
103
+ t->y = c - 4715;
104
+ }
105
+ }
106
+
107
+
108
+ /*
109
+ * Date::jd_to_civil(jd, sg=GREGORIAN)
110
+ */
111
+ static VALUE
112
+ rb_date_jd_to_civil(int argc, VALUE * argv, VALUE self)
113
+ {
114
+ int jd = FIX2INT(argv[0]);
115
+ VALUE sg = (argc == 2 ? argv[1] : GREGORIAN);
116
+ struct mini_tm t;
117
+ jd_to_civil(jd, sg, &t);
118
+ return rb_ary_new3(3, INT2FIX(t.y), INT2FIX(t.m), INT2FIX(t.d));
119
+ }
120
+
121
+
122
+ /*
123
+ * Calculate the AJD from a julian date, fractional day, and offset.
124
+ */
125
+ static inline VALUE
126
+ jd_to_ajd(long jd, VALUE fr, VALUE of)
127
+ {
128
+ /* Ruby Implementation: jd + fr - of - 1.to_r/2 */
129
+ if ( TYPE(fr) == T_FIXNUM && TYPE(of) == T_FIXNUM ) {
130
+ /* fast path the common case of no fraction and no offset */
131
+ long numerator = (((jd + FIX2LONG(fr) - FIX2LONG(of)) - 1) * 2) + 1;
132
+ return rb_funcall(rb_cRational, id_new, 2, LONG2FIX(numerator), LONG2FIX(2));
133
+ }else{
134
+ /* use slower rational math */
135
+ VALUE result = rb_funcall(rb_cRational, id_new, 2, LONG2FIX(jd), LONG2FIX(1));
136
+ result = rb_funcall(result, id_add, 1, fr);
137
+ if ( of != LONG2FIX(0) )
138
+ result = rb_funcall(result, id_subtract, 1, of);
139
+ result = rb_funcall(result, id_subtract, 1, ra_one_half);
140
+ return result;
141
+ }
142
+ }
143
+
144
+
145
+ /*
146
+ * Date::jd_to_ajd(jd, fr, of=0)
147
+ */
148
+ static VALUE
149
+ rb_date_jd_to_ajd(int argc, VALUE * argv, VALUE self)
150
+ {
151
+ long jd = FIX2LONG(argv[0]);
152
+ VALUE fr = (argc > 1 ? argv[1] : LONG2FIX(0));
153
+ VALUE of = (argc > 2 ? argv[2] : LONG2FIX(0));
154
+ return jd_to_ajd(jd, fr, of);
155
+ }
156
+
157
+
158
+ /*
159
+ * Date::ajd_to_jd(ajd, of=0)
160
+ *
161
+ * TODO: handle offsets properly.
162
+ *
163
+ * Ruby Implementation: (ajd + of + 1.to_r/2).divmod(1)
164
+ */
165
+ static VALUE
166
+ rb_date_ajd_to_jd(int argc, VALUE * argv, VALUE self)
167
+ {
168
+ VALUE ajd = argv[0];
169
+ VALUE of = (argc == 2 ? argv[1] : INT2FIX(0));
170
+ long den = FIX2LONG(rb_funcall(ajd, id_denominator, 0));
171
+ long num = FIX2LONG(rb_funcall(ajd, id_numerator, 0));
172
+ if ( den == 2 && of == INT2FIX(0) ) {
173
+ /* fast path */
174
+ return rb_ary_new3(2, LONG2FIX((num + 1) / 2), ra_one_half);
175
+ }else{
176
+ VALUE result = rb_funcall(ajd, id_add, 1, of);
177
+ result = rb_funcall(result, id_add, 1, ra_one_half);
178
+ return rb_funcall(result, id_divmod, 1, LONG2FIX(1));
179
+ }
180
+ }
181
+
182
+
183
+ /*
184
+ * Date::new(y=-4712, m=1, d=1, sg=ITALY)
185
+ */
186
+ static VALUE
187
+ rb_date_new(int argc, VALUE * argv, VALUE self) {
188
+ int y = (argc > 0 ? NUM2INT(argv[0]) : -4712),
189
+ m = (argc > 1 ? NUM2INT(argv[1]) : 1),
190
+ d = (argc > 2 ? NUM2INT(argv[2]) : 1);
191
+ VALUE sg = (argc > 3 ? argv[3] : ITALY);
192
+ int jd = -1;
193
+ struct mini_tm t;
194
+ if (d < 0) {
195
+ int ny = (y * 12 + m) / 12;
196
+ int nm = (y * 12 + m) % 12;
197
+ nm = (nm + 1) / 1;
198
+ jd = civil_to_jd(ny, nm, d+1, sg);
199
+
200
+ VALUE ns = jd < 2299161 ? JULIAN : GREGORIAN;
201
+ jd_to_civil(jd-d, ns, &t);
202
+ if ( t.y != ny || t.m != nm || t.d != 1 ) {
203
+ rb_raise(rb_eArgError, "Invalid date: (%d, %d, %d)", y, m, d);
204
+ return Qnil;
205
+ }
206
+ jd_to_civil(jd, sg, &t);
207
+ if ( t.y != y || t.m != m ) {
208
+ rb_raise(rb_eArgError, "Invalid date: (%d, %d, %d)", y, m, d);
209
+ return Qnil;
210
+ }
211
+ } else {
212
+ jd = civil_to_jd(y, m, d, sg);
213
+ jd_to_civil(jd, sg, &t);
214
+ if ( t.y != y || t.m != m || t.d != d ) {
215
+ rb_raise(rb_eArgError, "Invalid date: (%d, %d, %d)", y, m, d);
216
+ return Qnil;
217
+ }
218
+ }
219
+ VALUE ajd = jd_to_ajd(jd, INT2FIX(0), INT2FIX(0));
220
+ VALUE date = rb_funcall(self, id_new_bang, 3, ajd, INT2FIX(0), sg);
221
+ rb_ivar_set(date, id_ivar_civil, rb_ary_new3(3, INT2FIX(t.y), INT2FIX(t.m), INT2FIX(t.d)));
222
+ return date;
223
+ }
224
+
225
+
226
+ /*
227
+ * Date#civil
228
+ *
229
+ * Fast path the case where the date is created with civil parameters.
230
+ */
231
+ static VALUE
232
+ rb_date_civil(VALUE self) {
233
+ if ( rb_ivar_defined(self, id_ivar_civil) == Qfalse ) {
234
+ VALUE jd = rb_funcall(self, id_jd, 0);
235
+ VALUE sg = rb_ivar_get(self, id_ivar_sg);
236
+ VALUE result = rb_funcall(rb_cDate, id_jd_to_civil, 2, jd, sg);
237
+ return rb_ivar_set(self, id_ivar_civil, result);
238
+ }else{
239
+ return rb_ivar_get(self, id_ivar_civil);
240
+ }
241
+ }
242
+
243
+
244
+ /*
245
+ * Date#sys_strftime(fmt="%F")
246
+ */
247
+ static VALUE
248
+ rb_date_strftime(int argc, VALUE * argv, VALUE self)
249
+ {
250
+ VALUE format = (argc > 0 ? *argv : DEFAULT_FORMAT);
251
+ VALUE civil = rb_date_civil(self);
252
+
253
+ char * pf = RSTRING(format)->ptr;
254
+ VALUE * pc = RARRAY(civil)->ptr;
255
+ int ic[3];
256
+
257
+ ic[0] = FIX2INT(pc[0]);
258
+ ic[1] = FIX2INT(pc[1]);
259
+ ic[2] = FIX2INT(pc[2]);
260
+
261
+ /* fast path default format: %F or %Y-%m-%d */
262
+ if ( (pf[0] == '%' && pf[1] == 'F' && pf[2] == 0) ||
263
+ (pf[0] == '%' && pf[1] == 'Y' && pf[2] == '-'
264
+ && pf[3] == '%' && pf[4] == 'm' && pf[5] == '-'
265
+ && pf[6] == '%' && pf[7] == 'd' && pf[8] == 0) )
266
+ {
267
+ VALUE buf = rb_str_buf_new(11);
268
+ char * pb = RSTRING(buf)->ptr;
269
+ RSTRING(buf)->len =
270
+ sprintf(pb, "%04d-%02d-%02d", ic[0], ic[1], ic[2]);
271
+ return buf;
272
+ }
273
+
274
+ /* Use libc's strftime but only for Date class */
275
+ if ( RBASIC(self)->klass == rb_cDate ){
276
+ VALUE buf = rb_str_buf_new(128);
277
+ char * pb = RSTRING(buf)->ptr;
278
+ struct tm t;
279
+ bzero(&t, sizeof(struct tm));
280
+ t.tm_year = ic[0] - 1900;
281
+ t.tm_mon = ic[1] - 1;
282
+ t.tm_mday = ic[2];
283
+ mktime(&t); /* fill in missing items (tm_wday, tm_yday) */
284
+ if ( (RSTRING(buf)->len = strftime(pb, 128, pf, &t)) > 0 )
285
+ return buf;
286
+ }
287
+
288
+ /* fall back on Ruby implementation if libc's strftime fails */
289
+ return rb_funcall2(self, id_strftime_without_performance, argc, argv);
290
+ }
291
+
292
+
293
+ /*
294
+ * Date::strptime(str="-4712-01-01", fmt='%F')
295
+ */
296
+ static VALUE
297
+ rb_date_strptime(int argc, VALUE * argv, VALUE self)
298
+ {
299
+ char *pe;
300
+ struct tm buf;
301
+ VALUE str = (argc > 0 ? argv[0] : rb_str_new2("-4712-01-01")),
302
+ fmt = (argc > 1 ? argv[1] : DEFAULT_FORMAT),
303
+ sg = (argc > 2 ? argv[2] : ITALY);
304
+ char * ps = RSTRING(str)->ptr;
305
+ char * pf = RSTRING(fmt)->ptr;
306
+ VALUE parts[4];
307
+
308
+ /* fast path default format */
309
+ if ( (pf[0] == '%' && pf[1] == 'F' && pf[0])
310
+ || (pf[0] == '%' && pf[1] == 'Y' && pf[2] == '-'
311
+ && pf[3] == '%' && pf[4] == 'm' && pf[5] == '-'
312
+ && pf[6] == '%' && pf[7] == 'd' && pf[8] == 0) )
313
+ {
314
+ parts[0] = INT2FIX(strtol(ps, &pe, 10));
315
+ parts[1] = Qnil;
316
+ parts[2] = Qnil;
317
+ parts[3] = sg;
318
+ if( pe == ps + 4 ) {
319
+ parts[1] = INT2FIX(strtol(ps + 5, &pe, 10));
320
+ if ( pe == ps + 7 ) {
321
+ parts[2] = INT2FIX(strtol(ps + 8, &pe, 10));
322
+ if ( pe == ps + 10 )
323
+ return rb_date_new(4, (VALUE*)&parts, self);
324
+ }
325
+ }
326
+ }
327
+
328
+ /* fall back on strptime(3) */
329
+ if ( strptime(ps, pf, &buf) )
330
+ {
331
+ parts[0] = INT2FIX(buf.tm_year + 1900);
332
+ parts[1] = INT2FIX(buf.tm_mon + 1);
333
+ parts[2] = INT2FIX(buf.tm_mday);
334
+ parts[3] = sg;
335
+ return rb_date_new(4, (VALUE*)&parts, self);
336
+ }
337
+
338
+ /* if that doesn't work, fall back on Ruby implementation */
339
+ return rb_funcall2(self, id_strptime_without_performance, argc, argv);
340
+ }
341
+
342
+ /*
343
+ * Date::<=>(other)
344
+ */
345
+ static VALUE
346
+ rb_date_compare(int argc, VALUE * argv, VALUE self)
347
+ {
348
+ if (NIL_P(argv[0]))
349
+ return Qnil;
350
+ long other_den = -1;
351
+ long other_num = -1;
352
+ if (FIXNUM_P(argv[0])) {
353
+ //compare with argument as with astronomical julian day number
354
+ other_den = 1;
355
+ other_num = FIX2LONG(argv[0]);
356
+ } else if (rb_obj_is_kind_of(argv[0], rb_cDate)) {
357
+ VALUE other_date = argv[0];
358
+ VALUE other_ajd = rb_ivar_get(other_date, id_ivar_ajd);
359
+ other_den = FIX2LONG(rb_funcall(other_ajd, id_denominator, 0));
360
+ other_num = FIX2LONG(rb_funcall(other_ajd, id_numerator, 0));
361
+ } else {
362
+ return Qnil;
363
+ }
364
+
365
+ VALUE ajd = rb_ivar_get(self, id_ivar_ajd);
366
+ long den = FIX2LONG(rb_funcall(ajd, id_denominator, 0));
367
+ long num = FIX2LONG(rb_funcall(ajd, id_numerator, 0));
368
+
369
+ long v = (num * other_den) - (other_num * den);
370
+ if (v > 0)
371
+ return INT2FIX(1);
372
+ else if (v < 0)
373
+ return INT2FIX(-1);
374
+ else
375
+ return INT2FIX(0);
376
+ }
377
+
378
+
379
+ VALUE
380
+ Init_date_performance() {
381
+ /* initialization is not idemponent - make sure it only happens once. */
382
+ if ( initialized )
383
+ rb_raise(rb_eStandardError, "date_performance extension already initialized.");
384
+ initialized = 1;
385
+
386
+ /* Grab Date class */
387
+ rb_require("date");
388
+ rb_cDate = rb_define_class("Date", rb_cObject);
389
+
390
+ if( ! rb_const_defined_from(rb_cDate, rb_intern("Performance")) )
391
+ rb_raise(rb_eStandardError,
392
+ "Date::Performance not defined. The date_performance extension can not be required directly.");
393
+
394
+ /* Date Instance Methods */
395
+ rb_define_method(rb_cDate, "civil", rb_date_civil, 0);
396
+ rb_define_method(rb_cDate, "sys_strftime", rb_date_strftime, -1);
397
+ rb_define_method(rb_cDate, "strftime", rb_date_strftime, -1);
398
+ rb_define_method(rb_cDate, "<=>", rb_date_compare, -1);
399
+
400
+ /* Date Singleton Methods */
401
+ rb_define_singleton_method(rb_cDate, "civil_to_jd", rb_date_civil_to_jd, -1);
402
+ rb_define_singleton_method(rb_cDate, "jd_to_civil", rb_date_jd_to_civil, -1);
403
+ rb_define_singleton_method(rb_cDate, "jd_to_ajd", rb_date_jd_to_ajd, -1);
404
+ rb_define_singleton_method(rb_cDate, "ajd_to_jd", rb_date_ajd_to_jd, -1);
405
+ rb_define_singleton_method(rb_cDate, "new", rb_date_new, -1);
406
+ rb_define_singleton_method(rb_cDate, "civil", rb_date_new, -1);
407
+ rb_define_singleton_method(rb_cDate, "sys_strptime", rb_date_strptime, -1);
408
+ rb_define_singleton_method(rb_cDate, "strptime", rb_date_strptime, -1);
409
+
410
+ /* Date Related Constants */
411
+ JULIAN = rb_eval_string("Date::JULIAN");
412
+ GREGORIAN = rb_eval_string("Date::GREGORIAN");
413
+ ITALY = INT2FIX(2299161);
414
+
415
+ DEFAULT_FORMAT = rb_str_new2("%F");
416
+ rb_gc_register_address(&DEFAULT_FORMAT);
417
+
418
+ /* Symbol Constants */
419
+ id_subtract = rb_intern("-");
420
+ id_add = rb_intern("+");
421
+ id_divmod = rb_intern("divmod");
422
+ id_new = rb_intern("new");
423
+ id_jd = rb_intern("jd");
424
+ id_ivar_ajd = rb_intern("@ajd");
425
+ id_jd_to_civil = rb_intern("jd_to_civil");
426
+ id_ivar_civil = rb_intern("@__civil__");
427
+ id_ivar_sg = rb_intern("@sg");
428
+ id_new_bang = rb_intern("new!");
429
+ id_numerator = rb_intern("numerator");
430
+ id_denominator = rb_intern("denominator");
431
+ id_strptime_without_performance = rb_intern("strptime_without_performance");
432
+ id_strftime_without_performance = rb_intern("strftime_without_performance");
433
+
434
+ /* Rational Stuff */
435
+ rb_require("rational");
436
+ rb_cRational = rb_define_class("Rational", rb_cNumeric);
437
+ ra_one_half = rb_funcall(rb_cRational, id_new, 2, INT2FIX(1), INT2FIX(2));
438
+ rb_gc_register_address(&ra_one_half);
439
+ return Qnil;
440
+ }
data/ext/extconf.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ # Disable warnings from ld
4
+ $LDFLAGS = "-w"
5
+ # turn on warnings from gcc
6
+ $CFLAGS = "-pedantic -Wall -Wno-long-long -Winline"
7
+
8
+ dir_config 'date_performance'
9
+ create_makefile 'date_performance'
@@ -0,0 +1,86 @@
1
+ # See Date::Memoize.
2
+
3
+ require 'date'
4
+ require 'date/performance'
5
+
6
+ class Date #:nodoc:
7
+
8
+ # Adds memoization to Date. This can speed things up significantly in cases where a lot
9
+ # of the same Date objects are created.
10
+ module Memoize
11
+
12
+ # Memoized version of Date::strptime.
13
+ def strptime(str='-4712-01-01', fmt='%F', sg=ITALY)
14
+ @__memoized_strptime_dates[ [ str, fmt, sg ] ]
15
+ end
16
+
17
+ # Memoized version Date::parse.
18
+ def parse(str='-4712-01-01', comp=false, sg=ITALY)
19
+ @__memoized_parse_dates[ [ str, comp, sg ] ]
20
+ end
21
+
22
+ # Memoized version of Date::civil.
23
+ def civil(y=-4712, m=1, d=1, sg=ITALY)
24
+ @__memoized_civil_dates[ [ y, m, d, sg ] ]
25
+ end
26
+
27
+ alias_method :new, :civil
28
+
29
+ public
30
+
31
+ # The methods we'll be replacing on the Date singleton.
32
+ def self.methods_replaced
33
+ [ :new, :civil, :strptime, :parse ]
34
+ end
35
+
36
+ # Overridden to move the existing methods out of the way before copying this module's
37
+ # methods.
38
+ def self.extend_object(base)
39
+ singleton = (class<<base;self;end)
40
+ methods_replaced.each do |method|
41
+ singleton.send :alias_method, "#{method}_without_memoization", method
42
+ singleton.send :remove_method, method
43
+ end
44
+ base.send :instance_variable_set, :@__memoized_civil_dates,
45
+ Hash.new{|h,key| h[key]=Date.new_without_memoization(*key)}
46
+ base.send :instance_variable_set, :@__memoized_strptime_dates,
47
+ Hash.new{|h,key| h[key]=Date.strptime_without_memoization(*key)}
48
+ base.send :instance_variable_set, :@__memoized_parse_dates,
49
+ Hash.new{|h,key| h[key]=Date.parse_without_memoization(*key)}
50
+ super
51
+ end
52
+
53
+ # Removes memoization methods from singleton of the class provided.
54
+ def self.unextend_object(base)
55
+ singleton = (class<<base;self;end)
56
+ methods_replaced.each do |method|
57
+ singleton.send :alias_method, method, "#{method}_without_memoization"
58
+ singleton.send :remove_method, "#{method}_without_memoization"
59
+ end
60
+ base.send :remove_instance_variable, :@__memoized_civil_dates
61
+ base.send :remove_instance_variable, :@__memoized_strptime_dates
62
+ base.send :remove_instance_variable, :@__memoized_parse_dates
63
+ base
64
+ end
65
+
66
+ # Is Date memoization currently installed and active?
67
+ def self.installed?
68
+ Date.respond_to? :civil_without_memoization
69
+ end
70
+
71
+ # Extend the Date class with memoized versions of +new+ and +civil+ but only if
72
+ # memoization has not yet been installed.
73
+ def self.install!
74
+ Date.extend self unless installed?
75
+ end
76
+
77
+ # Remove memoized methods and free up memo cache. This method is idempotent.
78
+ def self.uninstall!
79
+ unextend_object Date if installed?
80
+ end
81
+
82
+ end
83
+
84
+ Memoize.install!
85
+
86
+ end
@@ -0,0 +1,44 @@
1
+ require 'date'
2
+
3
+ # Loading this file is not idemponent and can cause damage when loaded twice.
4
+ # Fail hard and fast.
5
+ fail "Date::Performance already loaded." if defined? Date::Performance
6
+
7
+ class Date
8
+
9
+ # The Date::Performance module is present when the performance enhacing extension
10
+ # has been loaded. It serves no other purpose.
11
+ module Performance
12
+ VERSION = "0.4.7"
13
+ end
14
+
15
+ # The extension replaces Date#strftime but falls back on the stock version when
16
+ # strftime(3) cannot handle the format.
17
+ alias_method :strftime_without_performance, :strftime
18
+
19
+ class << self
20
+ # Ruby 1.8.6 introduced Date.new! and the extension uses it. The method was
21
+ # called new0 in <= 1.8.5.
22
+ alias_method :new!, :new0 unless Date.respond_to?(:new!)
23
+
24
+ # The extension replaces Date.strptime but falls back on the stock version when
25
+ # strptime(3) can't handle the format.
26
+ alias_method :strptime_without_performance, :strptime
27
+ end
28
+
29
+ end
30
+
31
+ # Load up the extension but bring the Date class back to its original state
32
+ # if the extension fails to load properly.
33
+ begin
34
+ require 'date_performance.so'
35
+ rescue
36
+ class Date
37
+ remove_const :Performance
38
+ remove_method :strftime_without_performance
39
+ class << self
40
+ remove_method :strptime_without_performance
41
+ end
42
+ end
43
+ raise
44
+ end
@@ -0,0 +1,68 @@
1
+ $: << 'lib'
2
+ require 'date'
3
+ # require 'date/fast'
4
+
5
+ def profile_new_civil(iterations=1000)
6
+ iterations.times do |i|
7
+ Date.new(1912, 6, 23)
8
+ end
9
+ end
10
+
11
+ def profile_new_jd(iterations=1000)
12
+ jd = Date.civil_to_jd(1912, 6, 23, Date::ITALY)
13
+ iterations.times do |i|
14
+ Date.jd(jd)
15
+ end
16
+ end
17
+
18
+ def profile_comparison(iterations=1000)
19
+ d1, d2 = Date.new(1912, 6, 23), Date.new(1906, 4, 28)
20
+ iterations.times do |i|
21
+ d1 == d2
22
+ end
23
+ end
24
+
25
+ def profile_greater_than(iterations=1000)
26
+ d1, d2 = Date.new(1912, 6, 23), Date.new(1906, 4, 28)
27
+ iterations.times do |i|
28
+ d1 > d2
29
+ end
30
+ end
31
+
32
+ def profile_strftime(iterations=1000)
33
+ date = Date.new(1912, 6, 23)
34
+ iterations.times do |i|
35
+ date.strftime
36
+ end
37
+ end
38
+
39
+ def profile_strftime_after_new(iterations=1000)
40
+ iterations.times do |i|
41
+ Date.new(1912, 6, 23).strftime
42
+ end
43
+ end
44
+
45
+ def profile_strptime(iterations=1000)
46
+ iterations.times do |i|
47
+ Date.strptime '06/23/1912', '%m/%d/%Y'
48
+ end
49
+ end
50
+
51
+ def run_profiles
52
+ require 'benchmark'
53
+ all_methods = private_methods.select{|m| m =~ /^profile_/}.map{|m| m.sub /^profile_/, '' }.sort
54
+ selected_methods =
55
+ if ARGV.empty?
56
+ all_methods
57
+ else
58
+ ARGV
59
+ end
60
+ # warm-up
61
+ selected_methods.each {|method| send "profile_#{method}", 1 }
62
+ selected_methods.each do |method|
63
+ puts method
64
+ puts Benchmark.measure{ send "profile_#{method}", 10000 }
65
+ end
66
+ end
67
+
68
+ run_profiles
@@ -0,0 +1,70 @@
1
+ require 'date/performance'
2
+ require 'date/memoize'
3
+ require 'test/unit'
4
+
5
+ class DateMemoizeTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ Date::Memoize.install!
9
+ end
10
+
11
+ def test_installed_by_default
12
+ assert Date::Memoize.installed?, "didn't install when required"
13
+ end
14
+
15
+ def test_civil_memoization
16
+ expected = Date.new_without_memoization(2002, 03, 22)
17
+ actual = Date.new(2002, 03, 22)
18
+ assert_not_same expected, actual
19
+ assert_equal expected, actual
20
+ expected, actual = actual, Date.new(2002, 03, 22)
21
+ assert_same expected, actual
22
+ assert_equal expected, actual
23
+ end
24
+
25
+ def test_strptime_memoization
26
+ Date::Memoize.uninstall!
27
+ expected = Date.strptime('2002-03-22', '%Y-%m-%d')
28
+ Date::Memoize.install!
29
+ actual = Date.strptime('2002-03-22', '%Y-%m-%d')
30
+ assert_equal expected, actual
31
+ assert_not_same expected, actual
32
+ expected, actual = actual, Date.strptime('2002-03-22', '%Y-%m-%d')
33
+ assert_same expected, actual
34
+ assert_equal expected, actual
35
+ end
36
+
37
+ def test_parse_memoization
38
+ Date::Memoize.uninstall!
39
+ expected = Date.parse('2002-03-22')
40
+ Date::Memoize.install!
41
+ actual = Date.parse('2002-03-22')
42
+ assert_equal expected, actual
43
+ assert_not_same expected, actual, "With (#{actual.to_s}) and without (#{expected.to_s}) memoization are the same (#{actual.object_id}, #{expected.object_id})."
44
+ expected, actual = actual, Date.parse('2002-03-22')
45
+ assert_same expected, actual
46
+ assert_equal expected, actual
47
+ end
48
+
49
+ def test_uninstall_is_detected
50
+ Date::Memoize.uninstall!
51
+ assert !Date::Memoize.installed?, "didn't uninstall or uninstall not detected properly"
52
+ methods = Date.methods.select{|m| m.to_s =~ /_without_memoization$/}
53
+ flunk "Memoization methods not removed: #{methods.inspect}" if methods.any?
54
+ vars = Date.send(:instance_variables).select{|v| v.to_s =~ /^@__memoized_/}
55
+ flunk "Memoization instance variables not removed: #{vars.inspect}" if vars.any?
56
+ end
57
+
58
+ def test_uninstall_removes_methods
59
+ Date::Memoize.uninstall!
60
+ methods = Date.methods.select{|m| m.to_s =~ /_without_memoization$/}
61
+ assert_equal 0, methods.length, "Memoization methods not removed: #{methods.inspect}"
62
+ end
63
+
64
+ def test_uninstall_removes_instance_variables
65
+ Date::Memoize.uninstall!
66
+ vars = Date.send(:instance_variables).select{|v| v.to_s =~ /^@__memoized_/}
67
+ assert_equal 0, vars.length, "Memoization instance variables not removed: #{vars.inspect}"
68
+ end
69
+
70
+ end
@@ -0,0 +1,145 @@
1
+ require 'date/performance'
2
+ require 'test/unit'
3
+
4
+ class ExtensionTest < Test::Unit::TestCase
5
+
6
+ def test_civil_to_jd
7
+ assert_equal 2419577, Date.civil_to_jd(1912, 6, 23)
8
+ assert_equal 2419577, Date.civil_to_jd(1912, 6, 23, Date::GREGORIAN)
9
+ end
10
+
11
+ def test_jd_to_civil
12
+ expected = [ 1912, 6, 23 ]
13
+ assert_equal expected, Date.jd_to_civil(2419577)
14
+ assert_equal expected, Date.jd_to_civil(2419577, Date::GREGORIAN)
15
+ end
16
+
17
+ def test_jd_to_ajd
18
+ expected = Rational(4839153, 2)
19
+ assert_equal expected, Date.jd_to_ajd(2419577)
20
+ assert_equal expected, Date.jd_to_ajd(2419577, 0)
21
+ assert_equal expected, Date.jd_to_ajd(2419577, 0, 0)
22
+ end
23
+
24
+ def test_ajd_to_jd
25
+ ajd = Rational(4839153, 2)
26
+ expected = [ 2419577, Rational(1, 2) ]
27
+ assert_equal(expected, Date.ajd_to_jd(ajd))
28
+ assert_equal(expected, Date.ajd_to_jd(ajd, 0))
29
+ end
30
+
31
+ def test_new
32
+ expected = Date.new!(Rational(4839153, 2))
33
+ assert_equal expected, Date.new(1912, 6, 23)
34
+ assert_equal expected, Date.new(1912, 6, 23, Date::ITALY)
35
+ end
36
+
37
+ def test_new_raises_argument_error
38
+ assert_raise ArgumentError do
39
+ Date.new(1912, 25, 55)
40
+ end
41
+ end
42
+
43
+ def test_civil_cached_on_new
44
+ date = Date.new(1912, 6, 23)
45
+ assert_not_nil(expected = date.send(:instance_variable_get, :@__civil__))
46
+ assert_equal expected, date.civil
47
+ end
48
+
49
+ def test_civil_cached_on_new!
50
+ date = Date.new!(Rational(4839153, 2))
51
+ assert !date.send(:instance_variables).include?('@__civil__'), '@__civil__ ivar should not exist'
52
+ assert_equal([1912, 6, 23], date.civil)
53
+ assert_equal([1912, 6, 23], date.instance_variable_get(:@__civil__))
54
+ end
55
+
56
+ def test_sys_strftime
57
+ date = Date.new(1912, 6, 23)
58
+ assert_equal "1912-06-23", date.sys_strftime
59
+ assert_equal "06/23/1912", date.sys_strftime("%m/%d/%Y")
60
+ end
61
+
62
+ def test_sys_strptime
63
+ assert_equal Date.new(1912, 6, 23), Date.sys_strptime("1912-06-23")
64
+ assert_equal Date.new(1912, 6, 23), Date.sys_strptime("1912-06-23", "%Y-%m-%d")
65
+ end
66
+
67
+ # This falls back on Ruby's strptime on BSD systems because BSD's strptime doesn't
68
+ # handle years before 1900.
69
+ def test_strptime_fallback
70
+ assert_equal Date.new(1, 1, 1), Date.strptime("01/01/0001", "%m/%d/%Y")
71
+ end
72
+
73
+ # Make sure we're not breaking DateTime
74
+ def test_datetime
75
+ datetime = DateTime.parse("1912-06-23T04:20:37Z")
76
+ assert_equal 1912, datetime.year
77
+ assert_equal 6, datetime.month
78
+ assert_equal 23, datetime.day
79
+ assert_equal 4, datetime.hour
80
+ assert_equal 20, datetime.min
81
+ assert_equal 37, datetime.sec
82
+ end
83
+
84
+ def test_new_fails_with_nils
85
+ assert_raise(TypeError) { Date.new(nil, nil, nil) }
86
+ assert_raise(TypeError) { Date.new(2007, nil, nil) }
87
+ assert_raise(TypeError) { Date.new(2007, 9, nil) }
88
+ end
89
+
90
+ def test_strftime_with_weekday
91
+ assert_equal "Monday", Date.new(2007, 1, 1).strftime("%A")
92
+ end
93
+
94
+ def test_strftime_with_datetime
95
+ dt = DateTime.new(2007, 1, 1, 4, 20, 00)
96
+ assert_equal "2007-01-01T04:20:00+00:00", dt.strftime("%FT%T%:z")
97
+ end
98
+
99
+ def test_constructor_with_negative_days
100
+ #leap year
101
+ month_ends = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
102
+ (1..12).each do |m|
103
+ d = Date.new(2008, m, -1)
104
+ assert_equal d.day, month_ends[m-1]
105
+ end
106
+ #normal year
107
+ month_ends = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
108
+ (1..12).each do |m|
109
+ d = Date.new(2009, m, -1)
110
+ assert_equal d.day, month_ends[m-1]
111
+ end
112
+ #before calendar reform for Italy
113
+ month_ends = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
114
+ (1..12).each do |m|
115
+ d = Date.new(1581, m, -1)
116
+ assert_equal d.day, month_ends[m-1]
117
+ end
118
+ end
119
+
120
+ def test_date_comparisons
121
+ #regular Date class comparison
122
+ d = Date.new(2008, 1, 1)
123
+ 366.times do |i|
124
+ assert_equal( 0, d+i <=> d+i )
125
+ assert_equal(-1, d+i <=> d+i+1 )
126
+ assert_equal( 1, d+i+1 <=> d+i )
127
+ end
128
+
129
+ #DateTime comparison
130
+ dt1 = DateTime.new(2006,1,1,12,15,30)
131
+ dt2 = DateTime.new(2006,1,1,12,15,31)
132
+ dt3 = DateTime.new(2006,1,1,12,15,29)
133
+
134
+ assert_equal( 0, DateTime.new(2006,1,1,12,15,30) <=> dt1 )
135
+ assert_equal( 1, dt1 <=> dt3 )
136
+ assert_equal( -1, dt1 <=> dt2 )
137
+
138
+ #comparison with some random type
139
+ assert_equal nil, dt1 <=> "foo"
140
+
141
+ #comparison with Fixnum that represents ajd
142
+ assert_equal(-1, d <=> d.ajd.numerator )
143
+ end
144
+
145
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: seamusabshere-date-performance
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Tomayko
8
+ - Alexander Dymo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-05-14 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Adds some semblance of performance to Ruby's core Date class.
18
+ email: Ryan Tomayko <r@tomayko.com>
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README.md
25
+ files:
26
+ - AUTHORS
27
+ - BENCHMARKS
28
+ - README.md
29
+ - Rakefile
30
+ - VERSION.yml
31
+ - ext/date_performance.c
32
+ - ext/extconf.rb
33
+ - lib/date/memoize.rb
34
+ - lib/date/performance.rb
35
+ - test/benchmarks.rb
36
+ - test/date_memoize_test.rb
37
+ - test/extension_test.rb
38
+ has_rdoc: true
39
+ homepage: http://tomayko.com/src/date-performance/
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.2.0
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Adds some semblance of performance to Ruby's core Date class.
64
+ test_files:
65
+ - test/benchmarks.rb
66
+ - test/date_memoize_test.rb
67
+ - test/extension_test.rb