diff --git a/lib/edtf/date.rb b/lib/edtf/date.rb index fa58a75..370ed5e 100644 --- a/lib/edtf/date.rb +++ b/lib/edtf/date.rb @@ -19,7 +19,10 @@ class Date extend Forwardable class << self - + def gregorian(year = -4712, month = 1, mday = 1) + new(year, month, mday, Date::GREGORIAN) + end + def edtf(input, options = {}) edtf!(input, options) rescue diff --git a/lib/edtf/parser.rb b/lib/edtf/parser.rb index f03b4d2..e77999d 100644 --- a/lib/edtf/parser.rb +++ b/lib/edtf/parser.rb @@ -889,21 +889,21 @@ def next_token module_eval(<<'.,.,', 'parser.y', 23) def _reduce_6(val, _values, result) - result = Date.new(val[0]).year_precision! + result = Date.gregorian(val[0]).year_precision! result end .,., module_eval(<<'.,.,', 'parser.y', 24) def _reduce_7(val, _values, result) - result = Date.new(*val.flatten).month_precision! + result = Date.gregorian(*val.flatten).month_precision! result end .,., module_eval(<<'.,.,', 'parser.y', 25) def _reduce_8(val, _values, result) - result = Date.new(*val.flatten).day_precision! + result = Date.gregorian(*val.flatten).day_precision! result end .,., @@ -1057,7 +1057,7 @@ def _reduce_38(val, _values, result) module_eval(<<'.,.,', 'parser.y', 99) def _reduce_39(val, _values, result) - result = Date.new(val[0][0]).year_precision! + result = Date.gregorian(val[0][0]).year_precision! result.unspecified.year[2,2] = val[0][1] result @@ -1099,7 +1099,7 @@ def _reduce_46(val, _values, result) module_eval(<<'.,.,', 'parser.y', 125) def _reduce_47(val, _values, result) - result = Date.new(val[0]).unspecified!(:month) + result = Date.gregorian(val[0]).unspecified!(:month) result.precision = :month result @@ -1108,7 +1108,7 @@ def _reduce_47(val, _values, result) module_eval(<<'.,.,', 'parser.y', 130) def _reduce_48(val, _values, result) - result = Date.new(*val[0]).unspecified!(:day) + result = Date.gregorian(*val[0]).unspecified!(:day) result end @@ -1116,7 +1116,7 @@ def _reduce_48(val, _values, result) module_eval(<<'.,.,', 'parser.y', 134) def _reduce_49(val, _values, result) - result = Date.new(val[0]).unspecified!([:day,:month]) + result = Date.gregorian(val[0]).unspecified!([:day,:month]) result end @@ -1212,7 +1212,7 @@ def _reduce_67(val, _values, result) module_eval(<<'.,.,', 'parser.y', 181) def _reduce_73(val, _values, result) - result = Date.new(val[1]) + result = Date.gregorian(val[1]) result.precision = :year result @@ -1221,7 +1221,7 @@ def _reduce_73(val, _values, result) module_eval(<<'.,.,', 'parser.y', 186) def _reduce_74(val, _values, result) - result = Date.new(-1 * val[2]) + result = Date.gregorian(-1 * val[2]) result.precision = :year result @@ -1305,7 +1305,7 @@ def _reduce_90(val, _values, result) module_eval(<<'.,.,', 'parser.y', 231) def _reduce_91(val, _values, result) - result = Date.new(val[0].year * 10 ** val[2]).year_precision! + result = Date.gregorian(val[0].year * 10 ** val[2]).year_precision! result end @@ -1313,7 +1313,7 @@ def _reduce_91(val, _values, result) module_eval(<<'.,.,', 'parser.y', 235) def _reduce_92(val, _values, result) - result = Date.new(val[1] * 10 ** val[3]).year_precision! + result = Date.gregorian(val[1] * 10 ** val[3]).year_precision! result end @@ -1321,7 +1321,7 @@ def _reduce_92(val, _values, result) module_eval(<<'.,.,', 'parser.y', 239) def _reduce_93(val, _values, result) - result = Date.new(-1 * val[2] * 10 ** val[4]).year_precision! + result = Date.gregorian(-1 * val[2] * 10 ** val[4]).year_precision! result end @@ -1448,49 +1448,49 @@ def _reduce_113(val, _values, result) module_eval(<<'.,.,', 'parser.y', 289) def _reduce_114(val, _values, result) - result = Date.new(*val[0]).day_precision! + result = Date.gregorian(*val[0]).day_precision! result end .,., module_eval(<<'.,.,', 'parser.y', 290) def _reduce_115(val, _values, result) - result = Date.new(*val[0]).month_precision! + result = Date.gregorian(*val[0]).month_precision! result end .,., module_eval(<<'.,.,', 'parser.y', 291) def _reduce_116(val, _values, result) - result = Date.new(val[0]).year_precision! + result = Date.gregorian(val[0]).year_precision! result end .,., module_eval(<<'.,.,', 'parser.y', 294) def _reduce_117(val, _values, result) - result = (Date.new(*val[0]).day_precision! .. Date.new(*val[2]).day_precision!) + result = (Date.gregorian(*val[0]).day_precision! .. Date.gregorian(*val[2]).day_precision!) result end .,., module_eval(<<'.,.,', 'parser.y', 295) def _reduce_118(val, _values, result) - result = (Date.new(*val[0]).month_precision! .. Date.new(*val[2]).month_precision!) + result = (Date.gregorian(*val[0]).month_precision! .. Date.gregorian(*val[2]).month_precision!) result end .,., module_eval(<<'.,.,', 'parser.y', 296) def _reduce_119(val, _values, result) - result = (Date.new(val[0]).year_precision! .. Date.new(val[2]).year_precision!) + result = (Date.gregorian(val[0]).year_precision! .. Date.gregorian(val[2]).year_precision!) result end .,., module_eval(<<'.,.,', 'parser.y', 302) def _reduce_120(val, _values, result) - result = Date.new(val[0][0], val[2], val[4]) + result = Date.gregorian(val[0][0], val[2], val[4]) result.unspecified.year[2,2] = val[0][1] result @@ -1499,7 +1499,7 @@ def _reduce_120(val, _values, result) module_eval(<<'.,.,', 'parser.y', 307) def _reduce_121(val, _values, result) - result = Date.new(val[0][0], val[2], 1) + result = Date.gregorian(val[0][0], val[2], 1) result.month_precision! result.unspecified.year[2,2] = val[0][1] @@ -1509,7 +1509,7 @@ def _reduce_121(val, _values, result) module_eval(<<'.,.,', 'parser.y', 313) def _reduce_122(val, _values, result) - result = Date.new(val[0][0], 1, val[5]) + result = Date.gregorian(val[0][0], 1, val[5]) result.unspecified.year[2,2] = val[0][1] result.unspecified!(:month) @@ -1519,7 +1519,7 @@ def _reduce_122(val, _values, result) module_eval(<<'.,.,', 'parser.y', 319) def _reduce_123(val, _values, result) - result = Date.new(val[0][0], 1, 1) + result = Date.gregorian(val[0][0], 1, 1) result.unspecified.year[2,2] = val[0][1] result.unspecified!([:month, :day]) @@ -1529,7 +1529,7 @@ def _reduce_123(val, _values, result) module_eval(<<'.,.,', 'parser.y', 325) def _reduce_124(val, _values, result) - result = Date.new(val[0][0], val[2], 1) + result = Date.gregorian(val[0][0], val[2], 1) result.unspecified.year[2,2] = val[0][1] result.unspecified!(:day) @@ -1539,7 +1539,7 @@ def _reduce_124(val, _values, result) module_eval(<<'.,.,', 'parser.y', 331) def _reduce_125(val, _values, result) - result = Date.new(val[0], 1, val[5]) + result = Date.gregorian(val[0], 1, val[5]) result.unspecified!(:month) result @@ -1578,7 +1578,7 @@ def _reduce_130(val, _values, result) module_eval(<<'.,.,', 'parser.y', 344) def _reduce_131(val, _values, result) - result = uoa(Date.new(val[0]), val[1], :year) + result = uoa(Date.gregorian(val[0]), val[1], :year) result end .,., @@ -1593,7 +1593,7 @@ def _reduce_132(val, _values, result) module_eval(<<'.,.,', 'parser.y', 351) def _reduce_133(val, _values, result) - result = [uoa(Date.new(val[0], val[2]), val[3], [:year, :month])] + result = [uoa(Date.gregorian(val[0], val[2]), val[3], [:year, :month])] result end @@ -1601,7 +1601,7 @@ def _reduce_133(val, _values, result) module_eval(<<'.,.,', 'parser.y', 354) def _reduce_134(val, _values, result) - result = [uoa(Date.new(val[0], val[2]), val[4], [:month]), true] + result = [uoa(Date.gregorian(val[0], val[2]), val[4], [:month]), true] result end @@ -1633,7 +1633,7 @@ def _reduce_137(val, _values, result) module_eval(<<'.,.,', 'parser.y', 369) def _reduce_138(val, _values, result) - result = uoa(uoa(Date.new(val[0], val[2], val[5]), val[4], :month), val[6], :day) + result = uoa(uoa(Date.gregorian(val[0], val[2], val[5]), val[4], :month), val[6], :day) result end @@ -1641,7 +1641,7 @@ def _reduce_138(val, _values, result) module_eval(<<'.,.,', 'parser.y', 372) def _reduce_139(val, _values, result) - result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[3]) + result = uoa(Date.gregorian(val[0][0], val[0][1], val[2]), val[3]) result end @@ -1649,7 +1649,7 @@ def _reduce_139(val, _values, result) module_eval(<<'.,.,', 'parser.y', 375) def _reduce_140(val, _values, result) - result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[4], [:day]) + result = uoa(Date.gregorian(val[0][0], val[0][1], val[2]), val[4], [:day]) result end @@ -1657,7 +1657,7 @@ def _reduce_140(val, _values, result) module_eval(<<'.,.,', 'parser.y', 378) def _reduce_141(val, _values, result) - result = uoa(Date.new(val[0], val[2], val[4]), val[6], [:month, :day]) + result = uoa(Date.gregorian(val[0], val[2], val[4]), val[6], [:month, :day]) result end @@ -1665,7 +1665,7 @@ def _reduce_141(val, _values, result) module_eval(<<'.,.,', 'parser.y', 381) def _reduce_142(val, _values, result) - result = Date.new(val[0], val[2], val[4]) + result = Date.gregorian(val[0], val[2], val[4]) result = uoa(result, val[6], [:day]) result = uoa(result, val[8], [:month, :day]) diff --git a/lib/edtf/parser.y b/lib/edtf/parser.y index 976be02..930b163 100644 --- a/lib/edtf/parser.y +++ b/lib/edtf/parser.y @@ -21,9 +21,9 @@ rule ; date : - year { result = Date.new(val[0]).year_precision! } - | year_month { result = Date.new(*val.flatten).month_precision! } - | year_month_day { result = Date.new(*val.flatten).day_precision! } + year { result = Date.gregorian(val[0]).year_precision! } + | year_month { result = Date.gregorian(*val.flatten).month_precision! } + | year_month_day { result = Date.gregorian(*val.flatten).day_precision! } ; @@ -97,7 +97,7 @@ rule unspecified : unspecified_year { - result = Date.new(val[0][0]).year_precision! + result = Date.gregorian(val[0][0]).year_precision! result.unspecified.year[2,2] = val[0][1] } | unspecified_month @@ -123,16 +123,16 @@ rule } unspecified_month : year '-' U U { - result = Date.new(val[0]).unspecified!(:month) + result = Date.gregorian(val[0]).unspecified!(:month) result.precision = :month } unspecified_day : year_month '-' U U { - result = Date.new(*val[0]).unspecified!(:day) + result = Date.gregorian(*val[0]).unspecified!(:day) } unspecified_day_and_month : year '-' U U '-' U U { - result = Date.new(val[0]).unspecified!([:day,:month]) + result = Date.gregorian(val[0]).unspecified!([:day,:month]) } level_1_interval : level_1_interval_closed | level_1_interval_open | level_1_interval_unknown @@ -179,12 +179,12 @@ rule long_year_simple : LONGYEAR long_year { - result = Date.new(val[1]) + result = Date.gregorian(val[1]) result.precision = :year } | LONGYEAR '-' long_year { - result = Date.new(-1 * val[2]) + result = Date.gregorian(-1 * val[2]) result.precision = :year } ; @@ -229,15 +229,15 @@ rule long_year_scientific : long_year_simple E integer { - result = Date.new(val[0].year * 10 ** val[2]).year_precision! + result = Date.gregorian(val[0].year * 10 ** val[2]).year_precision! } | LONGYEAR int1_4 E integer { - result = Date.new(val[1] * 10 ** val[3]).year_precision! + result = Date.gregorian(val[1] * 10 ** val[3]).year_precision! } | LONGYEAR '-' int1_4 E integer { - result = Date.new(-1 * val[2] * 10 ** val[4]).year_precision! + result = Date.gregorian(-1 * val[2] * 10 ** val[4]).year_precision! } ; @@ -287,49 +287,49 @@ rule earlier : DOTS date { result = val[1] } - later : year_month_day DOTS { result = Date.new(*val[0]).day_precision! } - | year_month DOTS { result = Date.new(*val[0]).month_precision! } - | year DOTS { result = Date.new(val[0]).year_precision! } + later : year_month_day DOTS { result = Date.gregorian(*val[0]).day_precision! } + | year_month DOTS { result = Date.gregorian(*val[0]).month_precision! } + | year DOTS { result = Date.gregorian(val[0]).year_precision! } ; - consecutives : year_month_day DOTS year_month_day { result = (Date.new(*val[0]).day_precision! .. Date.new(*val[2]).day_precision!) } - | year_month DOTS year_month { result = (Date.new(*val[0]).month_precision! .. Date.new(*val[2]).month_precision!) } - | year DOTS year { result = (Date.new(val[0]).year_precision! .. Date.new(val[2]).year_precision!) } + consecutives : year_month_day DOTS year_month_day { result = (Date.gregorian(*val[0]).day_precision! .. Date.gregorian(*val[2]).day_precision!) } + | year_month DOTS year_month { result = (Date.gregorian(*val[0]).month_precision! .. Date.gregorian(*val[2]).month_precision!) } + | year DOTS year { result = (Date.gregorian(val[0]).year_precision! .. Date.gregorian(val[2]).year_precision!) } ; partial_unspecified : unspecified_year '-' month '-' day { - result = Date.new(val[0][0], val[2], val[4]) + result = Date.gregorian(val[0][0], val[2], val[4]) result.unspecified.year[2,2] = val[0][1] } | unspecified_year '-' month { - result = Date.new(val[0][0], val[2], 1) + result = Date.gregorian(val[0][0], val[2], 1) result.month_precision! result.unspecified.year[2,2] = val[0][1] } | unspecified_year '-' U U '-' day { - result = Date.new(val[0][0], 1, val[5]) + result = Date.gregorian(val[0][0], 1, val[5]) result.unspecified.year[2,2] = val[0][1] result.unspecified!(:month) } | unspecified_year '-' U U '-' U U { - result = Date.new(val[0][0], 1, 1) + result = Date.gregorian(val[0][0], 1, 1) result.unspecified.year[2,2] = val[0][1] result.unspecified!([:month, :day]) } | unspecified_year '-' month '-' U U { - result = Date.new(val[0][0], val[2], 1) + result = Date.gregorian(val[0][0], val[2], 1) result.unspecified.year[2,2] = val[0][1] result.unspecified!(:day) } | year '-' U U '-' day { - result = Date.new(val[0], 1, val[5]) + result = Date.gregorian(val[0], 1, val[5]) result.unspecified!(:month) } ; @@ -342,17 +342,17 @@ rule | pua_year_month { result = val[0][0].month_precision! } | pua_year_month_day { result = val[0].day_precision! } - pua_year : year UA { result = uoa(Date.new(val[0]), val[1], :year) } + pua_year : year UA { result = uoa(Date.gregorian(val[0]), val[1], :year) } pua_year_month : pua_year '-' month ua { result = [uoa(val[0].change(:month => val[2]), val[3], [:month, :year])] } | year '-' month UA { - result = [uoa(Date.new(val[0], val[2]), val[3], [:year, :month])] + result = [uoa(Date.gregorian(val[0], val[2]), val[3], [:year, :month])] } | year '-(' month ')' UA { - result = [uoa(Date.new(val[0], val[2]), val[4], [:month]), true] + result = [uoa(Date.gregorian(val[0], val[2]), val[4], [:month]), true] } | pua_year '-(' month ')' UA { result = [uoa(val[0].change(:month => val[2]), val[4], [:month]), true] @@ -367,19 +367,19 @@ rule result = uoa(val[0][0].change(:day => val[2]), val[4], [:day]) } | year '-(' month ')' UA day ua { - result = uoa(uoa(Date.new(val[0], val[2], val[5]), val[4], :month), val[6], :day) + result = uoa(uoa(Date.gregorian(val[0], val[2], val[5]), val[4], :month), val[6], :day) } | year_month '-' day UA { - result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[3]) + result = uoa(Date.gregorian(val[0][0], val[0][1], val[2]), val[3]) } | year_month '-(' day ')' UA { - result = uoa(Date.new(val[0][0], val[0][1], val[2]), val[4], [:day]) + result = uoa(Date.gregorian(val[0][0], val[0][1], val[2]), val[4], [:day]) } | year '-(' month '-' day ')' UA { - result = uoa(Date.new(val[0], val[2], val[4]), val[6], [:month, :day]) + result = uoa(Date.gregorian(val[0], val[2], val[4]), val[6], [:month, :day]) } | year '-(' month '-(' day ')' UA ')' UA { - result = Date.new(val[0], val[2], val[4]) + result = Date.gregorian(val[0], val[2], val[4]) result = uoa(result, val[6], [:day]) result = uoa(result, val[8], [:month, :day]) } diff --git a/spec/edtf/date_spec.rb b/spec/edtf/date_spec.rb index ae83540..382a7a9 100644 --- a/spec/edtf/date_spec.rb +++ b/spec/edtf/date_spec.rb @@ -4,6 +4,20 @@ it 'responds to edtf' do expect(Date).to respond_to(:edtf) end + + describe 'gregorian' do + it 'returns a proleptic Gregorian date for pre-Gregorian dates' do + date = Date.gregorian(1582, 9, 30) + expect(date).to be_gregorian + expect(date).to eq(Date.new(1582, 9, 30, Date::GREGORIAN)) + end + + it 'returns a Gregorian date for valid Gregorian dates' do + date = Date.gregorian(1582, 10, 30) + expect(date).to be_gregorian + expect(date).to eq(Date.new(1582, 10, 30)) + end + end end describe 'instance methods' do diff --git a/spec/edtf/parser_spec.rb b/spec/edtf/parser_spec.rb index 78f573d..308663b 100644 --- a/spec/edtf/parser_spec.rb +++ b/spec/edtf/parser_spec.rb @@ -17,6 +17,17 @@ module EDTF it 'parses year zero' do expect(Parser.new.parse('0000').to_s).to eq('0000-01-01') end + + it 'handles the Gregorian 10-day correction' do + (4..15).each do |mday| + date_str = '1582-10-%02d' % mday + date = Parser.new.parse(date_str) + expect(date).to be_gregorian + expect(date.year).to eq(1582) + expect(date.month).to eq(10) + expect(date.mday).to eq(mday) + end + end it 'parses date/time with time zones' do expect(Parser.new.parse('2011-08-15T11:19:00+01:00').to_s).to eq('2011-08-15T11:19:00+01:00')