tod 1.5.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,41 +1,55 @@
1
1
  module Tod
2
+
3
+ # Shift is a range-like class that handles wrapping around midnight.
4
+ # For example, the Shift of 2300 to 0200 would include 0100.
2
5
  class Shift
3
- attr_reader :beginning, :ending
6
+ attr_reader :beginning, :ending, :range
4
7
 
5
- def initialize(beginning, ending)
8
+ def initialize(beginning, ending, exclude_end=false)
6
9
  raise ArgumentError, "beginning can not be nil" unless beginning
7
10
  raise ArgumentError, "ending can not be nil" unless ending
11
+ unless [true, false].include? exclude_end
12
+ raise ArgumentError, "exclude_end must be true or false"
13
+ end
8
14
 
9
15
  @beginning = beginning
10
16
  @ending = ending
17
+ @exclude_end = exclude_end
18
+
19
+ normalized_ending = ending.to_i
20
+ normalized_ending += TimeOfDay::NUM_SECONDS_IN_DAY if normalized_ending < beginning.to_i
21
+
22
+ @range = Range.new(beginning.to_i, normalized_ending, @exclude_end)
23
+
11
24
  freeze # Shift instances are value objects
12
25
  end
13
26
 
14
- # Returns true if the time of day is inside the shift (inclusive range), false otherwise
27
+ # Returns true if the time of day is inside the shift, false otherwise.
15
28
  def include?(tod)
16
- if ending >= beginning
17
- tod >= beginning && tod <= ending
18
- else
19
- start_of_day = TimeOfDay.new(0,0,0)
20
- end_of_day = TimeOfDay.new(23,59,59)
21
- (tod >= beginning && tod <= end_of_day) || (tod >= start_of_day && tod <= ending)
22
- end
29
+ second = tod.to_i
30
+ second += TimeOfDay::NUM_SECONDS_IN_DAY if second < @range.first
31
+ @range.cover?(second)
32
+ end
33
+
34
+ # Returns true if ranges overlap, false otherwise.
35
+ def overlaps?(other)
36
+ a, b = [self, other].map(&:range).sort_by(&:first)
37
+ op = a.exclude_end? ? :> : :>=
38
+ a.last.send(op, b.first)
39
+ end
40
+
41
+ def contains?(shift)
42
+ self.include?(shift.beginning) && self.include?(shift.ending)
23
43
  end
24
44
 
25
45
  # Return shift duration in seconds.
26
46
  # if ending is lower than beginning this method will calculate the duration as the ending time is from the following day
27
47
  def duration
28
- if ending >= beginning
29
- (ending.to_i - beginning.to_i)
30
- else
31
- start_of_day = TimeOfDay.new(0,0,0)
32
- end_of_day = TimeOfDay.new(23,59,59)
33
- duration_day_1 = (end_of_day.to_i - beginning.to_i) + 1
34
- duration_day_2 = (ending.to_i - start_of_day.to_i)
35
- duration_day_1 + duration_day_2
36
- end
48
+ @range.last - @range.first
49
+ end
50
+
51
+ def exclude_end?
52
+ @exclude_end
37
53
  end
38
54
  end
39
55
  end
40
-
41
- Shift = Tod::Shift
@@ -0,0 +1,9 @@
1
+ module Tod
2
+ module TimeExtensions
3
+ def to_time_of_day
4
+ Tod::TimeOfDay.new hour, min, sec
5
+ end
6
+ end
7
+ end
8
+
9
+ Time.send :include, Tod::TimeExtensions
@@ -1,3 +1,3 @@
1
1
  module Tod
2
- VERSION = "1.5.0"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -3,5 +3,5 @@ ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
3
3
 
4
4
  ActiveRecord::Migration.create_table :orders do |t|
5
5
  t.time :time
6
- t.timestamps
7
- end
6
+ t.timestamps null: false
7
+ end
@@ -1,7 +1,9 @@
1
1
  $LOAD_PATH.unshift File.join(File.dirname(__FILE__),'..','lib')
2
2
 
3
3
  require 'bundler/setup'
4
+ require 'active_support/time'
4
5
  require 'tod'
5
- require 'test/unit'
6
- require 'shoulda'
7
- require 'mocha/setup'
6
+ require 'tod/core_extensions'
7
+ require 'minitest/autorun'
8
+
9
+ Time.zone = "Central Time (US & Canada)"
@@ -1,48 +1,52 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__),'..','test_helper'))
2
- require 'active_support/time'
3
2
 
4
- class TimeOfDayConversionTest < Test::Unit::TestCase
5
- should "handle TimeOfDay" do
6
- t = TimeOfDay.new(13, 56, 12)
7
- tod = TimeOfDay(t)
3
+ describe "TimeOfDay()" do
4
+ it "handles Tod::TimeOfDay" do
5
+ t = Tod::TimeOfDay.new(13, 56, 12)
6
+ tod = Tod::TimeOfDay(t)
8
7
 
9
8
  assert_equal(t, tod)
10
9
  end
11
- should "handle Time" do
10
+
11
+ it "handles Time" do
12
12
  t = Time.new(2014, 2, 27, 12, 01, 02)
13
- tod = TimeOfDay(t)
13
+ tod = Tod::TimeOfDay(t)
14
14
 
15
- assert_equal(tod, TimeOfDay.new(12, 01, 02))
15
+ assert_equal(tod, Tod::TimeOfDay.new(12, 01, 02))
16
16
  end
17
- should "handle Date" do
17
+
18
+ it "handles Date" do
18
19
  t = Date.new(2014, 2, 27)
19
- tod = TimeOfDay(t)
20
+ tod = Tod::TimeOfDay(t)
20
21
 
21
- assert_equal(tod, TimeOfDay.new(0, 0, 0))
22
+ assert_equal(tod, Tod::TimeOfDay.new(0, 0, 0))
22
23
  end
23
- should "handle DateTime" do
24
+
25
+ it "handles DateTime" do
24
26
  t = DateTime.new(2014, 2, 27, 12, 01, 02)
25
- tod = TimeOfDay(t)
27
+ tod = Tod::TimeOfDay(t)
26
28
 
27
- assert_equal(tod, TimeOfDay.new(12, 01, 02))
29
+ assert_equal(tod, Tod::TimeOfDay.new(12, 01, 02))
28
30
  end
29
- should "string" do
31
+
32
+ it "parses string" do
30
33
  t = "12:01:02"
31
- tod = TimeOfDay(t)
34
+ tod = Tod::TimeOfDay(t)
32
35
 
33
- assert_equal(tod, TimeOfDay.new(12, 01, 02))
36
+ assert_equal(tod, Tod::TimeOfDay.new(12, 01, 02))
34
37
  end
35
- should "parse 'noon'" do
38
+
39
+ it "parses 'noon'" do
36
40
  t = "noon"
37
- tod = TimeOfDay(t)
41
+ tod = Tod::TimeOfDay(t)
38
42
 
39
- assert_equal(tod, TimeOfDay.new(12, 00, 00))
43
+ assert_equal(tod, Tod::TimeOfDay.new(12, 00, 00))
40
44
  end
41
- should "parse 'midnight'" do
45
+
46
+ it "parses 'midnight'" do
42
47
  t = "midnight"
43
- tod = TimeOfDay(t)
48
+ tod = Tod::TimeOfDay(t)
44
49
 
45
- assert_equal(tod, TimeOfDay.new(0, 00, 00))
50
+ assert_equal(tod, Tod::TimeOfDay.new(0, 00, 00))
46
51
  end
47
-
48
- end
52
+ end
@@ -1,28 +1,27 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__),'..','test_helper'))
2
- require 'active_support/time'
3
2
 
4
- class DateTest < Test::Unit::TestCase
5
- context "at" do
6
- should "accept TimeOfDay and return Time on same date" do
3
+ describe "Date extensions" do
4
+ describe "#at" do
5
+ it "accepts TimeOfDay and return Time on same date" do
7
6
  date = Date.civil 2000,1,1
8
- tod = TimeOfDay.new 8,30
9
- assert_equal Time.local(2000,1,1, 8,30), date.at(tod)
7
+ tod = Tod::TimeOfDay.new 8,30
8
+ assert_equal Time.zone.local(2000,1,1, 8,30), date.at(tod)
10
9
  end
11
10
 
12
- context "with a time zone" do
13
- should "accept TimeOfDay and TimeWithZone on same date" do
11
+ describe "with a time zone" do
12
+ it "accepts TimeOfDay and TimeWithZone on same date" do
14
13
  date = Date.civil 2000,1,1
15
- tod = TimeOfDay.new 8,30
14
+ tod = Tod::TimeOfDay.new 8,30
16
15
  time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)']
17
16
  assert_equal time_zone.local(2000,1,1, 8,30), date.at(tod, time_zone)
18
17
  end
19
18
  end
20
19
  end
21
20
 
22
- context "to_time_day" do
23
- should "be TimeOfDay" do
21
+ describe "#to_time_day" do
22
+ it "is TimeOfDay" do
24
23
  date_time = DateTime.new 2013, 9, 19, 15, 17, 34
25
- assert_equal TimeOfDay.new(15, 17, 34), date_time.to_time_of_day
24
+ assert_equal Tod::TimeOfDay.new(15, 17, 34), date_time.to_time_of_day
26
25
  end
27
26
  end
28
27
  end
@@ -1,94 +1,169 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__),'..','test_helper'))
2
- require 'active_support/time'
3
2
 
4
- class ShiftTest < Test::Unit::TestCase
5
- context "duration" do
6
- should "return correct duration when first time is lower than the second one" do
3
+ describe "Shift" do
4
+ describe "#initialize" do
5
+ it "parses bounds" do
6
+ shift = Tod::Shift.new Tod::TimeOfDay.new(8), Tod::TimeOfDay.new(10), false
7
+ refute shift.exclude_end?
8
+
9
+ shift = Tod::Shift.new Tod::TimeOfDay.new(8), Tod::TimeOfDay.new(10), true
10
+ assert shift.exclude_end?
11
+ end
12
+ end
13
+
14
+ describe "#duration" do
15
+ it "returns correct duration when first time is lower than the second one" do
7
16
  duration_expected = 4 * 60 * 60 + 30 * 60 + 30 # 4 hours, 30 min and 30 sec later
8
- tod1 = TimeOfDay.new 8,30
9
- tod2 = TimeOfDay.new 13,00,30
10
- shift = Shift.new tod1, tod2
17
+ tod1 = Tod::TimeOfDay.new 8,30
18
+ tod2 = Tod::TimeOfDay.new 13,00,30
19
+ shift = Tod::Shift.new tod1, tod2
11
20
  duration = shift.duration
12
21
  assert_equal duration, duration_expected
13
22
  end
14
- should "return correct duration when first time is greater than the second one" do
23
+
24
+ it "returns correct duration when first time is greater than the second one" do
15
25
  duration_expected = 4 * 60 * 60 + 30 * 60 + 30 # 4 hours, 30 min and 30 sec later
16
- tod1 = TimeOfDay.new 22,30
17
- tod2 = TimeOfDay.new 3,00,30
18
- shift = Shift.new tod1, tod2
26
+ tod1 = Tod::TimeOfDay.new 22,30
27
+ tod2 = Tod::TimeOfDay.new 3,00,30
28
+ shift = Tod::Shift.new tod1, tod2
19
29
  duration = shift.duration
20
30
  assert_equal duration, duration_expected
21
31
  end
22
- should "be zero when both times are equal" do
23
- tod1 = TimeOfDay.new 3,00,30
24
- shift = Shift.new tod1, tod1
32
+
33
+ it "is zero when both times are equal" do
34
+ tod1 = Tod::TimeOfDay.new 3,00,30
35
+ shift = Tod::Shift.new tod1, tod1
25
36
  duration = shift.duration
26
37
  assert_equal duration, 0
27
38
  end
28
39
  end
29
40
 
30
- context "include?" do
41
+ describe "overlaps?" do
42
+ it "is true when shifts overlap" do
43
+ shift1 = Tod::Shift.new(Tod::TimeOfDay.new(12), Tod::TimeOfDay.new(18))
44
+ shift2 = Tod::Shift.new(Tod::TimeOfDay.new(13), Tod::TimeOfDay.new(15))
45
+ assert shift1.overlaps?(shift2)
46
+ end
47
+
48
+ it "is false when shifts don't overlap" do
49
+ shift1 = Tod::Shift.new(Tod::TimeOfDay.new(1), Tod::TimeOfDay.new(5))
50
+ shift2 = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(12))
51
+ refute shift1.overlaps?(shift2)
52
+ end
53
+
54
+ it "is true when shifts touch with inclusive end" do
55
+ shift1 = Tod::Shift.new(Tod::TimeOfDay.new(1), Tod::TimeOfDay.new(5))
56
+ shift2 = Tod::Shift.new(Tod::TimeOfDay.new(5), Tod::TimeOfDay.new(12))
57
+ assert shift1.overlaps?(shift2)
58
+ end
59
+
60
+ it "is false when shifts touch with exclusive end" do
61
+ shift1 = Tod::Shift.new(Tod::TimeOfDay.new(1), Tod::TimeOfDay.new(5), true)
62
+ shift2 = Tod::Shift.new(Tod::TimeOfDay.new(5), Tod::TimeOfDay.new(12), true)
63
+ refute shift1.overlaps?(shift2)
64
+ end
65
+ end
66
+
67
+ describe "contains?" do
68
+ it "is true when one shift contains another" do
69
+ outside = Tod::Shift.new(Tod::TimeOfDay.new(12), Tod::TimeOfDay.new(18))
70
+ inside = Tod::Shift.new(Tod::TimeOfDay.new(13), Tod::TimeOfDay.new(15))
71
+ assert outside.contains?(inside)
72
+ end
73
+
74
+ it "is false when a shift is contained by another" do
75
+ outside = Tod::Shift.new(Tod::TimeOfDay.new(12), Tod::TimeOfDay.new(18))
76
+ inside = Tod::Shift.new(Tod::TimeOfDay.new(13), Tod::TimeOfDay.new(15))
77
+ refute inside.contains?(outside)
78
+ end
79
+
80
+ it "is false when shifts don't even overlap" do
81
+ shift1 = Tod::Shift.new(Tod::TimeOfDay.new(12), Tod::TimeOfDay.new(15))
82
+ shift2 = Tod::Shift.new(Tod::TimeOfDay.new(18), Tod::TimeOfDay.new(19))
83
+ refute shift1.contains?(shift2)
84
+ end
85
+ end
86
+
87
+ describe "#include?" do
31
88
  # |------------------------|--------T1----V----T2----|------------------------|
32
- should "be true when value is between ToDs and boths tods are in the same day" do
33
- tod1 = TimeOfDay.new 8
34
- tod2 = TimeOfDay.new 16
35
- value = TimeOfDay.new 12
36
- shift = Shift.new tod1, tod2
37
- assert_equal shift.include?(value), true
89
+ it "is true when value is between ToDs and boths tods are in the same day" do
90
+ tod1 = Tod::TimeOfDay.new 8
91
+ tod2 = Tod::TimeOfDay.new 16
92
+ value = Tod::TimeOfDay.new 12
93
+ shift = Tod::Shift.new tod1, tod2
94
+ assert shift.include?(value)
38
95
  end
39
96
 
40
97
  # |------------------T1----|-------V----------T2-----|------------------------|
41
- should "be true when value is on second day between ToDs and start ToD is in a different day" do
42
- tod1 = TimeOfDay.new 20
43
- tod2 = TimeOfDay.new 15
44
- value = TimeOfDay.new 12
45
- shift = Shift.new tod1, tod2
46
- assert_equal shift.include?(value), true
98
+ it "is true when value is on second day between ToDs and start ToD is in a different day" do
99
+ tod1 = Tod::TimeOfDay.new 20
100
+ tod2 = Tod::TimeOfDay.new 15
101
+ value = Tod::TimeOfDay.new 12
102
+ shift = Tod::Shift.new tod1, tod2
103
+ assert shift.include?(value)
47
104
  end
48
105
 
49
106
  # |------------------T1--V-|------------------T2-----|------------------------|
50
- should "be true when value is on first day between ToDs and start ToD is in a different day" do
51
- tod1 = TimeOfDay.new 20
52
- tod2 = TimeOfDay.new 15
53
- value = TimeOfDay.new 22
54
- shift = Shift.new tod1, tod2
55
- assert_equal shift.include?(value), true
107
+ it "is true when value is on first day between ToDs and start ToD is in a different day" do
108
+ tod1 = Tod::TimeOfDay.new 20
109
+ tod2 = Tod::TimeOfDay.new 15
110
+ value = Tod::TimeOfDay.new 22
111
+ shift = Tod::Shift.new tod1, tod2
112
+ assert shift.include?(value)
56
113
  end
57
114
 
58
115
  # |------------------------|--------T1----------V----|----T2------------------|
59
- should "be true when value is on first day between ToDs and end ToD is in a different day" do
60
- tod1 = TimeOfDay.new 16
61
- tod2 = TimeOfDay.new 4
62
- value = TimeOfDay.new 20
63
- shift = Shift.new tod1, tod2
64
- assert_equal shift.include?(value), true
116
+ it "is true when value is on first day between ToDs and end ToD is in a different day" do
117
+ tod1 = Tod::TimeOfDay.new 16
118
+ tod2 = Tod::TimeOfDay.new 4
119
+ value = Tod::TimeOfDay.new 20
120
+ shift = Tod::Shift.new tod1, tod2
121
+ assert shift.include?(value)
65
122
  end
66
123
 
67
124
  # |------------------------|--------T1---------------|--V---T2----------------|
68
- should "be true when value is on second day between ToDs and end ToD is in a different day" do
69
- tod1 = TimeOfDay.new 16
70
- tod2 = TimeOfDay.new 4
71
- value = TimeOfDay.new 2
72
- shift = Shift.new tod1, tod2
73
- assert_equal shift.include?(value), true
125
+ it "is true when value is on second day between ToDs and end ToD is in a different day" do
126
+ tod1 = Tod::TimeOfDay.new 16
127
+ tod2 = Tod::TimeOfDay.new 4
128
+ value = Tod::TimeOfDay.new 2
129
+ shift = Tod::Shift.new tod1, tod2
130
+ assert shift.include?(value)
74
131
  end
75
132
 
76
133
  # |------------------------|--------T1-----T2----V---|------------------------|
77
- should "be false when value is after second ToD" do
78
- tod1 = TimeOfDay.new 10
79
- tod2 = TimeOfDay.new 16
80
- value = TimeOfDay.new 20
81
- shift = Shift.new tod1, tod2
82
- assert_equal shift.include?(value), false
134
+ it "is false when value is after second ToD" do
135
+ tod1 = Tod::TimeOfDay.new 10
136
+ tod2 = Tod::TimeOfDay.new 16
137
+ value = Tod::TimeOfDay.new 20
138
+ shift = Tod::Shift.new tod1, tod2
139
+ refute shift.include?(value)
83
140
  end
84
141
 
85
142
  # |------------------------|--V-----T1-----T2--------|------------------------|
86
- should "be false when value is before first ToD" do
87
- tod1 = TimeOfDay.new 10
88
- tod2 = TimeOfDay.new 16
89
- value = TimeOfDay.new 8
90
- shift = Shift.new tod1, tod2
91
- assert_equal shift.include?(value), false
143
+ it "is false when value is before first ToD" do
144
+ tod1 = Tod::TimeOfDay.new 10
145
+ tod2 = Tod::TimeOfDay.new 16
146
+ value = Tod::TimeOfDay.new 8
147
+ shift = Tod::Shift.new tod1, tod2
148
+ refute shift.include?(value)
149
+ end
150
+
151
+ # |------------------------|--------T1-----T2V-------|------------------------|
152
+ it "is false when value is equal to second ToD and Shift is ending exclusive" do
153
+ tod1 = Tod::TimeOfDay.new 10
154
+ tod2 = Tod::TimeOfDay.new 16
155
+ value = Tod::TimeOfDay.new 16
156
+ shift = Tod::Shift.new tod1, tod2, true
157
+ refute shift.include?(value)
158
+ end
159
+
160
+ # |------------------------|--------T1-----T2V-------|------------------------|
161
+ it "is true when value is equal to second ToD and Shift is ending inclusive" do
162
+ tod1 = Tod::TimeOfDay.new 10
163
+ tod2 = Tod::TimeOfDay.new 16
164
+ value = Tod::TimeOfDay.new 16
165
+ shift = Tod::Shift.new tod1, tod2, false
166
+ assert shift.include?(value)
92
167
  end
93
168
  end
94
169
  end