diff --git a/lib/chronic/repeaters/repeater_time.rb b/lib/chronic/repeaters/repeater_time.rb index 0a496e73..2d90d5cd 100644 --- a/lib/chronic/repeaters/repeater_time.rb +++ b/lib/chronic/repeaters/repeater_time.rb @@ -81,27 +81,32 @@ def next(pointer) yesterday_midnight = midnight - full_day tomorrow_midnight = midnight + full_day - offset_fix = midnight.gmt_offset - tomorrow_midnight.gmt_offset - tomorrow_midnight += offset_fix - catch :done do if pointer == :future if @type.ambiguous? - [midnight + @type.time + offset_fix, midnight + half_day + @type.time + offset_fix, tomorrow_midnight + @type.time].each do |t| + [midnight, midnight + half_day, tomorrow_midnight].each do |base_time| + base_time = adjust_daylight_savings_offset(midnight, base_time) + t = adjust_daylight_savings_offset(base_time, base_time + @type.time) (@current_time = t; throw :done) if t >= @now end else - [midnight + @type.time + offset_fix, tomorrow_midnight + @type.time].each do |t| + [midnight, tomorrow_midnight].each do |base_time| + base_time = adjust_daylight_savings_offset(midnight, base_time) + t = adjust_daylight_savings_offset(base_time, base_time + @type.time) (@current_time = t; throw :done) if t >= @now end end else # pointer == :past if @type.ambiguous? - [midnight + half_day + @type.time + offset_fix, midnight + @type.time + offset_fix, yesterday_midnight + @type.time + half_day].each do |t| + [midnight + half_day, midnight, yesterday_midnight + half_day].each do |base_time| + base_time = adjust_daylight_savings_offset(midnight, base_time) + t = adjust_daylight_savings_offset(base_time, base_time + @type.time) (@current_time = t; throw :done) if t <= @now end else - [midnight + @type.time + offset_fix, yesterday_midnight + @type.time].each do |t| + [midnight, yesterday_midnight].each do |base_time| + base_time = adjust_daylight_savings_offset(midnight, base_time) + t = adjust_daylight_savings_offset(base_time, base_time + @type.time) (@current_time = t; throw :done) if t <= @now end end @@ -119,6 +124,26 @@ def next(pointer) Span.new(@current_time, @current_time + width) end + # Every time a time crosses a Daylight Savings interval we must adjust the + # current time by that amount. For example, if we take midnight of Daylight + # Savings and only add an hour, the offset does not change: + # + # Time.parse('2008-03-09 00:00') + # => 2008-03-09 00:00:00 -0800 + # Time.parse('2008-03-09 00:00') + (60 * 60) + # => 2008-03-09 01:00:00 -0800 + # + # However, if we add 2 hours, we notice the time advances to 03:00 instead of 02:00: + # + # Time.parse('2008-03-09 00:00') + (60 * 60 * 2) + # => 2008-03-09 03:00:00 -0700 + # + # Since we gained an hour and we actually want 02:00, we subtract an hour. + def adjust_daylight_savings_offset(base_time, current_time) + offset_fix = base_time.gmt_offset - current_time.gmt_offset + current_time + offset_fix + end + def this(context = :future) super diff --git a/test/test_daylight_savings.rb b/test/test_daylight_savings.rb index d6f303fb..1b11c3af 100644 --- a/test/test_daylight_savings.rb +++ b/test/test_daylight_savings.rb @@ -86,6 +86,11 @@ def test_end_past t = Chronic::RepeaterTime.new('1300') t.start = @end_daylight_savings assert_equal Time.local(2008, 11, 1, 13), t.next(:past).begin + + # unambiguous - resolve to yesterday + t = Chronic::RepeaterTime.new('1300') + t.start = @end_daylight_savings + 60 * 60 * 24 # Advance one day + assert_equal Time.local(2008, 11, 2, 13), t.next(:past).begin end def test_end_future @@ -114,5 +119,4 @@ def test_end_future t.start = @end_daylight_savings assert_equal Time.local(2008, 11, 3, 4), t.next(:future).begin end - end diff --git a/test/test_parsing.rb b/test/test_parsing.rb index 932b4d23..3a359526 100644 --- a/test/test_parsing.rb +++ b/test/test_parsing.rb @@ -41,7 +41,9 @@ def test_handle_generic time2 = Time.parse("2012-01-03 01:00:00.234567") assert_in_delta time, time2, 0.000001 - assert_nil Chronic.parse("1/1/32.1") + time = Chronic.parse("2020-11-01 00:00:00") + time2 = Time.parse("2020-11-01 00:00:00") + assert_in_delta time, time2, 0.000001 time = Chronic.parse("28th", {:guess => :begin}) assert_equal Time.new(Time.now.year, Time.now.month, 28), time