From 909e9e7e2bcd722c68a079f087f45a25ebc9b6fb Mon Sep 17 00:00:00 2001 From: Eugene Kenny Date: Thu, 7 Mar 2024 08:52:24 +0000 Subject: [PATCH] Resolve ambiguity for permanent timezone changes `Biz::Time` already passes true as the `dst` parameter to `local_to_utc` to avoid the ambiguity of converting a local time that occurred twice due to daylight savings time. However if a local time occurs twice due to a permanent timezone change, the `dst` parameter doesn't help. `local_to_utc` can be given a block to handle this case: https://github.com/tzinfo/tzinfo/blob/v2.0.6/lib/tzinfo/timezone.rb#L613-L619 > The `dst` parameter will not be able to resolve an ambiguity resulting > from the clocks being set back without changing from daylight savings > time to standard time. In this case, if a block is specified, it will > be called to resolve the ambiguity. The block must take a single > parameter - an `Array` of {TimezonePeriod}s that need to be resolved. > The block can select and return a single {TimezonePeriod} or return > `nil` or an empty `Array` to cause an {AmbiguousTime} exception to be > raised. --- lib/biz/time.rb | 3 ++- spec/time_spec.rb | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/biz/time.rb b/lib/biz/time.rb index 29bcada..ea108cc 100644 --- a/lib/biz/time.rb +++ b/lib/biz/time.rb @@ -86,7 +86,8 @@ def on_date(date, day_time) day_time.minute, day_time.second ), - true + true, + &:first ) rescue TZInfo::PeriodNotFound on_date(Date.for_dst(date, day_time), day_time.for_dst) diff --git a/spec/time_spec.rb b/spec/time_spec.rb index e204a75..9fa9ff7 100644 --- a/spec/time_spec.rb +++ b/spec/time_spec.rb @@ -130,6 +130,18 @@ ) end end + + context 'when an ambiguous non-daylight-savings time is targeted' do + let(:time_zone) { TZInfo::Timezone.get('Asia/Pyongyang') } + let(:date) { Date.new(2015, 8, 14) } + let(:day_time) { Biz::DayTime.new(day_second(hour: 23, min: 59)) } + + it 'returns the earliest occurrence of the time' do + expect(time.on_date(date, day_time)).to eq( + time_zone.local_to_utc(Time.utc(2015, 8, 14, 23, 59), &:first) + ) + end + end end describe '#during_week' do