From b046086d7f19a2e50320795891ebd7502d7af1ce Mon Sep 17 00:00:00 2001 From: Joseph Dayo Date: Tue, 19 Dec 2017 18:31:45 +0800 Subject: [PATCH] Add support for timezone offsets --- README.md | 5 +++++ lib/cron_parser.rb | 20 ++++++++++++++------ spec/cron_parser_spec.rb | 24 ++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d40d529..847f3a4 100644 --- a/README.md +++ b/README.md @@ -14,5 +14,10 @@ next_time = cron_parser.next(Time.now) # Last occurrence most_recent_time = cron_parser.last(Time.now) + +# With Timezone + +cron_parser = CronParser.new('30 * * * * +08:00') + ``` diff --git a/lib/cron_parser.rb b/lib/cron_parser.rb index 41a8a5e..c556409 100644 --- a/lib/cron_parser.rb +++ b/lib/cron_parser.rb @@ -7,9 +7,9 @@ class CronParser # internal "mutable" time representation class InternalTime attr_accessor :year, :month, :day, :hour, :min - attr_accessor :time_source + attr_accessor :time_source, :offset - def initialize(time,time_source = Time) + def initialize(time, time_source = Time) @year = time.year @month = time.month @day = time.day @@ -20,7 +20,11 @@ def initialize(time,time_source = Time) end def to_time - time_source.local(@year, @month, @day, @hour, @min, 0) + if @offset + time_source.new(@year, @month, @day, @hour, @min, 0, @offset) + else + time_source.local(@year, @month, @day, @hour, @min, 0) + end end def inspect @@ -50,10 +54,11 @@ def inspect "fri" => "5", "sat" => "6" } - - def initialize(source,time_source = Time) + attr_accessor :offset + def initialize(source,time_source = Time, offset = nil) @source = interpret_vixieisms(source) @time_source = time_source + @offset = offset validate_source end @@ -80,6 +85,7 @@ def interpret_vixieisms(spec) # returns the next occurence after the given date def next(now = @time_source.now, num = 1) t = InternalTime.new(now, @time_source) + t.offset = time_specs[:offset] unless time_specs[:month][0].include?(t.month) nudge_month(t) @@ -109,6 +115,7 @@ def next(now = @time_source.now, num = 1) # returns the last occurence before the given date def last(now = @time_source.now, num=1) t = InternalTime.new(now,@time_source) + t.offset = time_specs[:offset] unless time_specs[:month][0].include?(t.month) nudge_month(t, :last) @@ -253,7 +260,8 @@ def time_specs :hour => parse_element(tokens[1], 0..23), #hour :dom => parse_element(tokens[2], 1..31), #DOM :month => parse_element(tokens[3], 1..12), #mon - :dow => parse_element(tokens[4], 0..6) #DOW + :dow => parse_element(tokens[4], 0..6), #DOW + :offset => (tokens.length == 6) ? tokens[5] : nil, #timezone offset } end end diff --git a/spec/cron_parser_spec.rb b/spec/cron_parser_spec.rb index 94f7ad0..164d6ce 100644 --- a/spec/cron_parser_spec.rb +++ b/spec/cron_parser_spec.rb @@ -8,6 +8,11 @@ def parse_date(str) Time.local(dt.year, dt.month, dt.day, dt.hour, dt.min, 0) end +def parse_date_with_tz(str) + dt = DateTime.strptime(str, "%Y-%m-%d %H:%M %z") + Time.new(dt.year, dt.month, dt.day, dt.hour, dt.min, 0, dt.zone) +end + describe "CronParser#parse_element" do [ ["*", 0..59, (0..59).to_a], @@ -169,6 +174,25 @@ def parse_date(str) end end +describe "With timezone" do + describe "#time_spec" do + it "returns the correct breakdown" do + parser = CronParser.new('0 17 * * * -08:00') + expect(parser.send(:time_specs)[:offset]).to eq("-08:00") + end + end + + it "Should return in the specified timezone with the correct computed day" do + now = parse_date_with_tz('2017-12-14 17:15 -08:00') + expected_last = parse_date_with_tz('2017-12-14 17:00 -08:00') + expected_next = parse_date_with_tz('2017-12-15 17:00 -08:00') + + parser = CronParser.new('0 17 * * * -08:00') + parser.next(now).should == expected_next + parser.last(now).should == expected_last + end +end + describe "CronParser#new" do it 'should not raise error when given a valid cronline' do expect { CronParser.new('30 * * * *') }.not_to raise_error