partial-date 1.1.9 → 1.1.10

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ tmp/
1
2
  coverage/
2
3
  .yardoc/
3
4
  doc/
data/ChangeLog.markdown CHANGED
@@ -1,5 +1,9 @@
1
1
  ### 1.1.9 / 2012-05-31
2
2
 
3
+ * Implemented faster Date#.to_s method.
4
+
5
+ ### 1.1.9 / 2012-05-31
6
+
3
7
  * BugFix: to_s will preserve minus sign for negative year when formatter starts with %Y
4
8
  * BugFix: <=> correct for negative dates.
5
9
 
data/Rakefile CHANGED
@@ -3,6 +3,13 @@
3
3
  require 'rubygems'
4
4
  require 'rake'
5
5
 
6
+ # Benchmark setup inspired by https://github.com/stonean/slim
7
+ # Call 'rake benchmark iterations=10000' to set iterations
8
+ desc 'Run PartialDate benchmarks (default iterations=1000)'
9
+ task :benchmark, :iterations do
10
+ ruby('benchmarks/run-benchmarks.rb')
11
+ end
12
+
6
13
  begin
7
14
  gem 'rubygems-tasks', '~> 0.2'
8
15
  require 'rubygems/tasks'
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'), File.dirname(__FILE__))
4
+
5
+ require 'partial-date'
6
+ require 'benchmark'
7
+ require 'date'
8
+
9
+ class PartialDateBenchmarks
10
+ def initialize(iterations)
11
+ @iterations = (iterations || 10000).to_i
12
+ @benches = []
13
+
14
+ stdlib_date = Date.new(2012,12,1)
15
+ partial_date = PartialDate::Date.new {|d| d.year = 2012; d.month = 12; d.day = 1}
16
+
17
+ bench('(1a) create empty stdlib date objects') do |d|
18
+ Date.new
19
+ end
20
+
21
+ bench('(1b) create empty partial-date objects') do |d|
22
+ PartialDate::Date.new
23
+ end
24
+
25
+ bench('(2a) create populated stdlib date objects') do |d|
26
+ Date.new(2012,12,1)
27
+ end
28
+
29
+ bench('(2b) create populated partial-date objects from block') do |d|
30
+ PartialDate::Date.new { |d| d.year = 2012; d.month = 12; d.day = 1 }
31
+ end
32
+
33
+ bench('(3) create partial-date objects from load method') do |d|
34
+ PartialDate::Date.load 20121201
35
+ end
36
+
37
+ bench('(4a) read stdlib date parts') do |d|
38
+ stdlib_date.year
39
+ stdlib_date.month
40
+ stdlib_date.day
41
+ end
42
+
43
+ bench('(4b) read partial-date date parts') do |d|
44
+ partial_date.year
45
+ partial_date.month
46
+ partial_date.day
47
+ end
48
+
49
+ bench('(5a) call stdlib date strftime') do |d|
50
+ stdlib_date.strftime('%Y-%m-%d')
51
+ end
52
+
53
+ bench('(5b) call partial-date to_s') do |d|
54
+ partial_date.to_s
55
+ end
56
+
57
+ bench('(5c) call partial-date old to_s') do |d|
58
+ partial_date.old_to_s
59
+ end
60
+ end
61
+
62
+ def run
63
+ puts "#{@iterations} Iterations"
64
+ Benchmark.bmbm do |x|
65
+ @benches.each do |name, block|
66
+ x.report name.to_s do
67
+ @iterations.to_i.times { block.call }
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def bench(name, &block)
74
+ @benches.push([name, block])
75
+ end
76
+ end
77
+
78
+ PartialDateBenchmarks.new(ENV['iterations']).run
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'), File.dirname(__FILE__))
4
+
5
+ require 'partial-date'
6
+ require 'date'
7
+
8
+
9
+ class PartialDateMemoryProfile
10
+ def initialize(iterations)
11
+ @tmp_dir = File.expand_path('../tmp', __FILE__)
12
+ Dir.mkdir @tmp_dir unless Dir.exists? @tmp_dir
13
+
14
+ @iterations = (iterations || 100000).to_i
15
+ @profiles = []
16
+ @memory = []
17
+
18
+ # profile('create_stdlib_date') do |d|
19
+ # @memory << Date.new(2012,12,1)
20
+ # end
21
+
22
+ profile('create_partial_date') do |d|
23
+ @memory << PartialDate::Date.new { |d| d.year - 2012; d.month = 12; d.day = 1}
24
+ end
25
+
26
+ # profile('load_partial_date') do |d|
27
+ # PartialDate::Date.load(20121201)
28
+ # end
29
+ end
30
+
31
+ def run
32
+ puts "#{@iterations} Iterations"
33
+ GC::Profiler.enable()
34
+ @profiles.each do |name, block|
35
+ count = 1
36
+ @iterations.times do
37
+ block.call
38
+ if count % 1000 == 0
39
+ puts " GC Profile Report #{ count.to_s.rjust(5, '0') } for #{ name } and #{ @memory.length} objects."
40
+ puts GC::Profiler.report()
41
+ end
42
+ count += 1
43
+ end
44
+ end
45
+ end
46
+
47
+ def profile(name, &block)
48
+ @profiles.push([@tmp_dir + "/" + name, block])
49
+ end
50
+ end
51
+
52
+ PartialDateMemoryProfile.new(ENV['iterations']).run
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'), File.dirname(__FILE__))
4
+
5
+ require 'perftools'
6
+ require 'partial-date'
7
+ require 'date'
8
+
9
+ class PartialDateCPUProfile
10
+ def initialize(iterations)
11
+ @tmp_dir = File.expand_path('../tmp', __FILE__)
12
+ Dir.mkdir @tmp_dir unless Dir.exists? @tmp_dir
13
+
14
+ @iterations = (iterations || 100000).to_i
15
+ @profiles = []
16
+
17
+ profile('create_stdlib_date') do |d|
18
+ Date.new(2012,12,1)
19
+ end
20
+
21
+ profile('create_partial_date') do |d|
22
+ PartialDate::Date.new { |d| d.year - 2012; d.month = 12; d.day = 1}
23
+ end
24
+
25
+ profile('load_partial_date') do |d|
26
+ PartialDate::Date.load(20121201)
27
+ end
28
+ end
29
+
30
+ def run
31
+ puts "#{@iterations} Iterations"
32
+ @profiles.each do |name, block|
33
+ PerfTools::CpuProfiler.start(name) do
34
+ @iterations.times { block.call }
35
+ #@iterations.to_i.times { block.call }
36
+ end
37
+ system "pprof.rb --gif #{name} > #{name}.gif"
38
+ end
39
+ end
40
+
41
+ def profile(name, &block)
42
+ @profiles.push([@tmp_dir + "/" + name, block])
43
+ end
44
+ end
45
+
46
+ PartialDateCPUProfile.new(ENV['iterations']).run
@@ -22,6 +22,8 @@ module PartialDate
22
22
  YEAR_SHIFT = 9
23
23
  MONTH_SHIFT = 5
24
24
 
25
+ REMOVALS = /[\/\,\-\s]/
26
+
25
27
  # TODO: Implement i18n support detecting whether a load path has been set or not
26
28
  # and if not - setting it here to a default set of translations that match
27
29
  # the generally available tranlsations for localizing dates.
@@ -145,7 +147,7 @@ module PartialDate
145
147
  if value.is_a?(Integer) && (value >= -10485761231 && value <= 10485761231)
146
148
  @bits = self.class.set_date(@bits, value)
147
149
  else
148
- raise PartialDateError, "Date value must be an integer betwen -10485761231 and 10485761231"
150
+ raise PartialDateError, "Date value must be an integer betwen -10485761231 and 10485761231"
149
151
  end
150
152
  end
151
153
 
@@ -212,10 +214,10 @@ module PartialDate
212
214
  # Public: Set the day portion of a partial date. Day is optional so zero,
213
215
  # nil and empty strings are allowed.
214
216
  def day=(value)
215
- raise DayError, "A month must be set before a day" if month == 0
216
-
217
217
  value = 0 if value.nil?
218
218
 
219
+ raise DayError, "A month must be set before a day" if month == 0 && value !=0
220
+
219
221
  if value.is_a?(String)
220
222
  if value =~ /\A\d{1,2}\z/
221
223
  value = value.to_i
@@ -246,8 +248,8 @@ module PartialDate
246
248
  # %Y - Year with century (can be negative, 4 digits at least)
247
249
  # -0001, 0000, 1995, 2009, 14292, etc.
248
250
  # %m - Month of the year, zero-padded (01..12)
249
- # %B - The full month name (``January'')
250
- # %b - The abbreviated month name (``Jan'')
251
+ # %B - The full month name ('January')
252
+ # %b - The abbreviated month name ('Jan')
251
253
  # %d - Day of the month, zero-padded (01..31)
252
254
  # %e - Day of the month, blank-padded ( 1..31)
253
255
  #
@@ -261,6 +263,35 @@ module PartialDate
261
263
  def to_s(format = :default)
262
264
  format = FORMATS[format] if format.is_a?(Symbol)
263
265
 
266
+ s = format.dup
267
+
268
+ n = b = 0
269
+ a = 1
270
+ while n < s.length
271
+ if s[n] == "%"
272
+ t = FORMAT_METHODS[s[n..n+1]].call( self )
273
+ if t.length == 0
274
+ if n >= 0 && n < s.length - 2
275
+ a = a + 1 if s[n+2] =~ REMOVALS
276
+ else
277
+ b = n - 1 if s[n-1] =~ REMOVALS
278
+ end
279
+ end
280
+ s.slice!(b..n+a)
281
+ s.insert(b, t)
282
+ n = b = b + t.length
283
+ a = 1
284
+ else
285
+ n = b += 1
286
+ end
287
+ end
288
+ s
289
+ end
290
+
291
+ # Here for the moment for benchmark comparisons
292
+ def old_to_s(format = :default)
293
+ format = FORMATS[format] if format.is_a?(Symbol)
294
+
264
295
  result = format.dup
265
296
  FORMAT_METHODS.each_pair do |key, value|
266
297
  result.gsub!( key, value.call( self )) if result.include? key
@@ -272,9 +303,9 @@ module PartialDate
272
303
  # Remove any trailing "/-," chars.
273
304
  # Anything else - you're on your own ;-)
274
305
  lead_trim = (year != 0 && format.lstrip.start_with?("%Y")) ? /\A[\/\,\s]+/ : /\A[\/\,\-\s]+/
275
- result = result.gsub(lead_trim, '').gsub(/\s\s/, ' ').gsub(/[\/\-\,]([\/\-\,])/, '\1').gsub(/[\/\,\-\s]+\z/, '')
306
+ result = result.gsub(lead_trim, '').gsub(/\s\s/, ' ').gsub(/[\/\-\,]([\/\-\,])/, '\1').gsub(/[\/\,\-\s]+\z/, '')
276
307
  end
277
-
308
+
278
309
  # Public: Spaceship operator for date comparisons. Comparisons
279
310
  # are made by cascading down from year, to month to day. This
280
311
  # should be faster than passing to self.value <=> other_date.value
@@ -1,4 +1,4 @@
1
1
  module PartialDate
2
2
  # partial-date version
3
- VERSION = "1.1.9"
3
+ VERSION = "1.1.10"
4
4
  end
data/spec/to_s_spec.rb ADDED
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'partial-date'
3
+
4
+ describe PartialDate::Date do
5
+
6
+ let(:date) { PartialDate::Date.new }
7
+
8
+ describe "to string" do
9
+ it "should be fast" do
10
+ date.year = 2012; date.month = 12; date.day = 1
11
+ puts date.to_s
12
+ end
13
+
14
+ it "should not have a dash at the end if day is missing" do
15
+ date.year = 2012; date.month = 12; date.day = 0
16
+ puts date.to_s
17
+ end
18
+
19
+ it "it should not have a trailing dash if month and day are missing" do
20
+ date.year = 2012; date.month = 0; date.day = 0
21
+ puts date.to_s
22
+ end
23
+
24
+ it "it should not have a leading dash if year is missing" do
25
+ date.year = 0; date.month = 12; date.day = 1
26
+ puts date.to_s
27
+ end
28
+
29
+ it "it should preserve the minus sign if year is negative" do
30
+ date.year = -1000; date.month = 12; date.day = 1
31
+ puts date.to_s
32
+ end
33
+ end
34
+ end
35
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: partial-date
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.9
4
+ version: 1.1.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-05-30 00:00:00.000000000 Z
12
+ date: 2012-05-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rubygems-tasks
@@ -77,6 +77,9 @@ files:
77
77
  - LICENSE.txt
78
78
  - README.markdown
79
79
  - Rakefile
80
+ - benchmarks/run-benchmarks.rb
81
+ - benchmarks/run-gcprof.rb
82
+ - benchmarks/run-perftools.rb
80
83
  - lib/partial-date.rb
81
84
  - lib/partial-date/date.rb
82
85
  - lib/partial-date/error.rb
@@ -84,6 +87,7 @@ files:
84
87
  - partial-date.gemspec
85
88
  - spec/date_spec.rb
86
89
  - spec/spec_helper.rb
90
+ - spec/to_s_spec.rb
87
91
  homepage: https://github.com/58bits/partial-date#readme
88
92
  licenses:
89
93
  - MIT
@@ -113,3 +117,4 @@ summary: A simple date class that can be used to store partial date values in a
113
117
  test_files:
114
118
  - spec/date_spec.rb
115
119
  - spec/spec_helper.rb
120
+ - spec/to_s_spec.rb