edtf 0.0.6 → 0.0.7

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