From 57b0364d9dbfb2baa855b3ea4d24817b0258b828 Mon Sep 17 00:00:00 2001 From: Andrew Vit Date: Mon, 7 Aug 2017 23:36:03 -0700 Subject: [PATCH 1/5] Handle possible string formats for numeric offsets Parse numeric strings to avoid errors when API responses don't send a known zone string but default to a numeric offset instead. --- lib/barometer/data/zone.rb | 18 ++++++++++++------ spec/data/zone_spec.rb | 27 +++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/lib/barometer/data/zone.rb b/lib/barometer/data/zone.rb index 4b3fe48..28acc0b 100644 --- a/lib/barometer/data/zone.rb +++ b/lib/barometer/data/zone.rb @@ -59,21 +59,27 @@ def utc_to_local(utc_time) end class ZoneOffset + NUMERIC_OFFSET = /^([-+]?[01]?\d)(\d\d)?$/ + def self.detect?(zone) + zone.respond_to?(:match) && zone.match(NUMERIC_OFFSET) { |m| zone = m[1].to_i } zone.respond_to?(:abs) && zone.abs <= 14 end def initialize(zone, time_class=::Time) @zone = zone + @offset = case zone + when Integer + zone * 60 * 60 + when NUMERIC_OFFSET + h = $1.to_i * 60 * 60 + m = $2.to_i * 60 + h < 0 ? h - m : h + m + end @time_class = time_class end - def code - end - - def offset - zone.to_f * 60 * 60 - end + attr_reader :code, :offset def now time_class.now.utc + offset diff --git a/spec/data/zone_spec.rb b/spec/data/zone_spec.rb index cdb9877..4a856aa 100644 --- a/spec/data/zone_spec.rb +++ b/spec/data/zone_spec.rb @@ -154,12 +154,30 @@ def stub_time(utc_now) expect( ZoneOffset.detect?('PST') ).to be false end - it 'returns true when given an offset' do + it 'returns true when given a numeric offset' do expect( ZoneOffset.detect?(10) ).to be true end + it 'returns true when given a one-digit hour offset' do + expect( ZoneOffset.detect?('1') ).to be true + expect( ZoneOffset.detect?('+1') ).to be true + expect( ZoneOffset.detect?('-1') ).to be true + end + + it 'returns true when given a two-digit hour offset' do + expect( ZoneOffset.detect?('09') ).to be true + expect( ZoneOffset.detect?('+09') ).to be true + expect( ZoneOffset.detect?('-09') ).to be true + end + + it 'returns true when given a four-digit offset' do + expect( ZoneOffset.detect?('0100') ).to be true + expect( ZoneOffset.detect?('+0100') ).to be true + expect( ZoneOffset.detect?('-1200') ).to be true + end + it 'returns false when given an offset out of range' do - expect( ZoneOffset.detect?(15) ).to be false + expect( ZoneOffset.detect?('15') ).to be false end it 'returns false when given nothing' do @@ -178,6 +196,11 @@ def stub_time(utc_now) it 'converts the input from hours to seconds' do expect( ZoneOffset.new(5).offset ).to eq(5 * 60 * 60) end + + it 'converts 4-digit input from HHMM to seconds' do + expect( ZoneOffset.new('+0130').offset ).to eq(90 * 60) + expect( ZoneOffset.new('-0130').offset ).to eq(-90 * 60) + end end describe '#now' do From 455b5678f243c893975c4b0b86815d39edb97e4e Mon Sep 17 00:00:00 2001 From: Andrew Vit Date: Wed, 9 Aug 2017 11:35:16 -0700 Subject: [PATCH 2/5] Zone parsing needs to extract offset from end of string The entire time string is passed to the zone parser, so look for numeric offsets that may be preceded by a space. --- lib/barometer/data/zone.rb | 2 +- spec/data/zone_spec.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/barometer/data/zone.rb b/lib/barometer/data/zone.rb index 28acc0b..097123c 100644 --- a/lib/barometer/data/zone.rb +++ b/lib/barometer/data/zone.rb @@ -59,7 +59,7 @@ def utc_to_local(utc_time) end class ZoneOffset - NUMERIC_OFFSET = /^([-+]?[01]?\d)(\d\d)?$/ + NUMERIC_OFFSET = /(?:^| )([-+]?[01]?\d)(\d\d)?$/ def self.detect?(zone) zone.respond_to?(:match) && zone.match(NUMERIC_OFFSET) { |m| zone = m[1].to_i } diff --git a/spec/data/zone_spec.rb b/spec/data/zone_spec.rb index 4a856aa..f41accc 100644 --- a/spec/data/zone_spec.rb +++ b/spec/data/zone_spec.rb @@ -176,6 +176,14 @@ def stub_time(utc_now) expect( ZoneOffset.detect?('-1200') ).to be true end + it 'returns true when preceded by a space' do + expect('August 9, 6:56 AM -10').to be true + end + + it 'returns false when part of a date' do + expect('2017-10-10').to be false + end + it 'returns false when given an offset out of range' do expect( ZoneOffset.detect?('15') ).to be false end From a5731bf6f02ac5bd2ad4108cb167c173929e5cb6 Mon Sep 17 00:00:00 2001 From: Andrew Vit Date: Thu, 17 Aug 2017 10:50:43 -0700 Subject: [PATCH 3/5] Fix spec --- spec/data/zone_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/data/zone_spec.rb b/spec/data/zone_spec.rb index f41accc..3c29aca 100644 --- a/spec/data/zone_spec.rb +++ b/spec/data/zone_spec.rb @@ -177,11 +177,11 @@ def stub_time(utc_now) end it 'returns true when preceded by a space' do - expect('August 9, 6:56 AM -10').to be true + expect( ZoneOffset.detect?('August 9, 6:56 AM -10') ).to be true end it 'returns false when part of a date' do - expect('2017-10-10').to be false + expect( ZoneOffset.detect?('2017-10-10') ).to be false end it 'returns false when given an offset out of range' do From a503dca98099765628cc7c717c38815e6168187a Mon Sep 17 00:00:00 2001 From: Andrew Vit Date: Thu, 17 Aug 2017 10:53:52 -0700 Subject: [PATCH 4/5] Wunderground sends local time without a zone Parsing the time string using `strptime` against the format string was causing errors. Unsure if all times sent from Wunderground are like this, or only some locations. In any case, both formats are recognized without the format string using the `DateTime.parse` method --- .../wunderground_v1/response/current_weather.rb | 2 +- spec/utils/time_spec.rb | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/barometer/weather_services/wunderground_v1/response/current_weather.rb b/lib/barometer/weather_services/wunderground_v1/response/current_weather.rb index 69f6363..7cddf8c 100644 --- a/lib/barometer/weather_services/wunderground_v1/response/current_weather.rb +++ b/lib/barometer/weather_services/wunderground_v1/response/current_weather.rb @@ -9,7 +9,7 @@ def initialize(payload) end def parse - current.observed_at = observed_at, '%B %e, %l:%M %p %Z' + current.observed_at = observed_at current.stale_at = stale_at current.humidity = humidity current.condition = condition diff --git a/spec/utils/time_spec.rb b/spec/utils/time_spec.rb index 74c2154..c1f91b4 100644 --- a/spec/utils/time_spec.rb +++ b/spec/utils/time_spec.rb @@ -35,13 +35,21 @@ module Barometer assert_times_are_equal(time, start_of_local_day) end - it "parses a String (with no format), assumes UTC" do + it "parses a String with year (with no format), assumes UTC" do t = "March 15, 10:36 AM 2013" time = Utils::Time.parse(t) assert_times_are_equal(time, ::Time.utc(2013, 3, 15, 10, 36, 0)) end + it "parses a String without year (with no format), assumes UTC" do + t = "August 17, 1:16 AM" + + time = Utils::Time.parse(t) + current_year = Time.now.year + assert_times_are_equal(time, ::Time.utc(current_year, 8, 17, 1, 16, 0)) + end + it "parses a String (with optional format), assumes UTC" do format = "%B %e, %l:%M %p %Y" t = "March 15, 10:36 AM 2013" @@ -58,6 +66,13 @@ module Barometer assert_times_are_equal(time, ::Time.utc(2013, 3, 15, 18, 36, 0)) end + it "parses a timezoned String (with no format)" do + t = "March 15, 10:36 AM -0800 2013" + + time = Utils::Time.parse(t) + assert_times_are_equal(time, ::Time.utc(2013, 3, 15, 18, 36, 0)) + end + it "accepts an array of values, creating a UTC time" do time = Utils::Time.parse(2013, 3, 15, 18, 36, 0) assert_times_are_equal(time, ::Time.utc(2013, 3, 15, 18, 36, 0)) From ed4ac03cd055eb13dba6d86a5e485ca747295d65 Mon Sep 17 00:00:00 2001 From: Andrew Vit Date: Thu, 17 Aug 2017 11:48:32 -0700 Subject: [PATCH 5/5] Test for sanity check --- spec/data/zone_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/data/zone_spec.rb b/spec/data/zone_spec.rb index 3c29aca..71d196c 100644 --- a/spec/data/zone_spec.rb +++ b/spec/data/zone_spec.rb @@ -180,6 +180,10 @@ def stub_time(utc_now) expect( ZoneOffset.detect?('August 9, 6:56 AM -10') ).to be true end + it 'returns false when only given a year' do + expect( ZoneOffset.detect?('August 9, 6:56 AM 2017') ).to be false + end + it 'returns false when part of a date' do expect( ZoneOffset.detect?('2017-10-10') ).to be false end