edtf 0.0.6 → 0.0.7

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/lib/edtf/parser.y CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  class EDTF::Parser
4
4
 
5
- token T Z E X U UNKNOWN OPEN LONGYEAR UNMATCHED DOTS IUA
5
+ token T Z E X U UNKNOWN OPEN LONGYEAR UNMATCHED DOTS PUA
6
6
 
7
7
  expect 0
8
8
 
@@ -133,7 +133,7 @@ rule
133
133
  result = Interval.new(val[0], val[2])
134
134
  }
135
135
 
136
- level_1_start : date | internal_uncertain_or_approximate_date | UNKNOWN
136
+ level_1_start : date | partial_uncertain_or_approximate | unspecified | partial_unspecified | UNKNOWN
137
137
 
138
138
  level_1_end : level_1_start | OPEN
139
139
 
@@ -159,7 +159,7 @@ rule
159
159
  ;
160
160
 
161
161
 
162
- season : year '-' season_number { result = Season.new(val[0], val[2]) }
162
+ season : year '-' season_number opt_ua { result = Season.new(val[0], val[2]) }
163
163
 
164
164
  season_number : '2' '1' { result = 21 }
165
165
  | '2' '2' { result = 22 }
@@ -172,8 +172,8 @@ rule
172
172
 
173
173
  # NB: Level 2 Intervals are covered by the Level 1 Interval rules.
174
174
  level_2_expression : season_qualified
175
- | internal_uncertain_or_approximate_date
176
- | internal_unspecified
175
+ | partial_uncertain_or_approximate
176
+ | partial_unspecified
177
177
  | choice_list
178
178
  | inclusive_list
179
179
  | masked_precision
@@ -239,7 +239,7 @@ rule
239
239
  ;
240
240
 
241
241
  list_element : date
242
- | internal_uncertain_or_approximate_date
242
+ | partial_uncertain_or_approximate
243
243
  | unspecified
244
244
  | consecutives { result = val[0].map { |d| Date.new(*d) } }
245
245
  ;
@@ -256,7 +256,7 @@ rule
256
256
  | year DOTS year { result = (val[0]..val[2]).to_a.map }
257
257
  ;
258
258
 
259
- internal_unspecified :
259
+ partial_unspecified :
260
260
  unspecified_year '-' month '-' d01_31
261
261
  {
262
262
  result = Date.new(val[0][0], val[2], val[4])
@@ -287,22 +287,23 @@ rule
287
287
  }
288
288
  ;
289
289
 
290
- internal_uncertain_or_approximate_date : internal_uncertain_or_approximate
291
- | '(' internal_uncertain_or_approximate ')' ua { result = uoa(val[1], val[3]) }
292
290
 
293
- internal_uncertain_or_approximate :
294
- iua_year { result = val[0]; result.precision = :year }
295
- | iua_year_month { result = val[0]; result.precision = :month }
296
- | iua_year_month_day
297
-
298
- iua_year : year ua { result = uoa(Date.new(val[0]), val[1], :year) }
299
-
300
- iua_year_month :
301
- iua_year '-' month opt_ua
291
+ partial_uncertain_or_approximate : pua_base
292
+ | '(' pua_base ')' ua { result = uoa(val[1], val[3]) }
293
+
294
+ pua_base :
295
+ pua_year { result = val[0]; result.precision = :year }
296
+ | pua_year_month { result = val[0]; result.precision = :month }
297
+ | pua_year_month_day
298
+
299
+ pua_year : year ua { result = uoa(Date.new(val[0]), val[1], :year) }
300
+
301
+ pua_year_month :
302
+ pua_year '-' month opt_ua
302
303
  {
303
304
  result = uoa(val[0].change(:month => val[2]), val[3], [:month, :year])
304
305
  }
305
- | '(' iua_year IUA month opt_ua
306
+ | '(' pua_year PUA month opt_ua
306
307
  {
307
308
  result = uoa(uoa(val[1], val[2], :year).change(:month => val[3]), val[4], :month)
308
309
  }
@@ -315,21 +316,21 @@ rule
315
316
  result = uoa(Date.new(val[0], val[3]), val[5], [:month])
316
317
  }
317
318
  ;
318
-
319
- iua_year_month_day :
320
- iua_year_month '-' d01_31 opt_ua
319
+
320
+ pua_year_month_day :
321
+ pua_year_month '-' d01_31 opt_ua
321
322
  {
322
323
  result = uoa(val[0].change(:day => val[2]), val[3])
323
324
  }
324
- | iua_year_month '-' '(' d01_31 ')' ua
325
+ | pua_year_month '-' '(' d01_31 ')' ua
325
326
  {
326
327
  result = uoa(val[0].change(:day => val[3]), val[5], [:day])
327
328
  }
328
- | '(' iua_year_month IUA d01_31 opt_ua
329
+ | '(' pua_year_month PUA d01_31 opt_ua
329
330
  {
330
331
  result = uoa(uoa(val[1], val[2], [:year, :month]).change(:day => val[3]), val[4], :day)
331
332
  }
332
- | year '-' '(' month IUA d01_31 opt_ua
333
+ | year '-' '(' month PUA d01_31 opt_ua
333
334
  {
334
335
  result = uoa(uoa(Date.new(val[0], val[3], val[5]), val[4], :month), val[6], :day)
335
336
  }
@@ -447,14 +448,22 @@ require 'strscan'
447
448
  @options = Parser.defaults.merge(options)
448
449
  end
449
450
 
450
- def parse(input)
451
+ def parse(input)
452
+ parse!(input)
453
+ rescue => e
454
+ warn e.message if options[:debug]
455
+ nil
456
+ end
457
+
458
+ def parse!(input)
451
459
  @yydebug = @options[:debug] || ENV['DEBUG']
452
460
  @src = StringScanner.new(input)
453
461
  do_parse
454
462
  end
455
463
 
456
464
  def on_error(tid, val, vstack)
457
- warn "failed to parse extended date time %s (%s) %s" % [val.inspect, token_to_str(tid) || '?', vstack.inspect]
465
+ raise ArgumentError,
466
+ "failed to parse extended date time %s [%s]: %s" % [val.inspect, token_to_str(tid) || '?', vstack.inspect]
458
467
  end
459
468
 
460
469
  def apply_uncertainty(date, uncertainty, scope = nil)
@@ -475,11 +484,11 @@ require 'strscan'
475
484
  when @src.scan(/\(/)
476
485
  ['(', @src.matched]
477
486
  when @src.scan(/\)\?~-/)
478
- [:IUA, [:uncertain!, :approximate!]]
487
+ [:PUA, [:uncertain!, :approximate!]]
479
488
  when @src.scan(/\)\?-/)
480
- [:IUA, [:uncertain!]]
489
+ [:PUA, [:uncertain!]]
481
490
  when @src.scan(/\)~-/)
482
- [:IUA, [:approximate!]]
491
+ [:PUA, [:approximate!]]
483
492
  when @src.scan(/\)/)
484
493
  [')', @src.matched]
485
494
  when @src.scan(/\[/)
data/lib/edtf/season.rb CHANGED
@@ -28,7 +28,8 @@ module EDTF
28
28
 
29
29
  attr_accessor :qualifier
30
30
 
31
- def_delegators :to_range, :each
31
+ def_delegators :to_range,
32
+ *Range.instance_methods(false).reject { |m| m.to_s =~ /^(each|eql?|hash)$/ }
32
33
 
33
34
  SEASONS.each_value do |s|
34
35
  define_method("#{s}?") { @season == s }
@@ -65,6 +66,15 @@ module EDTF
65
66
  end
66
67
  end
67
68
 
69
+ def each
70
+ if block_given?
71
+ to_range(&Proc.new)
72
+ self
73
+ else
74
+ to_enum
75
+ end
76
+ end
77
+
68
78
  def year=(new_year)
69
79
  @year = new_year.to_i
70
80
  end
@@ -82,12 +92,12 @@ module EDTF
82
92
  '%04d-%2d%s' % [year, CODES[season], qualified? ? "^#{qualifier}" : '']
83
93
  end
84
94
 
85
- alias to_edtf to_s
95
+ alias edtf to_s
86
96
 
87
97
  def <=>(other)
88
98
  case other
89
99
  when Date
90
- include?(other) ? 0 : to_date <=> other
100
+ cover?(other) ? 0 : to_date <=> other
91
101
  when Season
92
102
  [year, month, qualifier] <=> [other.year, other.month, other.qualifier]
93
103
  else
@@ -104,22 +114,13 @@ module EDTF
104
114
  end
105
115
 
106
116
  def to_date
107
- Date.new(year, month)
117
+ Date.new(year, month, 1)
108
118
  end
109
119
 
110
- # def include?(other)
111
- # case other
112
- # when Date
113
- # d = to_date
114
- # other >= d && other <= d.months_since(3).end_of_month
115
- # else
116
- # false
117
- # end
118
- # end
119
-
120
+ # Returns a Range that covers the season (a three month period).
120
121
  def to_range
121
122
  d = to_date
122
- d .. d.months_since(3).end_of_month
123
+ d .. d.months_since(2).end_of_month
123
124
  end
124
125
 
125
126
  protected
@@ -1,59 +1,95 @@
1
1
  module EDTF
2
-
3
- class Uncertainty < Struct.new(:year, :month, :day, :hour, :minute, :second)
4
-
2
+
3
+ # TODO use bitmasks instead of arrays
4
+
5
+ class Uncertainty < Struct.new(:year, :month, :day)
6
+
7
+ attr_reader :hash_base
8
+
9
+ def initialize(year = nil, month = nil, day = nil, hash_base = 1)
10
+ @hash_base = hash_base
11
+ super(year, month, day)
12
+ end
13
+
14
+ def hash_base=(base)
15
+ @hash_map = false
16
+ @hash_base = base
17
+ end
18
+
5
19
  def uncertain?(parts = members)
6
- [parts].flatten.any? { |p| !!send(p) }
20
+ [*parts].any? { |p| !!send(p) }
7
21
  end
8
22
 
9
23
  def uncertain!(parts = members)
10
- [parts].flatten.each { |p| send("#{p}=", true) }
24
+ [*parts].each { |p| send("#{p}=", true) }
11
25
  self
12
26
  end
13
27
 
14
28
  def certain?(parts = members); !uncertain?(parts); end
15
-
29
+
16
30
  def certain!(parts = members)
17
- [parts].flatten.each { |p| send("#{p}=", false) }
31
+ [*parts].each { |p| send("#{p}=", false) }
18
32
  self
19
33
  end
34
+
35
+ def eql?(other)
36
+ hash == other.hash
37
+ end
38
+
39
+ def hash
40
+ values.zip(hash_map).reduce(0) { |s, (v, h)| s + (v ? h : 0) }
41
+ end
42
+
43
+ private
44
+
45
+ def hash_map
46
+ @hash_map ||= (0...length).map { |i| hash_base << i }
47
+ end
48
+
20
49
  end
21
50
 
22
- # year = []
51
+
23
52
  class Unspecified < Struct.new(:year, :month, :day)
24
-
53
+
54
+ U = 'u'.freeze
55
+
25
56
  def initialize
26
- super year = Array.new(4), month = Array.new(2), day = Array.new(2)
57
+ super Array.new(4),Array.new(2), Array.new(2)
27
58
  end
28
-
59
+
29
60
  def unspecified?(parts = members)
30
- [parts].flatten.any? { |p| send(p).any? { |u| !!u } }
61
+ [*parts].any? { |p| send(p).any? { |u| !!u } }
31
62
  end
32
-
63
+
33
64
  def unspecified!(parts = members)
34
- [parts].flatten.each { |p| send(p).map! { true } }
65
+ [*parts].each { |p| send(p).map! { true } }
35
66
  self
36
67
  end
37
68
 
38
69
  def specified?(parts = members); !unspecified?(parts); end
39
70
 
40
71
  def specified!(parts = members)
41
- [parts].flatten.each { |p| send(p).map! { false } }
72
+ [*parts].each { |p| send(p).map! { false } }
42
73
  self
43
74
  end
44
-
75
+
45
76
  alias specific? specified?
46
77
  alias unspecific? unspecified?
47
78
 
48
79
  alias specific! specified!
49
80
  alias unspecific! unspecified!
50
-
81
+
51
82
  private :year=, :month=, :day=
52
-
83
+
53
84
  def to_s
54
- [year, month, day].map { |p| p.map { |i| i ? 'u' : 's' }.join }.join('-')
85
+ mask(%w{ ssss ss ss }).join('-')
86
+ end
87
+
88
+ def mask(values)
89
+ values.zip(members.take(values.length)).map do |value, mask|
90
+ value.split(//).zip(send(mask)).map { |v,m| m ? U : v }.join
91
+ end
55
92
  end
56
-
57
93
  end
58
-
94
+
59
95
  end
data/lib/edtf/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module EDTF
2
- VERSION = '0.0.6'.freeze
2
+ VERSION = '0.0.7'.freeze
3
3
  end
@@ -38,29 +38,83 @@ describe 'Date/DateTime' do
38
38
  end
39
39
 
40
40
  end
41
+
42
+ describe 'precisions' do
43
+ let(:date) { Date.today }
44
+
45
+ it 'has day precision by default' do
46
+ date.should be_day_precision
47
+ end
48
+
49
+ it '#day_precison returns a new date object' do
50
+ date.day_precision.should_not equal(date)
51
+ date.day_precision.should == date
52
+ end
53
+
54
+ end
55
+
41
56
 
42
57
  describe '#negate' do
43
58
  let(:date) { Date.edtf('2004?') }
44
59
 
45
- it 'should return a new date with the negated year' do
60
+ it 'returns a new date with the negated year' do
46
61
  date.negate.year.should == (date.year * -1)
47
62
  end
48
63
 
49
- it 'should return a new date with the same month and day' do
64
+ it 'returns a new date with the same month' do
50
65
  date.negate.month.should == date.month
66
+ end
67
+
68
+ it 'returns a new date with the same day' do
51
69
  date.negate.day.should == date.day
52
70
  end
53
71
 
54
- it 'should return a new date with the same precision' do
72
+ it 'returns a new date with the same precision' do
55
73
  date.negate.precision.should == date.precision
56
74
  end
57
75
  end
58
76
 
77
+ describe '#succ' do
78
+
79
+ it 'the successor of 2004 is 2005' do
80
+ Date.edtf('2004').succ.year.should == 2005
81
+ end
82
+
83
+ it 'the successor of 2004-03 is 2004-04' do
84
+ Date.edtf('2004-03').succ.strftime('%Y-%m').should == '2004-04'
85
+ end
86
+
87
+ it 'the successor of 2004-03-01 is 2004-03-02' do
88
+ Date.edtf('2004-03-01').succ.strftime('%Y-%m-%d').should == '2004-03-02'
89
+ end
90
+
91
+ it "the successor of 1999 has year precision" do
92
+ Date.edtf('1999').succ.should be_year_precision
93
+ end
94
+
95
+ end
96
+
97
+ describe '#next' do
98
+
99
+ it 'returns the successor when given no argument' do
100
+ Date.edtf('1999').next.year.should == 2000
101
+ end
102
+
103
+ it 'returns an array of the next 3 elements when passed 3 as an argument' do
104
+ Date.edtf('1999').next(3).map(&:year) == [2000, 2001, 2002]
105
+ end
106
+
107
+ end
108
+
109
+
59
110
  describe '#change' do
60
111
  let(:date) { Date.edtf('2004-09?~') }
61
112
 
62
113
  it 'returns a copy of the date if given empty option hash' do
63
114
  date.change({}).should == date
115
+ end
116
+
117
+ it 'the returned copy is not identical at object level' do
64
118
  date.change({}).should_not equal(date)
65
119
  end
66
120
 
@@ -114,5 +168,78 @@ describe 'Date/DateTime' do
114
168
  end
115
169
 
116
170
  end
171
+
172
+ describe 'uncertain/approximate hash' do
173
+
174
+ it 'is 0 by default' do
175
+ Date.new().send(:ua_hash).should == 0
176
+ end
177
+
178
+ it 'is 8 for approximate year' do
179
+ Date.new.approximate!(:year).send(:ua_hash).should == 8
180
+ end
181
+
182
+ end
183
+
184
+ describe 'sorting and comparisons' do
185
+ let(:xmas) { Date.new(2011, 12, 24) }
186
+ let(:new_years_eve) { Date.new(2011, 12, 31) }
187
+
188
+ describe '#values' do
189
+
190
+ it 'returns [2011,12,24] for christmas' do
191
+ xmas.values.should == [2011,12,24]
192
+ end
193
+
194
+ it 'returns [2011,12] for christmas with month precision' do
195
+ xmas.month_precision!.values.should == [2011,12]
196
+ end
197
+
198
+ it 'returns [2011] for christmas with year precision' do
199
+ xmas.year_precision!.values.should == [2011]
200
+ end
201
+
202
+ end
203
+
204
+ describe '#<=>' do
205
+
206
+ it '2009-12-24 should be less than 2011-12-31' do
207
+ Date.new(2009,12,24).should < Date.new(2011,12,31)
208
+ end
209
+
210
+ it '2009-12-24 should be less than 2011-12' do
211
+ Date.new(2009,12,24).should < Date.edtf('2011-12')
212
+ end
213
+
214
+ it '2009-12-24 should be less than 2011' do
215
+ Date.new(2009,12,24).should < Date.edtf('2011')
216
+ end
217
+
218
+
219
+ end
220
+
221
+ describe '#==' do
222
+
223
+ it "xmas and new year's eve are not equal" do
224
+ xmas.should_not == new_years_eve
225
+ end
226
+
227
+ it "xmas and new year's eve are equal using month percision" do
228
+ xmas.month_precision!.should == new_years_eve.month_precision!
229
+ end
230
+
231
+ it "xmas and january 24 are not equal using month percision" do
232
+ xmas.month_precision!.should_not == xmas.month_precision!.next
233
+ end
234
+
235
+
236
+ it "xmas and new year's eve are equal using year percision" do
237
+ xmas.year_precision!.should == new_years_eve.year_precision!
238
+ end
239
+
240
+ end
241
+
242
+ end
243
+
117
244
 
118
245
  end