zone_ranger 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +21 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +35 -0
- data/LICENSE +20 -0
- data/README.md +4 -0
- data/Rakefile +1 -0
- data/lib/zone_ranger/core.rb +182 -0
- data/lib/zone_ranger/util.rb +47 -0
- data/lib/zone_ranger/version.rb +3 -0
- data/lib/zone_ranger.rb +3 -0
- data/spec/core_spec.rb +260 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/util_spec.rb +24 -0
- data/zone_ranger.gemspec +30 -0
- metadata +185 -0
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
coverage
|
6
|
+
InstalledFiles
|
7
|
+
lib/bundler/man
|
8
|
+
pkg
|
9
|
+
rdoc
|
10
|
+
spec/reports
|
11
|
+
test/tmp
|
12
|
+
test/version_tmp
|
13
|
+
tmp
|
14
|
+
.ruby-version
|
15
|
+
.rbenv*
|
16
|
+
.DS_Store
|
17
|
+
|
18
|
+
# YARD artifacts
|
19
|
+
.yardoc
|
20
|
+
_yardoc
|
21
|
+
doc/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
zone_ranger (0.0.1)
|
5
|
+
activesupport (~> 3.0.20)
|
6
|
+
i18n
|
7
|
+
tzinfo (~> 0.3.35)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activesupport (3.0.20)
|
13
|
+
diff-lcs (1.2.4)
|
14
|
+
i18n (0.6.5)
|
15
|
+
rake (10.1.0)
|
16
|
+
rspec (2.14.1)
|
17
|
+
rspec-core (~> 2.14.0)
|
18
|
+
rspec-expectations (~> 2.14.0)
|
19
|
+
rspec-mocks (~> 2.14.0)
|
20
|
+
rspec-core (2.14.5)
|
21
|
+
rspec-expectations (2.14.2)
|
22
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
23
|
+
rspec-mocks (2.14.3)
|
24
|
+
timecop (0.3.5)
|
25
|
+
tzinfo (0.3.37)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
bundler (~> 1.3)
|
32
|
+
rake
|
33
|
+
rspec
|
34
|
+
timecop (~> 0.3.5)
|
35
|
+
zone_ranger!
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Rigor
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
|
3
|
+
module ZoneRanger
|
4
|
+
class Core
|
5
|
+
|
6
|
+
attr_reader :start_time_string, :repeat_type
|
7
|
+
attr_reader :duration_in_minutes
|
8
|
+
|
9
|
+
def initialize start_date_time, duration_in_minutes, timezone, options={}
|
10
|
+
|
11
|
+
@start_time_string = start_date_time
|
12
|
+
@duration_in_minutes = duration_in_minutes
|
13
|
+
@timezone = timezone
|
14
|
+
|
15
|
+
@ending = options.fetch(:ending, nil)
|
16
|
+
|
17
|
+
@repeat_type = options.fetch(:repeat, nil)
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def repeat?
|
22
|
+
!!@repeat_type
|
23
|
+
end
|
24
|
+
|
25
|
+
def daily?
|
26
|
+
@repeat_type == :daily
|
27
|
+
end
|
28
|
+
|
29
|
+
def weekly?
|
30
|
+
@repeat_type == :weekly
|
31
|
+
end
|
32
|
+
|
33
|
+
def monthly_by_day_of_week?
|
34
|
+
@repeat_type == :monthly_by_day_of_week
|
35
|
+
end
|
36
|
+
|
37
|
+
def monthly_by_day_of_month?
|
38
|
+
@repeat_type == :monthly_by_day_of_month
|
39
|
+
end
|
40
|
+
|
41
|
+
def timezone_object
|
42
|
+
ActiveSupport::TimeZone.new((@timezone || "UTC"))
|
43
|
+
end
|
44
|
+
|
45
|
+
def includes? time_point=Time.now.utc
|
46
|
+
return false if not_started_yet?(time_point) || expired?(time_point)
|
47
|
+
|
48
|
+
if repeat?
|
49
|
+
included_in_repeating_timeframe? time_point
|
50
|
+
else
|
51
|
+
static_include? time_point
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def static_include? time_point
|
56
|
+
zoned_time(time_point).between?(*time_range(time_point))
|
57
|
+
end
|
58
|
+
|
59
|
+
def expired? time=Time.now.utc
|
60
|
+
return false unless @ending
|
61
|
+
zoned_time(time) > zoned_end_date
|
62
|
+
end
|
63
|
+
|
64
|
+
def zoned_time time=nil
|
65
|
+
tzone = timezone_object
|
66
|
+
if time.nil?
|
67
|
+
tzone.now
|
68
|
+
else
|
69
|
+
time.in_time_zone(tzone)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def zoned_date time=Time.now.utc
|
74
|
+
zoned_time(time).to_date
|
75
|
+
end
|
76
|
+
|
77
|
+
def zoned_end_date
|
78
|
+
timezone_object.parse("#{@ending} 23:59:59 #{utc_offset}")
|
79
|
+
end
|
80
|
+
|
81
|
+
def utc_offset
|
82
|
+
ActiveSupport::TimeZone.seconds_to_utc_offset(zoned_time.utc_offset)
|
83
|
+
end
|
84
|
+
|
85
|
+
def not_started_yet? time
|
86
|
+
start_on > zoned_date
|
87
|
+
end
|
88
|
+
|
89
|
+
def start_on
|
90
|
+
parsed_time_string.to_date
|
91
|
+
end
|
92
|
+
|
93
|
+
def started?
|
94
|
+
!not_started_yet?
|
95
|
+
end
|
96
|
+
|
97
|
+
def time_range current_day=Time.now.utc, options={}
|
98
|
+
|
99
|
+
offset = options.fetch(:offset, 0)
|
100
|
+
|
101
|
+
start_date = if repeat?
|
102
|
+
#if daily?
|
103
|
+
# shift to the day being checked
|
104
|
+
start_time = zoned_time(current_day + offset.days)
|
105
|
+
start_time.to_date
|
106
|
+
#end
|
107
|
+
else
|
108
|
+
parsed_time_string.to_date
|
109
|
+
end
|
110
|
+
|
111
|
+
b_start = timezone_object.parse(parsed_time_string.strftime("#{start_date} %H:%M")).in_time_zone(timezone_object)
|
112
|
+
b_end = b_start + duration_in_minutes.minutes
|
113
|
+
|
114
|
+
[b_start, b_end]
|
115
|
+
end
|
116
|
+
|
117
|
+
def parsed_time_string
|
118
|
+
timezone_object.parse(start_time_string)
|
119
|
+
end
|
120
|
+
|
121
|
+
protected
|
122
|
+
|
123
|
+
def included_in_repeating_timeframe? time_point
|
124
|
+
repeat? && (daily? && include_for_daily?(time_point) ||
|
125
|
+
weekly? && include_for_weekly?(time_point) ||
|
126
|
+
monthly_by_day_of_week? && include_for_monthly_dow?(time_point) ||
|
127
|
+
monthly_by_day_of_month? && include_for_monthly_dom?(time_point))
|
128
|
+
end
|
129
|
+
|
130
|
+
def include_for_daily? time_point
|
131
|
+
zoned_time(time_point).between?(*time_range(time_point, :offset => -1)) || zoned_time(time_point).between?(*time_range(time_point))
|
132
|
+
end
|
133
|
+
|
134
|
+
def include_for_weekly? time_point
|
135
|
+
start_date = zoned_time(time_point).to_date
|
136
|
+
start_at, end_at = time_range(time_point)
|
137
|
+
|
138
|
+
if crosses_midnight?
|
139
|
+
wday1 = parsed_time_string.wday
|
140
|
+
wday2 = parsed_time_string.tomorrow.wday
|
141
|
+
|
142
|
+
case start_date.wday
|
143
|
+
when wday1
|
144
|
+
zoned_time(time_point).between?(start_at, end_at)
|
145
|
+
when wday2
|
146
|
+
yesterday_start, yesterday_end = time_range(zoned_time(time_point), :offset => -1)
|
147
|
+
zoned_time(time_point).between?(yesterday_start, yesterday_end)
|
148
|
+
else false
|
149
|
+
end
|
150
|
+
else
|
151
|
+
Util.wday_match?(start_at, parsed_time_string) && zoned_time(time_point).between?(start_at, end_at)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def include_for_monthly_dow? time_point
|
156
|
+
start_date = parsed_time_string.to_date
|
157
|
+
|
158
|
+
start_week = Util.week_of_month(start_date)
|
159
|
+
time_week = Util.week_of_month(zoned_time(time_point))
|
160
|
+
|
161
|
+
start_week == time_week && include_for_weekly?(time_point)
|
162
|
+
end
|
163
|
+
|
164
|
+
def include_for_monthly_dom? time_point
|
165
|
+
original_day = parsed_time_string.day
|
166
|
+
today_day = zoned_time(time_point).day
|
167
|
+
|
168
|
+
if today_day == original_day
|
169
|
+
static_include? time_point
|
170
|
+
elsif (today_day == parsed_time_string.tomorrow.day) && crosses_midnight?
|
171
|
+
zoned_time(time_point).between?(*time_range(time_point, :offset => -1))
|
172
|
+
else
|
173
|
+
false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def crosses_midnight?
|
178
|
+
Util.crosses_one_utc_midnight?(parsed_time_string, @duration_in_minutes)
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ZoneRanger
|
2
|
+
class Util
|
3
|
+
|
4
|
+
def self.week_of_month time
|
5
|
+
day_num = time.day
|
6
|
+
days_in_month = Time.days_in_month(time.month)
|
7
|
+
|
8
|
+
if day_num < 8
|
9
|
+
1
|
10
|
+
elsif day_num < 15
|
11
|
+
2
|
12
|
+
elsif day_num < 22
|
13
|
+
3
|
14
|
+
elsif day_num < days_in_month - 7
|
15
|
+
4 # fourth but not last
|
16
|
+
else
|
17
|
+
:last # last
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.crosses_one_utc_midnight? start, duration
|
22
|
+
# a utc midnight is when the start day is different the blackout ends on
|
23
|
+
day1 = start.utc.to_date
|
24
|
+
day2 = (start + duration.minutes).utc.to_date
|
25
|
+
day1 != day2
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.wday_match? date1, date2
|
29
|
+
date1.wday == date2.wday
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.calculate_duration_in_minutes starting, ending
|
33
|
+
start_hour = starting.strftime("%H")
|
34
|
+
start_minutes = starting.strftime("%M")
|
35
|
+
start_in_minutes = (start_hour.to_i * 60) + start_minutes.to_i
|
36
|
+
|
37
|
+
finish_hour = ending.strftime("%H").to_i
|
38
|
+
finish_minutes = (finish_hour * 60) + ending.strftime("%M").to_i
|
39
|
+
|
40
|
+
d = finish_minutes - start_in_minutes
|
41
|
+
d += 1440 if d < 0
|
42
|
+
|
43
|
+
d
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/zone_ranger.rb
ADDED
data/spec/core_spec.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ZoneRanger::Core do
|
4
|
+
|
5
|
+
def timeize t_or_s
|
6
|
+
t_or_s.is_a?(Time) ? t_or_s : Time.parse(t_or_s)
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate_active zr, time
|
10
|
+
Timecop.freeze(timeize(time))
|
11
|
+
zr.includes?(timeize(time)).should(be_true, "#{time.to_s} expected ACTIVE")
|
12
|
+
Timecop.return
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_inactive zr, time
|
16
|
+
Timecop.freeze(timeize(time))
|
17
|
+
zr.includes?(timeize(time)).should(be_false, "#{time.to_s} expected INACTIVE")
|
18
|
+
Timecop.return
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "standard operation" do
|
22
|
+
it "should require a start time, duration, and timezone" do
|
23
|
+
expect{ ZoneRanger::Core.new.includes? Time.now, 30 }.to raise_error ArgumentError
|
24
|
+
expect{ ZoneRanger::Core.new.includes? Time.now }.to raise_error ArgumentError
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not throw error if all fields are supplied" do
|
28
|
+
ZoneRanger::Core.new("#{Time.now - 300}", 30, "Eastern Time (US & Canada)").includes?
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should accept a Time parameter" do
|
32
|
+
ZoneRanger::Core.new("#{Time.now - 300}", 30, "Eastern Time (US & Canada)").includes? Time.now.utc
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should repeat? if told" do
|
36
|
+
zr = ZoneRanger::Core.new('2013-04-01 06:00 -04:00', 30, "Eastern Time (US & Canada)", :repeat => :daily)
|
37
|
+
zr.repeat?.should be_true
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should have a timezone" do
|
41
|
+
zr = ZoneRanger::Core.new('2013-04-01 06:00 -04:00', 30, "Eastern Time (US & Canada)")
|
42
|
+
zr.timezone_object.should be_a ActiveSupport::TimeZone
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#time_range" do
|
47
|
+
context "no repeat" do
|
48
|
+
it "should return the correct time range - same day" do
|
49
|
+
zr = ZoneRanger::Core.new('2013-04-01 06:00 -04:00', 30, "Eastern Time (US & Canada)")
|
50
|
+
zr.time_range.should eql([Time.parse('2013-04-01 06:00 -04:00'), Time.parse('2013-04-01 06:30 -04:00')])
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return the correct time range - crossing midnight in user's timezone" do
|
54
|
+
zr = ZoneRanger::Core.new('2013-04-01 23:01 -04:00', 60, "Eastern Time (US & Canada)")
|
55
|
+
zr.time_range.should eql([Time.parse('2013-04-01 23:01 -04:00'), Time.parse('2013-04-02 00:01 -04:00')])
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should work with other time zones" do
|
59
|
+
zr = ZoneRanger::Core.new('2013-04-01 23:01 -07:00', 60, "Pacific Time (US & Canada)")
|
60
|
+
zr.time_range.should eql([Time.parse('2013-04-01 23:01 -07:00'), Time.parse('2013-04-02 00:01 -07:00')])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "repeat daily" do
|
65
|
+
it "should return the correct time range - same day" do
|
66
|
+
zr = ZoneRanger::Core.new('2013-04-01 06:00 -04:00', 30, "Eastern Time (US & Canada)", :repeat => :daily)
|
67
|
+
zr.time_range(Time.parse('2013-04-02 06:00 -04:00')).should eql([Time.parse('2013-04-02 06:00 -04:00'), Time.parse('2013-04-02 06:30 -04:00')])
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should return the correct time range - crossing midnight in user's timezone" do
|
71
|
+
zr = ZoneRanger::Core.new('2013-04-01 23:01 -04:00', 60, "Eastern Time (US & Canada)", :repeat => :daily)
|
72
|
+
zr.time_range(Time.parse('2013-04-02 06:00 -04:00')).should eql([Time.parse('2013-04-02 23:01 -04:00'), Time.parse('2013-04-03 00:01 -04:00')])
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should work with other time zones" do
|
76
|
+
zr = ZoneRanger::Core.new('2013-04-01 23:01 -07:00', 60, "Pacific Time (US & Canada)", :repeat => :daily)
|
77
|
+
zr.time_range(Time.parse('2013-04-02 06:00 -07:00')).should eql([Time.parse('2013-04-02 23:01 -07:00'), Time.parse('2013-04-03 00:01 -07:00')])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#expired?" do
|
83
|
+
let(:zr_daily) { ZoneRanger::Core.new('2013-04-01 23:01 -04:00', 60, "Eastern Time (US & Canada)", :repeat => :daily, :ending => '2013-06-01')}
|
84
|
+
let(:zr_weekly) { ZoneRanger::Core.new('2013-04-01 00:01:00 -07:00', 60, "Pacific Time (US & Canada)", :ending => '2013-06-01', :repeat => :weekly)}
|
85
|
+
let(:zr_monthly_by_day_of_month) { ZoneRanger::Core.new('2013-04-01 00:01:00 -07:00', 60, "Pacific Time (US & Canada)", :ending => '2013-06-01', :repeat => :monthly_by_day_of_month)}
|
86
|
+
let(:zr_monthly_by_day_of_week) { ZoneRanger::Core.new('2013-04-01 00:01:00 -07:00', 60, "Pacific Time (US & Canada)", :ending => '2013-06-01', :repeat => :monthly_by_day_of_week)}
|
87
|
+
|
88
|
+
it "should return true if the checked time is past the expired time" do
|
89
|
+
[zr_daily, zr_weekly, zr_monthly_by_day_of_month, zr_monthly_by_day_of_week].each do |zr|
|
90
|
+
zr.expired?(Time.parse('2013-06-02 00:00:00 -07:00')).should(be_true, "#{zr.repeat_type} expected EXPIRED")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should be false if the checked time is before the expired time" do
|
95
|
+
[zr_daily, zr_weekly, zr_monthly_by_day_of_month, zr_monthly_by_day_of_week].each do |zr|
|
96
|
+
zr.expired?(Time.parse('2013-06-01 00:00:00 -07:00')).should(be_false, "#{zr.repeat_type} expected ACTIVE")
|
97
|
+
zr.expired?(Time.parse('2013-05-01 00:00:00 -07:00')).should(be_false, "#{zr.repeat_type} expected ACTIVE")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#includes?" do
|
103
|
+
|
104
|
+
context "expired" do
|
105
|
+
let(:daily_with_end) { ZoneRanger::Core.new("09:58 -04:00", 841, "Eastern Time (US & Canada)", :repeat => :daily, :ending => '2013-07-17') }
|
106
|
+
|
107
|
+
it "should never return true after the end date - not near UTC midnight" do
|
108
|
+
validate_inactive daily_with_end, "Fri Jul 19 11:57:22 -0400 2013"
|
109
|
+
validate_active daily_with_end, "Fri Jul 16 11:57:22 -0400 2013"
|
110
|
+
validate_active daily_with_end, "Fri Jul 17 23:58:22 -0400 2013"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "giving a time" do # does the present time fall in the given timeframe?
|
115
|
+
|
116
|
+
let(:zr) { ZoneRanger::Core.new('2013-04-01 23:01:00 -07:00', 60, "Pacific Time (US & Canada)") }
|
117
|
+
|
118
|
+
context "in timeframe" do # present time is inside timeframe
|
119
|
+
it "should be active" do
|
120
|
+
validate_active(zr, '2013-04-01 23:01:01 -07:00')
|
121
|
+
validate_active(zr, '2013-04-02 00:00:00 -07:00')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
context "outside timeframe" do # present time is outside timeframe
|
125
|
+
it "should not be active" do
|
126
|
+
validate_inactive(zr, '2013-04-01 23:00:01 -07:00')
|
127
|
+
validate_inactive(zr, '2013-04-02 00:02:00 -07:00')
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "over utc midnight" do
|
132
|
+
let(:set_timeframe_over_two_days){ ZoneRanger::Core.new("2013-07-24 23:01 UTC", 120, "UTC", :repeat => :weekly, :ending => Time.parse("2013-07-25 UTC")) }
|
133
|
+
|
134
|
+
it "should be active in timeframe" do
|
135
|
+
validate_active(set_timeframe_over_two_days, "2013-07-24 23:02 UTC")
|
136
|
+
validate_active(set_timeframe_over_two_days, "2013-07-25 01:00 UTC")
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should not be active in timeframe" do
|
140
|
+
validate_inactive(set_timeframe_over_two_days, "2013-07-24 23:00 UTC")
|
141
|
+
validate_inactive(set_timeframe_over_two_days, "2013-07-25 01:02 UTC")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "repeated" do
|
146
|
+
context "daily" do
|
147
|
+
let(:zr_daily) { ZoneRanger::Core.new('2013-04-01 23:01:00 -07:00', 60, "Pacific Time (US & Canada)", :repeat => :daily) }
|
148
|
+
let(:zr_daily_no_start_date) { ZoneRanger::Core.new('23:01:00 -07:00', 60, "Pacific Time (US & Canada)", :repeat => :daily) }
|
149
|
+
|
150
|
+
it "should cover until midnight every day" do
|
151
|
+
(1..8).each do |i|
|
152
|
+
validate_active(zr_daily, "2013-04-0#{i} 23:59:01 -07:00")
|
153
|
+
validate_active(zr_daily_no_start_date, "2013-04-0#{i} 23:59:01 -07:00")
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should cover after midnight every day" do
|
158
|
+
(1..8).each do |i|
|
159
|
+
validate_active(zr_daily, "2013-04-0#{i+1} 00:00:00 -07:00")
|
160
|
+
validate_active(zr_daily_no_start_date, "2013-04-0#{i+1} 00:00:00 -07:00")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should not cover after midnight from the day before if before starting time" do
|
165
|
+
validate_inactive(zr_daily, "2013-03-31 00:00:00 -07:00")
|
166
|
+
validate_active(zr_daily_no_start_date, "2013-03-31 00:00:00 -07:00")
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context "weekly" do
|
171
|
+
let(:zr_weekly) { ZoneRanger::Core.new('2013-04-01 00:01:00 -07:00', 60, "Pacific Time (US & Canada)", :repeat => :weekly) }
|
172
|
+
|
173
|
+
it "should cover only on the day of week, and in time range" do
|
174
|
+
validate_active(zr_weekly, '2013-04-01 00:01:01 -07:00')
|
175
|
+
validate_active(zr_weekly, '2013-04-01 00:59:59 -07:00')
|
176
|
+
|
177
|
+
# next week
|
178
|
+
validate_active(zr_weekly, '2013-04-08 00:01:01 -07:00')
|
179
|
+
validate_active(zr_weekly, '2013-04-08 00:59:59 -07:00')
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should not cover if on the day of week, but not in time range" do
|
183
|
+
validate_inactive(zr_weekly, '2013-04-08 00:00:59 -07:00')
|
184
|
+
validate_inactive(zr_weekly, '2013-04-08 01:02:59 -07:00')
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should not cover if not on the day of week and in time range" do
|
188
|
+
validate_inactive(zr_weekly, '2013-04-07 00:01:01 -07:00')
|
189
|
+
validate_inactive(zr_weekly, '2013-04-07 00:59:59 -07:00')
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context "monthly" do
|
194
|
+
context "day of the month" do
|
195
|
+
|
196
|
+
let(:zr_monthly_dom) { ZoneRanger::Core.new('2013-04-01 00:01:00 -07:00', 60, "Pacific Time (US & Canada)", :repeat => :monthly_by_day_of_month) }
|
197
|
+
let(:zr_monthly_dom2) { ZoneRanger::Core.new("2013-07-17 09:58 -04:00", 120, "Eastern Time (US & Canada)", :repeat => :monthly_by_day_of_month) }
|
198
|
+
|
199
|
+
it "should be active if on the day of the month of start_date and within the timeframe" do
|
200
|
+
validate_active(zr_monthly_dom, '2013-05-01 00:02:00 -07:00')
|
201
|
+
validate_active(zr_monthly_dom, '2013-05-01 00:59:00 -07:00')
|
202
|
+
validate_active(zr_monthly_dom, '2013-06-01 00:02:00 -07:00')
|
203
|
+
validate_active(zr_monthly_dom, '2013-06-01 00:59:00 -07:00')
|
204
|
+
end
|
205
|
+
|
206
|
+
it "should not be active if on the day of the month of start_date and within the timeframe but start_date is in the future" do
|
207
|
+
validate_inactive(zr_monthly_dom, '2013-03-01 00:02:00 -07:00')
|
208
|
+
validate_inactive(zr_monthly_dom, '2013-03-01 00:59:00 -07:00')
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should not be active if within the timeframe but not on the day of the month" do
|
212
|
+
validate_inactive(zr_monthly_dom, '2013-04-03 00:02:00 -07:00')
|
213
|
+
validate_inactive(zr_monthly_dom, '2013-04-03 00:59:00 -07:00')
|
214
|
+
validate_inactive(zr_monthly_dom2, "Thu Jul 18 9:59:22 -0400 2013")
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
context "day of the week" do
|
222
|
+
let(:zr_monthly_dow) { ZoneRanger::Core.new('2013-04-01 00:01:00 -07:00', 60, "Pacific Time (US & Canada)", :repeat => :monthly_by_day_of_week) }
|
223
|
+
|
224
|
+
it "should be active if on the same weekday and xth week and inside timeframe" do
|
225
|
+
validate_active(zr_monthly_dow, "2013-04-01 00:02:00 -07:00")
|
226
|
+
validate_active(zr_monthly_dow, "2013-04-01 01:00:00 -07:00")
|
227
|
+
|
228
|
+
# next period
|
229
|
+
validate_active(zr_monthly_dow, "2013-05-06 00:02:00 -07:00")
|
230
|
+
validate_active(zr_monthly_dow, "2013-05-06 01:00:00 -07:00")
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should not be active if on xth week and inside timeframe but not on same weekday" do
|
234
|
+
validate_inactive(zr_monthly_dow, "2013-05-05 00:02:00 -07:00")
|
235
|
+
validate_inactive(zr_monthly_dow, "2013-05-05 01:00:00 -07:00")
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should not be active if on the same weekday and inside timeframe but not on xth week" do
|
239
|
+
validate_inactive(zr_monthly_dow, "2013-04-08 00:02:00 -07:00")
|
240
|
+
validate_inactive(zr_monthly_dow, "2013-04-08 01:00:00 -07:00")
|
241
|
+
end
|
242
|
+
|
243
|
+
it "should not be active if on the same weekday and xth week but outside timeframe" do
|
244
|
+
validate_inactive(zr_monthly_dow, "2013-04-01 00:00:00 -07:00")
|
245
|
+
validate_inactive(zr_monthly_dow, "2013-04-01 01:02:00 -07:00")
|
246
|
+
|
247
|
+
# next period
|
248
|
+
validate_inactive(zr_monthly_dow, "2013-05-06 00:00:00 -07:00")
|
249
|
+
validate_inactive(zr_monthly_dow, "2013-05-06 01:02:00 -07:00")
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/util_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ZoneRanger::Util do
|
4
|
+
it "should pass" do
|
5
|
+
end
|
6
|
+
|
7
|
+
describe '#crosses_one_utc_midnight?' do
|
8
|
+
# let(:does_cross) { Factory(:check_blackout_period,
|
9
|
+
# :repeat_type => "daily",
|
10
|
+
# :start_time => "19:58", :end_time => "1:38", :start_date => nil)}
|
11
|
+
# let(:does_not_cross) { Factory(:check_blackout_period,
|
12
|
+
# :repeat_type => "daily",
|
13
|
+
# :start_time => "10:58", :end_time => "11:38", :start_date => nil)}
|
14
|
+
# let(:crossing_utc_weekly_blackout) { Factory(:check_blackout_period, :repeat_type => "weekly",
|
15
|
+
# :start_time => "19:58", :end_time => "1:30", :start_date => Date.parse('2013-07-17'))}
|
16
|
+
|
17
|
+
# it "should cross one midnight" do
|
18
|
+
# does_cross.send(:crosses_one_utc_midnight?).should be_true
|
19
|
+
# does_not_cross.send(:crosses_one_utc_midnight?).should be_false
|
20
|
+
# crossing_utc_weekly_blackout.send(:crosses_one_utc_midnight?).should be_true
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
end
|
24
|
+
end
|
data/zone_ranger.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'zone_ranger/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "zone_ranger"
|
8
|
+
spec.version = ZoneRanger::VERSION
|
9
|
+
spec.authors = ["Hubert Liu"]
|
10
|
+
spec.email = ["hubert.liu@rigor.com"]
|
11
|
+
spec.description = "Time Zone Ranger"
|
12
|
+
spec.summary = "Time Zone Ranger"
|
13
|
+
spec.homepage = "https://github.com/Rigor/zone_ranger"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "timecop", "~> 0.3.5"
|
25
|
+
|
26
|
+
spec.add_runtime_dependency "activesupport", "~> 3.0.20"
|
27
|
+
spec.add_runtime_dependency 'i18n' # required by active_support
|
28
|
+
spec.add_runtime_dependency 'tzinfo', '~> 0.3.35'
|
29
|
+
#spec.add_runtime_dependency 'tzinfo-data'
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zone_ranger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Hubert Liu
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-09-16 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: bundler
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 9
|
29
|
+
segments:
|
30
|
+
- 1
|
31
|
+
- 3
|
32
|
+
version: "1.3"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rake
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: rspec
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: timecop
|
65
|
+
prerelease: false
|
66
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ~>
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 25
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
- 3
|
75
|
+
- 5
|
76
|
+
version: 0.3.5
|
77
|
+
type: :development
|
78
|
+
version_requirements: *id004
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: activesupport
|
81
|
+
prerelease: false
|
82
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ~>
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
hash: 47
|
88
|
+
segments:
|
89
|
+
- 3
|
90
|
+
- 0
|
91
|
+
- 20
|
92
|
+
version: 3.0.20
|
93
|
+
type: :runtime
|
94
|
+
version_requirements: *id005
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: i18n
|
97
|
+
prerelease: false
|
98
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
type: :runtime
|
108
|
+
version_requirements: *id006
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: tzinfo
|
111
|
+
prerelease: false
|
112
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 85
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
- 3
|
121
|
+
- 35
|
122
|
+
version: 0.3.35
|
123
|
+
type: :runtime
|
124
|
+
version_requirements: *id007
|
125
|
+
description: Time Zone Ranger
|
126
|
+
email:
|
127
|
+
- hubert.liu@rigor.com
|
128
|
+
executables: []
|
129
|
+
|
130
|
+
extensions: []
|
131
|
+
|
132
|
+
extra_rdoc_files: []
|
133
|
+
|
134
|
+
files:
|
135
|
+
- .gitignore
|
136
|
+
- Gemfile
|
137
|
+
- Gemfile.lock
|
138
|
+
- LICENSE
|
139
|
+
- README.md
|
140
|
+
- Rakefile
|
141
|
+
- lib/zone_ranger.rb
|
142
|
+
- lib/zone_ranger/core.rb
|
143
|
+
- lib/zone_ranger/util.rb
|
144
|
+
- lib/zone_ranger/version.rb
|
145
|
+
- spec/core_spec.rb
|
146
|
+
- spec/spec_helper.rb
|
147
|
+
- spec/util_spec.rb
|
148
|
+
- zone_ranger.gemspec
|
149
|
+
homepage: https://github.com/Rigor/zone_ranger
|
150
|
+
licenses:
|
151
|
+
- MIT
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
|
155
|
+
require_paths:
|
156
|
+
- lib
|
157
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
158
|
+
none: false
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
hash: 3
|
163
|
+
segments:
|
164
|
+
- 0
|
165
|
+
version: "0"
|
166
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
|
+
none: false
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
hash: 3
|
172
|
+
segments:
|
173
|
+
- 0
|
174
|
+
version: "0"
|
175
|
+
requirements: []
|
176
|
+
|
177
|
+
rubyforge_project:
|
178
|
+
rubygems_version: 1.8.15
|
179
|
+
signing_key:
|
180
|
+
specification_version: 3
|
181
|
+
summary: Time Zone Ranger
|
182
|
+
test_files:
|
183
|
+
- spec/core_spec.rb
|
184
|
+
- spec/spec_helper.rb
|
185
|
+
- spec/util_spec.rb
|