edtf 3.2.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: 6cc486f565cf7987bbb547f9ddb63a8eb50e0f6cd016a409f3eb60caf7f4544b
4
- data.tar.gz: de1c6696854d6c7c58a6bb1f133ebe4cd7d363022617a2fe5cf78d3b2e55722d
3
+ metadata.gz: 68cf5a59b655ee503dfda487dd719422299c40ee1744baeb93605b5b08bd0c9f
4
+ data.tar.gz: 2b8e4a4e41c46f9607057c2b61720d3fce35acae2afa73ea112ff3bfdc03dc5e
5
5
  SHA512:
6
- metadata.gz: 27e4c029c50828e797ab21fe727bc850cb80367f0fe74421158dc32387d21b4424ef1614defc3b86bb9a957d397fc792d6f7152dc0c085d93b41f5c4b3c78055
7
- data.tar.gz: 4832debc9c007c0e324b6705fb5346b3755235c5b92b19498d46f50324498ff1673c9559e1242b6abdc8e968267c5a849fbaef08d8cd509ed9ab527bcda6ef49
6
+ metadata.gz: 34e2bf30004ba2eb3389d69fd9244cff29ddcf915e477e894b513116365efb0862585d2fa078786d5f40c22cfbddfec23d26a361886be0a78013f1b4457e0b93
7
+ data.tar.gz: fa871f182f1ec791e36946c0a8a5127323c6b4da1aa6915b625f180a2e0698d586f6452d4d969f2d9f4345195838a92a4ee6414068354334f79a27ceb6fc0000
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2011-2022 Sylvester Keil. All rights reserved.
1
+ Copyright 2011-2026 Sylvester Keil. All rights reserved.
2
2
 
3
3
  Redistribution and use in source and binary forms, with or without
4
4
  modification, are permitted provided that the following conditions are met:
data/README.md CHANGED
@@ -1,58 +1,65 @@
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 will part of the upcoming ISO 8601-2 standard. EDTF-Ruby
32
- does not support this new version of EDTF yet, but if you are curious,
33
- [EDTF.js](https://github.com/inukshuk/edtf.js), an ES6 implementation is already available.
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,
34
+ [EDTF.js](https://github.com/inukshuk/edtf.js), an ES6 implementation
35
+ is already available.
34
36
 
35
37
  Quickstart
36
38
  ----------
37
39
  EDTF Ruby is implemented as an extension to the regular Ruby date/time classes.
38
40
  You can parse EDTF strings either using `Date.edtf` or `EDTF.parse`
39
- (both methods come with an alternative bang! version, that will raise an error
40
- if the string cannot be parsed instead of silently returning nil); if
41
- 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`,
42
46
  `EDTF::Interval`, `EDTF::Set`, `EDTF::Epoch` or `EDTF::Season` instance.
43
47
 
44
- Given any of these instances, you can print the corresponding EDTF string
45
- using the `#edtf` method.
48
+ Given any of these instances,
49
+ you can print the corresponding EDTF string using the `#edtf` method.
46
50
 
47
51
  ### Dates
48
52
 
49
- Most of the EDTF features deal with dates; EDTF-Ruby implements these by
50
- extending Active Support's version of the regular Ruby Date class. The library
51
- intends to be transparent to Ruby's regular API, i.e., every Date instance
52
- 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.
53
59
 
54
- Most, notably, EDTF dates come in day, month, or year precision. This is a
55
- 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:
56
63
 
57
64
  > Date.today.precision
58
65
  => :day
@@ -63,16 +70,18 @@ subtle difference that determines how many other methods work. For instance:
63
70
  > Date.today.month_precision!.succ
64
71
  => Sat, 10 Dec 2011
65
72
 
66
- As you can see, dates have day precision by default; after setting the date's
67
- precision to month, however, the natural successor is not the next day, but
68
- a day a month from now. Always keep precision in mind when comparing dates,
69
- 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:
70
78
 
71
79
  > Date.new(1966).year_precision! == Date.new(1966)
72
80
  => false
73
81
 
74
- The year 1966 is not equal to the January 1st, 1966. You can set a date's
75
- 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:
76
85
 
77
86
  > d = Date.new(1993)
78
87
  > d.day_precision? # -> true
@@ -87,35 +96,39 @@ precision directly, or else use the dedicated bang! methods:
87
96
  > d.day_precision? # -> false
88
97
  > d.year_precision? # -> true
89
98
 
90
- In the examples above, you also see that the `#edtf` method will print a
91
- 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.
92
101
 
93
- The second important extension is that dates can be uncertain and or
94
- 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,
95
104
  but we have come to understand it as follows:
96
105
 
97
- Assume you take a history exam and have to answer one of those dreaded
98
- questions that require you to say exactly in what year a certain event
99
- happened; you studied hard, but all of a sudden you are *uncertain*: was
100
- that the year 1683 or was it 1638? This is what uncertainty is in the
101
- parlance of EDTF: in fact, you would write it just like that "1638?".
102
-
103
- Approximate dates are similar but slightly different. Lets say you want
104
- to tell a story about something that happened to you one winter; you
105
- don't recall the exact date, but you know it must have been sometime
106
- between Christmas and New Year's. Come to think of it, you don't
107
- remember the year either, but you must have been around ten years old.
108
- Using EDTF, you could write something like "1993~-12-(27)~": this
109
- indicates that both the year and the day are approximations: perhaps
110
- the day is not the 27th but it is somewhere close.
111
-
112
- This is the main difference between uncertain and approximate in
113
- EDTF (in our opinion at least): approximate always means close to the
114
- 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
115
128
  (just as there is a large temporal distance between 1638 and 1683).
116
129
 
117
- Here are a few examples of how you can access the uncertain/approximate
118
- 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:
119
132
 
120
133
  > d = Date.edtf('1984?')
121
134
  > d.uncertain?
@@ -132,12 +145,11 @@ state of dates in Ruby:
132
145
  > d.edtf
133
146
  => "1984?-01~"
134
147
 
135
- As you can see above, you can use the bang! methods to set individual date
136
- parts.
148
+ As you can see above, you can use the bang! methods to set individual date parts.
137
149
 
138
- In addition, EDTF supports *unspecified* date parts. EDTF-Ruby provides parsing
139
- for both the draft and finalized EDTF specifications, with output meeting the
140
- 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:
141
153
 
142
154
  > d = Date.edtf('1999-03-XX')
143
155
  > d.unspecified?
@@ -154,8 +166,9 @@ requirements of the final specification:
154
166
  > draft_d.edtf
155
167
  => "1999-03-XX"
156
168
 
157
- All three, uncertain, approximate, and unspecified attributes do not factor
158
- 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.).
159
172
 
160
173
  EDTF long or scientific years are mapped to normal date instances.
161
174
 
@@ -170,9 +183,10 @@ When printing date strings, EDTF-Ruby will try to avoid nested parentheses:
170
183
 
171
184
  ### Intervals
172
185
 
173
- If you parse an EDTF interval, the EDTF-Ruby parser will return an instance
174
- of `EDTF::Interval`; intervals mimic regular Ruby ranges, but offer additional
175
- 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.
176
190
 
177
191
  > d = Date.edtf('1984-06?/2004-08?')
178
192
  > d.from.uncertain?
@@ -180,14 +194,15 @@ functionality.
180
194
  > d.include?(Date.new(1987,04,13))
181
195
  => false
182
196
 
183
- The day is not included because interval has month precision. However:
197
+ The day is not included because interval has month precision.
198
+ However:
184
199
 
185
200
  > d.cover?(Date.new(1987,04,13))
186
201
  => true
187
202
 
188
- The day is still covered by the interval. In general, optimized in the
189
- same way that Ruby optimizes numeric ranges. Additionally, precision
190
- 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:
191
206
 
192
207
  > d.length
193
208
  => 243
@@ -197,14 +212,16 @@ There are 243 months between 1984-06-01 and 2004-08-31.
197
212
  > d.step(36).map(&:year)
198
213
  => [1984, 1987, 1990, 1993, 1996, 1999, 2002]
199
214
 
200
- Here we iterate through the interval in 36-month steps and map each date to
201
- the year.
215
+ Here we iterate through the interval in 36-month steps
216
+ and map each date to the year.
202
217
 
203
218
  > Date.edtf('1582-10-01/1582-10-31').length
204
- => 21
219
+ => 31
205
220
 
206
- This interval has day precision, so 21 is the number of days in October 1582,
207
- 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.
208
225
 
209
226
  Intervals can be open or have unknown start or end dates.
210
227
 
@@ -215,8 +232,10 @@ Intervals can be open or have unknown start or end dates.
215
232
 
216
233
  ### Sets
217
234
 
218
- EDTF supports two kind of sets: choice lists (meaning one date out of a list),
219
- 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`
220
239
  and the `choice` attribute.
221
240
 
222
241
  > s = Date.edtf('{1667,1668, 1670..1672}')
@@ -226,22 +245,23 @@ and the `choice` attribute.
226
245
  > s.edtf
227
246
  => "[1667,1668,1670..1672]"
228
247
 
229
- As you can see above, EDTF-Ruby remembers which parts of the set were
230
- 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:
231
251
 
232
252
  > s.include?(Date.edtf('1669'))
233
253
  => false
234
254
  > s.include?(Date.edtf('1671'))
235
255
  => true
236
256
 
237
- Even though we're still aware that the year 1671 was is not directly an
238
- element of the set:
257
+ Even though we're still aware
258
+ that the year 1671 is not directly an element of the set:
239
259
 
240
260
  > s.length
241
261
  => 3
242
262
 
243
- When in doubt, you can always map the set to an array. This will also
244
- enumerate all ranges:
263
+ When in doubt, you can always map the set to an array.
264
+ This will also enumerate all ranges:
245
265
 
246
266
  > s.map(&:year)
247
267
  => [1667,1668,1670,1671,1672] # when enumerated there are 5 elements
@@ -257,14 +277,14 @@ EDTF sets also feature an `#earlier?` and `#later?` attribute:
257
277
 
258
278
  ### Decades and Centuries
259
279
 
260
- The EDTF specification supports so called masked precision strings to define
261
- decades or centuries. EDTF-Ruby maps these to dedicated intervals which
262
- 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.
263
284
 
264
285
  > d = Date.edtf!('196x')
265
- => 196x
266
- > d.class
267
- => EDTF::Decade
286
+ > d.edtf
287
+ => "196x"
268
288
  > d.class
269
289
  => EDTF::Decade
270
290
  > d.min
@@ -276,13 +296,15 @@ always cover 10 or 100 years, respectively.
276
296
 
277
297
  ### Seasons
278
298
 
279
- Finally, EDTF covers seasons. Again, EDTF-Ruby provides a dedicated class
280
- for this. Note that EDTF does not make any assumption about the specifics
281
- (which months etc.) of the season and you don't have to either; however
282
- EDTF-Ruby defines method aliases which allow you to access the seasons
283
- by the names spring, summer, autumn (or fall), and winter, respectively.
284
- You can also use the more neutral taxonomy of first, second, third,
285
- 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.
286
308
 
287
309
  > w = Date.edtf!('2003-24')
288
310
  > w.winter?
@@ -298,24 +320,25 @@ fourth.
298
320
  => Mon, 31 May 2004
299
321
  > s.to_a.length
300
322
  => 92
301
- 005:0> w.to_a.length
323
+ > w.to_a.length
302
324
  => 91
303
325
 
304
- As you can see, spring 2004 lasted one day longer than winter 2003 (note
305
- 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
306
328
  but strictly to three month periods).
307
329
 
308
- Of course you can print seasons to EDTF strings, too. Finally, seasons can
309
- be uncertain/approximate.
330
+ Of course you can print seasons to EDTF strings, too.
331
+ Finally, seasons can be uncertain/approximate.
310
332
 
311
333
  > s.edtf
312
334
  => "2004-21"
313
335
  > s.approximate!.edtf
314
- => "2004-21"
336
+ => "2004-21~"
315
337
 
316
338
 
317
- For additional features, please take a look at the documentation or the
318
- 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.
319
342
 
320
343
 
321
344
  Contributing
@@ -325,16 +348,20 @@ You can check out a copy of the latest code using Git:
325
348
 
326
349
  $ git clone https://github.com/inukshuk/edtf-ruby.git
327
350
 
328
- To get started, generate the parser and run all tests:
351
+ To get started, install the dependencies and run all tests:
329
352
 
330
353
  $ cd edtf-ruby
331
354
  $ bundle install
332
- $ bundle exec rspec spec
333
- $ 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`.
334
359
 
335
- If you've found a bug or have a question, please open an issue on the
336
- [EDTF-Ruby issue tracker](https://github.com/inukshuk/edtf-ruby). Or, for extra
337
- 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
338
365
  and submit a pull request.
339
366
 
340
367
 
@@ -342,5 +369,5 @@ Credits
342
369
  -------
343
370
  EDTF-Ruby was written by Sylvester Keil and Ilja Srna.
344
371
 
345
- Published under the terms and conditions of the FreeBSD License; see LICENSE
346
- 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]