partial-date 1.1.9 → 1.1.10

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/.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