edtf 3.3.0 → 4.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e91fb341cfc4a8bc2ba77ed40f51240510578ef0c535b8b7ae44812328d69cdf
4
- data.tar.gz: 89af7cf2bdfb4dfdd644d88a4765f2cd7487e479ee8f75bcea75d0da7e42b83d
3
+ metadata.gz: 68cf5a59b655ee503dfda487dd719422299c40ee1744baeb93605b5b08bd0c9f
4
+ data.tar.gz: 2b8e4a4e41c46f9607057c2b61720d3fce35acae2afa73ea112ff3bfdc03dc5e
5
5
  SHA512:
6
- metadata.gz: 48847ec7463218a2fd6b505271da96471b0341e5e88c5cd11ff302fd636040f580dc2a78be5c1c22f33b225e93ffecf6a3dda9d9e4fa5e5ff998636e936c3e0d
7
- data.tar.gz: 1fbe3d006b3a72cf21a4c4ca07a5933b888a32c221158f32230f1011e159583c79fea5e8d8d00b1b1b434ecdf36cbc2b59b5e5d02b8b73124f26d3f51d79c11c
6
+ metadata.gz: 34e2bf30004ba2eb3389d69fd9244cff29ddcf915e477e894b513116365efb0862585d2fa078786d5f40c22cfbddfec23d26a361886be0a78013f1b4457e0b93
7
+ data.tar.gz: fa871f182f1ec791e36946c0a8a5127323c6b4da1aa6915b625f180a2e0698d586f6452d4d969f2d9f4345195838a92a4ee6414068354334f79a27ceb6fc0000
data/README.md CHANGED
@@ -1,35 +1,36 @@
1
1
  EDTF-Ruby
2
2
  =========
3
3
  [![ci](https://github.com/inukshuk/edtf-ruby/actions/workflows/ci.yml/badge.svg)](https://github.com/inukshuk/edtf-ruby/actions/workflows/ci.yml)
4
- [![Coverage Status](https://coveralls.io/repos/github/inukshuk/edtf-ruby/badge.svg?branch=main)](https://coveralls.io/github/inukshuk/edtf-ruby?branch=main)
5
4
 
6
- EDTF-Ruby comprises a parser and an API implementation of the [Extended
7
- Date/Time Format standard](http://www.loc.gov/standards/datetime/).
5
+ EDTF-Ruby comprises a parser and an API implementation
6
+ of the [Extended Date/Time Format standard](https://www.loc.gov/standards/datetime/).
8
7
 
9
8
 
10
9
  Compatibility
11
10
  -------------
12
11
  EDTF-Ruby parser implements all levels and features of the EDTF specification
13
- (version September 16, 2011). With the following known caveats:
14
-
15
- * In the latest revision of the EDTF specification alternative versions of
16
- partial uncertain/approximate strings were introduced (with or without
17
- nested parentheses); EDTF-Ruby currently uses the version that tries to
18
- reduce parentheses for printing as we find that one easier to read; the
19
- parser accepts all valid dates using this approach, plus some dates using
20
- nested expressions (the parser will not accept some of the more complex
21
- examples, though).
22
-
23
- EDTF-Ruby has been confirmed to work on the following Ruby implementations:
24
- 2.1, 2.0, 1.9.3, Rubinius, and JRuby (1.8.7 and 1.9.2 were originally supported
25
- but we are not testing compatibility actively anymore). Active Support's date
26
- extensions are currently listed as a dependency, because of many functional
27
- overlaps (version 3.x and 4.x are supported).
12
+ (version September 16, 2011).
13
+ With the following known caveats:
14
+
15
+ * In the latest revision of the EDTF specification
16
+ alternative versions of partial uncertain/approximate strings were introduced
17
+ (with or without nested parentheses);
18
+ EDTF-Ruby currently uses the version that tries to reduce parentheses for printing
19
+ as we find that one easier to read;
20
+ the parser accepts all valid dates using this approach,
21
+ plus some dates using nested expressions
22
+ (the parser will not accept some of the more complex examples, though).
23
+
24
+ EDTF-Ruby is tested on Ruby 3.3 and later, and on JRuby.
25
+ Active Support's date extensions are currently listed as a dependency,
26
+ because of many functional overlaps
27
+ (versions 3.x through 8.x are supported).
28
28
 
29
29
  ### ISO 8601-2
30
30
 
31
- A variation of EDTF has been adopted by the ISO 8601-2 standard. EDTF-Ruby
32
- does not support this new version of EDTF yet, but if you are curious,
31
+ A variation of EDTF has been adopted by the ISO 8601-2 standard.
32
+ EDTF-Ruby does not support this new version of EDTF yet,
33
+ but if you are curious,
33
34
  [EDTF.js](https://github.com/inukshuk/edtf.js), an ES6 implementation
34
35
  is already available.
35
36
 
@@ -37,23 +38,28 @@ Quickstart
37
38
  ----------
38
39
  EDTF Ruby is implemented as an extension to the regular Ruby date/time classes.
39
40
  You can parse EDTF strings either using `Date.edtf` or `EDTF.parse`
40
- (both methods come with an alternative bang! version, that will raise an error
41
- if the string cannot be parsed instead of silently returning nil); if
42
- given a valid EDTF string the return value will either be an (extended) `Date`,
41
+ (both methods come with an alternative bang! version,
42
+ that will raise an error if the string cannot be parsed
43
+ instead of silently returning nil);
44
+ if given a valid EDTF string
45
+ the return value will either be an (extended) `Date`,
43
46
  `EDTF::Interval`, `EDTF::Set`, `EDTF::Epoch` or `EDTF::Season` instance.
44
47
 
45
- Given any of these instances, you can print the corresponding EDTF string
46
- using the `#edtf` method.
48
+ Given any of these instances,
49
+ you can print the corresponding EDTF string using the `#edtf` method.
47
50
 
48
51
  ### Dates
49
52
 
50
- Most of the EDTF features deal with dates; EDTF-Ruby implements these by
51
- extending Active Support's version of the regular Ruby Date class. The library
52
- intends to be transparent to Ruby's regular API, i.e., every Date instance
53
- should act as you would normally expect but provides additional functionality.
53
+ Most of the EDTF features deal with dates;
54
+ EDTF-Ruby implements these by extending Active Support's version
55
+ of the regular Ruby Date class.
56
+ The library intends to be transparent to Ruby's regular API,
57
+ i.e., every Date instance should act as you would normally expect
58
+ but provides additional functionality.
54
59
 
55
- Most, notably, EDTF dates come in day, month, or year precision. This is a
56
- subtle difference that determines how many other methods work. For instance:
60
+ Most notably, EDTF dates come in day, month, or year precision.
61
+ This is a subtle difference that determines how many other methods work.
62
+ For instance:
57
63
 
58
64
  > Date.today.precision
59
65
  => :day
@@ -64,16 +70,18 @@ subtle difference that determines how many other methods work. For instance:
64
70
  > Date.today.month_precision!.succ
65
71
  => Sat, 10 Dec 2011
66
72
 
67
- As you can see, dates have day precision by default; after setting the date's
68
- precision to month, however, the natural successor is not the next day, but
69
- a day a month from now. Always keep precision in mind when comparing dates,
70
- too:
73
+ As you can see, dates have day precision by default;
74
+ after setting the date's precision to month, however,
75
+ the natural successor is not the next day,
76
+ but a day a month from now.
77
+ Always keep precision in mind when comparing dates, too:
71
78
 
72
79
  > Date.new(1966).year_precision! == Date.new(1966)
73
80
  => false
74
81
 
75
- The year 1966 is not equal to the January 1st, 1966. You can set a date's
76
- precision directly, or else use the dedicated bang! methods:
82
+ The year 1966 is not equal to the January 1st, 1966.
83
+ You can set a date's precision directly,
84
+ or else use the dedicated bang! methods:
77
85
 
78
86
  > d = Date.new(1993)
79
87
  > d.day_precision? # -> true
@@ -88,35 +96,39 @@ precision directly, or else use the dedicated bang! methods:
88
96
  > d.day_precision? # -> false
89
97
  > d.year_precision? # -> true
90
98
 
91
- In the examples above, you also see that the `#edtf` method will print a
92
- different string depending on the date's current precision.
99
+ In the examples above, you also see that the `#edtf` method
100
+ will print a different string depending on the date's current precision.
93
101
 
94
- The second important extension is that dates can be uncertain and or
95
- approximate. The distinction between the two may seem somewhat contrived,
102
+ The second important extension is that dates can be uncertain and or approximate.
103
+ The distinction between the two may seem somewhat contrived,
96
104
  but we have come to understand it as follows:
97
105
 
98
- Assume you take a history exam and have to answer one of those dreaded
99
- questions that require you to say exactly in what year a certain event
100
- happened; you studied hard, but all of a sudden you are *uncertain*: was
101
- that the year 1683 or was it 1638? This is what uncertainty is in the
102
- parlance of EDTF: in fact, you would write it just like that "1638?".
103
-
104
- Approximate dates are similar but slightly different. Lets say you want
105
- to tell a story about something that happened to you one winter; you
106
- don't recall the exact date, but you know it must have been sometime
107
- between Christmas and New Year's. Come to think of it, you don't
108
- remember the year either, but you must have been around ten years old.
109
- Using EDTF, you could write something like "1993~-12-(27)~": this
110
- indicates that both the year and the day are approximations: perhaps
111
- the day is not the 27th but it is somewhere close.
112
-
113
- This is the main difference between uncertain and approximate in
114
- EDTF (in our opinion at least): approximate always means close to the
115
- actual number, whilst uncertain could be something completely different
106
+ Assume you take a history exam
107
+ and have to answer one of those dreaded questions
108
+ that require you to say exactly in what year a certain event happened;
109
+ you studied hard, but all of a sudden you are *uncertain*:
110
+ was that the year 1683 or was it 1638?
111
+ This is what uncertainty is in the parlance of EDTF:
112
+ in fact, you would write it just like that "1638?".
113
+
114
+ Approximate dates are similar but slightly different.
115
+ Let's say you want to tell a story about something that happened to you one winter;
116
+ you don't recall the exact date,
117
+ but you know it must have been sometime between Christmas and New Year's.
118
+ Come to think of it, you don't remember the year either,
119
+ but you must have been around ten years old.
120
+ Using EDTF, you could write something like "1993~-12-(27)~":
121
+ this indicates that both the year and the day are approximations:
122
+ perhaps the day is not the 27th but it is somewhere close.
123
+
124
+ This is the main difference between uncertain and approximate in EDTF
125
+ (in our opinion at least):
126
+ approximate always means close to the actual number,
127
+ whilst uncertain could be something completely different
116
128
  (just as there is a large temporal distance between 1638 and 1683).
117
129
 
118
- Here are a few examples of how you can access the uncertain/approximate
119
- state of dates in Ruby:
130
+ Here are a few examples of how you can access
131
+ the uncertain/approximate state of dates in Ruby:
120
132
 
121
133
  > d = Date.edtf('1984?')
122
134
  > d.uncertain?
@@ -133,12 +145,11 @@ state of dates in Ruby:
133
145
  > d.edtf
134
146
  => "1984?-01~"
135
147
 
136
- As you can see above, you can use the bang! methods to set individual date
137
- parts.
148
+ As you can see above, you can use the bang! methods to set individual date parts.
138
149
 
139
- In addition, EDTF supports *unspecified* date parts. EDTF-Ruby provides parsing
140
- for both the draft and finalized EDTF specifications, with output meeting the
141
- requirements of the final specification:
150
+ In addition, EDTF supports *unspecified* date parts.
151
+ EDTF-Ruby provides parsing for both the draft and finalized EDTF specifications,
152
+ with output meeting the requirements of the final specification:
142
153
 
143
154
  > d = Date.edtf('1999-03-XX')
144
155
  > d.unspecified?
@@ -155,8 +166,9 @@ requirements of the final specification:
155
166
  > draft_d.edtf
156
167
  => "1999-03-XX"
157
168
 
158
- All three, uncertain, approximate, and unspecified attributes do not factor
159
- into date calculations (like comparisons or successors etc.).
169
+ All three, uncertain, approximate, and unspecified attributes
170
+ do not factor into date calculations
171
+ (like comparisons or successors etc.).
160
172
 
161
173
  EDTF long or scientific years are mapped to normal date instances.
162
174
 
@@ -171,9 +183,10 @@ When printing date strings, EDTF-Ruby will try to avoid nested parentheses:
171
183
 
172
184
  ### Intervals
173
185
 
174
- If you parse an EDTF interval, the EDTF-Ruby parser will return an instance
175
- of `EDTF::Interval`; intervals mimic regular Ruby ranges, but offer additional
176
- functionality.
186
+ If you parse an EDTF interval,
187
+ the EDTF-Ruby parser will return an instance of `EDTF::Interval`;
188
+ intervals mimic regular Ruby ranges,
189
+ but offer additional functionality.
177
190
 
178
191
  > d = Date.edtf('1984-06?/2004-08?')
179
192
  > d.from.uncertain?
@@ -181,14 +194,15 @@ functionality.
181
194
  > d.include?(Date.new(1987,04,13))
182
195
  => false
183
196
 
184
- The day is not included because interval has month precision. However:
197
+ The day is not included because interval has month precision.
198
+ However:
185
199
 
186
200
  > d.cover?(Date.new(1987,04,13))
187
201
  => true
188
202
 
189
- The day is still covered by the interval. In general, optimized in the
190
- same way that Ruby optimizes numeric ranges. Additionally, precision
191
- plays into the way intervals are enumerated:
203
+ The day is still covered by the interval.
204
+ In general, optimized in the same way that Ruby optimizes numeric ranges.
205
+ Additionally, precision plays into the way intervals are enumerated:
192
206
 
193
207
  > d.length
194
208
  => 243
@@ -198,14 +212,16 @@ There are 243 months between 1984-06-01 and 2004-08-31.
198
212
  > d.step(36).map(&:year)
199
213
  => [1984, 1987, 1990, 1993, 1996, 1999, 2002]
200
214
 
201
- Here we iterate through the interval in 36-month steps and map each date to
202
- the year.
215
+ Here we iterate through the interval in 36-month steps
216
+ and map each date to the year.
203
217
 
204
218
  > Date.edtf('1582-10-01/1582-10-31').length
205
- => 21
219
+ => 31
206
220
 
207
- This interval has day precision, so 21 is the number of days in October 1582,
208
- which was cut short because of the Gregorian calendar reform.
221
+ This interval has day precision,
222
+ so 31 is the number of days in October 1582:
223
+ EDTF-Ruby uses the proleptic Gregorian calendar,
224
+ so the ten days skipped by the Gregorian calendar reform are included.
209
225
 
210
226
  Intervals can be open or have unknown start or end dates.
211
227
 
@@ -216,8 +232,10 @@ Intervals can be open or have unknown start or end dates.
216
232
 
217
233
  ### Sets
218
234
 
219
- EDTF supports two kind of sets: choice lists (meaning one date out of a list),
220
- or inclusive lists. In EDTF-Ruby, these are covered by the class `EDTF::Set`
235
+ EDTF supports two kinds of sets:
236
+ choice lists (meaning one date out of a list),
237
+ or inclusive lists.
238
+ In EDTF-Ruby, these are covered by the class `EDTF::Set`
221
239
  and the `choice` attribute.
222
240
 
223
241
  > s = Date.edtf('{1667,1668, 1670..1672}')
@@ -227,22 +245,23 @@ and the `choice` attribute.
227
245
  > s.edtf
228
246
  => "[1667,1668,1670..1672]"
229
247
 
230
- As you can see above, EDTF-Ruby remembers which parts of the set were
231
- specified as a range; ranges are however enumerated for membership tests:
248
+ As you can see above,
249
+ EDTF-Ruby remembers which parts of the set were specified as a range;
250
+ ranges are however enumerated for membership tests:
232
251
 
233
252
  > s.include?(Date.edtf('1669'))
234
253
  => false
235
254
  > s.include?(Date.edtf('1671'))
236
255
  => true
237
256
 
238
- Even though we're still aware that the year 1671 was is not directly an
239
- element of the set:
257
+ Even though we're still aware
258
+ that the year 1671 is not directly an element of the set:
240
259
 
241
260
  > s.length
242
261
  => 3
243
262
 
244
- When in doubt, you can always map the set to an array. This will also
245
- enumerate all ranges:
263
+ When in doubt, you can always map the set to an array.
264
+ This will also enumerate all ranges:
246
265
 
247
266
  > s.map(&:year)
248
267
  => [1667,1668,1670,1671,1672] # when enumerated there are 5 elements
@@ -258,14 +277,14 @@ EDTF sets also feature an `#earlier?` and `#later?` attribute:
258
277
 
259
278
  ### Decades and Centuries
260
279
 
261
- The EDTF specification supports so called masked precision strings to define
262
- decades or centuries. EDTF-Ruby maps these to dedicated intervals which
263
- always cover 10 or 100 years, respectively.
280
+ The EDTF specification supports so called masked precision strings
281
+ to define decades or centuries.
282
+ EDTF-Ruby maps these to dedicated intervals
283
+ which always cover 10 or 100 years, respectively.
264
284
 
265
285
  > d = Date.edtf!('196x')
266
- => 196x
267
- > d.class
268
- => EDTF::Decade
286
+ > d.edtf
287
+ => "196x"
269
288
  > d.class
270
289
  => EDTF::Decade
271
290
  > d.min
@@ -277,13 +296,15 @@ always cover 10 or 100 years, respectively.
277
296
 
278
297
  ### Seasons
279
298
 
280
- Finally, EDTF covers seasons. Again, EDTF-Ruby provides a dedicated class
281
- for this. Note that EDTF does not make any assumption about the specifics
282
- (which months etc.) of the season and you don't have to either; however
283
- EDTF-Ruby defines method aliases which allow you to access the seasons
284
- by the names spring, summer, autumn (or fall), and winter, respectively.
285
- You can also use the more neutral taxonomy of first, second, third,
286
- fourth.
299
+ Finally, EDTF covers seasons.
300
+ Again, EDTF-Ruby provides a dedicated class for this.
301
+ Note that EDTF does not make any assumption about the specifics
302
+ (which months etc.) of the season
303
+ and you don't have to either;
304
+ however EDTF-Ruby defines method aliases
305
+ which allow you to access the seasons by the names
306
+ spring, summer, autumn (or fall), and winter, respectively.
307
+ You can also use the more neutral taxonomy of first, second, third, fourth.
287
308
 
288
309
  > w = Date.edtf!('2003-24')
289
310
  > w.winter?
@@ -299,24 +320,25 @@ fourth.
299
320
  => Mon, 31 May 2004
300
321
  > s.to_a.length
301
322
  => 92
302
- 005:0> w.to_a.length
323
+ > w.to_a.length
303
324
  => 91
304
325
 
305
- As you can see, spring 2004 lasted one day longer than winter 2003 (note
306
- that spring and winter here do not relate to the astronomical seasons
326
+ As you can see, spring 2004 lasted one day longer than winter 2003
327
+ (note that spring and winter here do not relate to the astronomical seasons
307
328
  but strictly to three month periods).
308
329
 
309
- Of course you can print seasons to EDTF strings, too. Finally, seasons can
310
- be uncertain/approximate.
330
+ Of course you can print seasons to EDTF strings, too.
331
+ Finally, seasons can be uncertain/approximate.
311
332
 
312
333
  > s.edtf
313
334
  => "2004-21"
314
335
  > s.approximate!.edtf
315
- => "2004-21"
336
+ => "2004-21~"
316
337
 
317
338
 
318
- For additional features, please take a look at the documentation or the
319
- extensive list of rspec examples.
339
+ For additional features,
340
+ please take a look at the documentation
341
+ or the extensive list of rspec examples.
320
342
 
321
343
 
322
344
  Contributing
@@ -326,16 +348,20 @@ You can check out a copy of the latest code using Git:
326
348
 
327
349
  $ git clone https://github.com/inukshuk/edtf-ruby.git
328
350
 
329
- To get started, generate the parser and run all tests:
351
+ To get started, install the dependencies and run all tests:
330
352
 
331
353
  $ cd edtf-ruby
332
354
  $ bundle install
333
- $ bundle exec rspec spec
334
- $ bundle exec cucumber
355
+ $ bundle exec rake
356
+
357
+ If you change the grammar in `lib/edtf/parser.y`,
358
+ regenerate the parser with `bundle exec rake racc`.
335
359
 
336
- If you've found a bug or have a question, please open an issue on the
337
- [EDTF-Ruby issue tracker](https://github.com/inukshuk/edtf-ruby). Or, for extra
338
- credit, clone the EDTF-Ruby repository, write a failing example, fix the bug
360
+ If you've found a bug or have a question,
361
+ please open an issue on the
362
+ [EDTF-Ruby issue tracker](https://github.com/inukshuk/edtf-ruby/issues).
363
+ Or, for extra credit, clone the EDTF-Ruby repository,
364
+ write a failing example, fix the bug
339
365
  and submit a pull request.
340
366
 
341
367
 
@@ -343,5 +369,5 @@ Credits
343
369
  -------
344
370
  EDTF-Ruby was written by Sylvester Keil and Ilja Srna.
345
371
 
346
- Published under the terms and conditions of the FreeBSD License; see LICENSE
347
- for details.
372
+ Published under the terms and conditions of the FreeBSD License;
373
+ see LICENSE for details.
data/lib/edtf/date.rb CHANGED
@@ -19,7 +19,10 @@ class Date
19
19
  extend Forwardable
20
20
 
21
21
  class << self
22
-
22
+ def gregorian(year = -4712, month = 1, mday = 1)
23
+ new(year, month, mday, Date::GREGORIAN)
24
+ end
25
+
23
26
  def edtf(input, options = {})
24
27
  edtf!(input, options)
25
28
  rescue
@@ -260,12 +263,34 @@ class Date
260
263
  when ::Date
261
264
  values <=> other.values
262
265
  when EDTF::Interval, EDTF::Season, EDTF::Epoch
263
- other.cover?(self) ? other.min <=> self : 0
266
+ (c = other <=> self) && -c
264
267
  else
265
268
  nil
266
269
  end
267
270
  end
268
271
 
272
+ alias original_eql? eql?
273
+ alias original_hash hash
274
+
275
+ # Returns true if other is the same date and has the same EDTF attributes.
276
+ def eql?(other)
277
+ if original_eql?(other)
278
+ extended_hash == other.extended_hash && calendar == other.calendar
279
+ else
280
+ false
281
+ end
282
+ end
283
+
284
+ # Returns the original Date hash unless EDTF attributes are set.
285
+ def hash
286
+ h = extended_hash
287
+ if h.zero? && !calendar?
288
+ original_hash
289
+ else
290
+ [original_hash, h, @calendar].hash
291
+ end
292
+ end
293
+
269
294
 
270
295
  # Returns an array of the current year, month, and day values filtered by
271
296
  # the Date/Time's precision.
@@ -311,6 +336,18 @@ class Date
311
336
 
312
337
  attr_writer :uncertain, :unspecified, :approximate
313
338
 
339
+ # Bits 0-1: precision (0 - day, 1 - month, 2 - year)
340
+ # Bits 2-4: uncertain year, month, day (Uncertainty#hash with base 1)
341
+ # Bits 5-7: approximate year, month, day (Uncertainty#hash with base 8)
342
+ # Bits 8-15: unspecified year, month, and day digits
343
+ def extended_hash
344
+ h = @precision == :year ? 2 : @precision == :month ? 1 : 0
345
+ h |= @uncertain.hash << 2 if @uncertain
346
+ h |= @approximate.hash << 2 if @approximate
347
+ h |= @unspecified.hash << 8 if @unspecified
348
+ h
349
+ end
350
+
314
351
  def copy_extended_attributes(other)
315
352
  @uncertain = other.uncertain.dup
316
353
  @approximate = other.approximate.dup
data/lib/edtf/epoch.rb CHANGED
@@ -44,7 +44,9 @@ module EDTF
44
44
  case other
45
45
  when Date
46
46
  cover?(other) ? 0 : to_date <=> other
47
- when Interval, Season
47
+ when Interval
48
+ (c = other <=> self) && -c
49
+ when Season
48
50
  [min, max] <=> [other.min, other.max]
49
51
  when Epoch
50
52
  [year, self.class.duration] <=> [other.year, other.class.duration]
data/lib/edtf/interval.rb CHANGED
@@ -166,18 +166,8 @@ module EDTF
166
166
 
167
167
  other = other.day_precision
168
168
 
169
- case
170
- when unknown_start?
171
- max.day_precision! == other
172
- when unknown_end?
173
- min.day_precision! == other
174
- when open_start?
175
- max.day_precision! >= other
176
- when open_end?
177
- min.day_precision! <= other
178
- else
179
- min.day_precision! <= other && other <= max.day_precision!
180
- end
169
+ (unbounded_start? || min.day_precision! <= other) &&
170
+ (unbounded_end? || other <= max.day_precision!)
181
171
  end
182
172
 
183
173
  # call-seq:
@@ -268,12 +258,20 @@ module EDTF
268
258
  max
269
259
  end
270
260
 
261
+ # Intervals are compared by their min and max dates. Open and unknown
262
+ # starts and ends are treated like -/+ infinity, with open being the
263
+ # more extreme of the two. Dates are equal to an interval if they are
264
+ # covered by it.
271
265
  def <=>(other)
272
266
  case other
273
267
  when Interval, Season, Epoch
274
- [min, max] <=> [other.min, other.max]
268
+ bounds <=> bounds_of(other)
275
269
  when Date
276
- cover?(other) ? min <=> other : 0
270
+ case
271
+ when cover?(other) then 0
272
+ when unbounded_start? then -1
273
+ else min.day_precision <=> other.day_precision
274
+ end
277
275
  else
278
276
  nil
279
277
  end
@@ -282,7 +280,8 @@ module EDTF
282
280
  def ===(other)
283
281
  case other
284
282
  when Interval
285
- cover?(other.min) && cover?(other.max)
283
+ (other.unbounded_start? ? unbounded_start? : cover?(other.min)) &&
284
+ (other.unbounded_end? ? unbounded_end? : cover?(other.max))
286
285
  when Date
287
286
  cover?(other)
288
287
  else
@@ -301,6 +300,32 @@ module EDTF
301
300
 
302
301
  alias to_s edtf
303
302
 
303
+ protected
304
+
305
+ def unbounded_start?
306
+ open_start? || unknown_start?
307
+ end
308
+
309
+ def unbounded_end?
310
+ open_end? || unknown_end?
311
+ end
312
+
313
+ START_RANK = { :open => -2, :unknown => -1 }.freeze
314
+ END_RANK = { :unknown => 1, :open => 2 }.freeze
315
+
316
+ def bounds
317
+ [
318
+ unbounded_start? ? START_RANK[from] : 0, min,
319
+ unbounded_end? ? END_RANK[to] : 0, max
320
+ ]
321
+ end
322
+
323
+ private
324
+
325
+ def bounds_of(other)
326
+ other.is_a?(Interval) ? other.bounds : [0, other.min, 0, other.max]
327
+ end
328
+
304
329
  end
305
330
 
306
331
  end