tod 2.0.1 → 3.0.0
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.
- checksums.yaml +5 -5
- data/.gitignore +2 -0
- data/.travis.yml +1 -9
- data/CHANGELOG.md +28 -0
- data/Gemfile.lock +35 -33
- data/README.markdown +50 -8
- data/lib/tod.rb +2 -0
- data/lib/tod/conversions.rb +2 -2
- data/lib/tod/date_extensions.rb +2 -0
- data/lib/tod/railtie.rb +12 -0
- data/lib/tod/shift.rb +51 -3
- data/lib/tod/time_of_day.rb +57 -25
- data/lib/tod/time_of_day_type.rb +18 -0
- data/lib/tod/version.rb +1 -1
- data/test/tod/conversion_test.rb +8 -1
- data/test/tod/date_test.rb +1 -1
- data/test/tod/shift_test.rb +230 -1
- data/test/tod/time_of_day_attribute_test.rb +74 -0
- data/test/tod/time_of_day_test.rb +99 -12
- data/test/tod/time_of_day_time_zone_with_active_support_test.rb +1 -1
- data/test/tod/time_test.rb +1 -1
- data/tod.gemspec +2 -2
- metadata +13 -16
- data/gemfiles/3.2.gemfile +0 -5
- data/gemfiles/4.0.gemfile +0 -5
- data/gemfiles/4.1.gemfile +0 -5
- data/gemfiles/4.2.gemfile +0 -5
- data/test/tod/time_of_day_serializable_attribute_test.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8986b140f4dcc736d253b188cd5f9a46a72110f38887cd6e8ff93ecee823c438
|
4
|
+
data.tar.gz: 95d31c83abc1264946bf2ce6aa6ba6fac45dcaeca1362b76a22d54eab27f402f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d1c3b50deb1df502928666b38e113fb7d58b4a25c140119bac8155ad3ff3add0689cffba810b7bdd6ab1461493838873a839ac259895630210e47679ae9ef89
|
7
|
+
data.tar.gz: 98a76ddcb2b8c866f602efe1def27c236b88ffcc6bbb12812580794113dc964d5b6df1bd95b0f055a48e9e95ca4b69ccf3dea93b20f866ca72be7932596e1aaf
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
# 3.0.0 (March 6, 2021)
|
2
|
+
|
3
|
+
* Support and require Rails 6
|
4
|
+
|
5
|
+
# 2.2.0 (October 10, 2018)
|
6
|
+
|
7
|
+
* Add string formatting compatible with Rails (Tate Johnson)
|
8
|
+
* Add ability to use ActiveRecord's attribute API (Brent Wheeldon)
|
9
|
+
* Add method for use in Rails database quoting (Ben Jackson)
|
10
|
+
|
11
|
+
# 2.1.1 (April 14, 2017)
|
12
|
+
|
13
|
+
* Fix serialize Ruby Time to Tod::TimeOfDay (Ryan Dick)
|
14
|
+
* Fix TimeOfDay.from_second_of_day when passed float (Jack Christensen)
|
15
|
+
* Fix Rails 5 multi-param assignment (Miklos Fazekas)
|
16
|
+
|
17
|
+
# 2.1.0 (May 9, 2016)
|
18
|
+
|
19
|
+
* Fix date extensions requiring date (ambirdsall)
|
20
|
+
* Add subtraction to TimeOfDay (Hiroki Shirai)
|
21
|
+
* Add equality comparison for shifts (Greg Beech)
|
22
|
+
* Add arel_extensions for TimeOfDay (Paul Tyng)
|
23
|
+
* Support for shifts that span to other days (kennyeni)
|
24
|
+
|
25
|
+
# 2.0.2 (May 21, 2015)
|
26
|
+
|
27
|
+
* Fix ActiveRecord serialization when core extensions not loaded
|
28
|
+
|
1
29
|
# 2.0.1 (May 8, 2015)
|
2
30
|
|
3
31
|
* Fix Tod::TimeOfDay() without core extensions
|
data/Gemfile.lock
CHANGED
@@ -1,50 +1,52 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
tod (
|
4
|
+
tod (3.0.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
activemodel (
|
10
|
-
activesupport (=
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
rake (
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
thread_safe (~> 0.1)
|
9
|
+
activemodel (6.1.3)
|
10
|
+
activesupport (= 6.1.3)
|
11
|
+
activerecord (6.1.3)
|
12
|
+
activemodel (= 6.1.3)
|
13
|
+
activesupport (= 6.1.3)
|
14
|
+
activesupport (6.1.3)
|
15
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
16
|
+
i18n (>= 1.6, < 2)
|
17
|
+
minitest (>= 5.1)
|
18
|
+
tzinfo (~> 2.0)
|
19
|
+
zeitwerk (~> 2.3)
|
20
|
+
byebug (11.1.3)
|
21
|
+
coderay (1.1.3)
|
22
|
+
concurrent-ruby (1.1.8)
|
23
|
+
i18n (1.8.9)
|
24
|
+
concurrent-ruby (~> 1.0)
|
25
|
+
method_source (1.0.0)
|
26
|
+
minitest (5.14.4)
|
27
|
+
pry (0.13.1)
|
28
|
+
coderay (~> 1.1)
|
29
|
+
method_source (~> 1.0)
|
30
|
+
pry-byebug (3.9.0)
|
31
|
+
byebug (~> 11.0)
|
32
|
+
pry (~> 0.13.0)
|
33
|
+
rake (13.0.3)
|
34
|
+
sqlite3 (1.4.2)
|
35
|
+
tzinfo (2.0.4)
|
36
|
+
concurrent-ruby (~> 1.0)
|
37
|
+
zeitwerk (2.4.2)
|
39
38
|
|
40
39
|
PLATFORMS
|
41
40
|
ruby
|
42
41
|
|
43
42
|
DEPENDENCIES
|
44
|
-
activerecord (>=
|
43
|
+
activerecord (>= 6.0.0)
|
45
44
|
minitest
|
46
|
-
pry
|
45
|
+
pry-byebug
|
47
46
|
rake
|
48
47
|
sqlite3
|
49
48
|
tod!
|
50
49
|
tzinfo
|
50
|
+
|
51
|
+
BUNDLED WITH
|
52
|
+
2.0.2
|
data/README.markdown
CHANGED
@@ -54,6 +54,12 @@ parsable.
|
|
54
54
|
Tod::TimeOfDay.try_parse "3:30pm" # => 15:30:00
|
55
55
|
Tod::TimeOfDay.try_parse "foo" # => nil
|
56
56
|
|
57
|
+
You can also give a block to parse to handle special input with your own logic.
|
58
|
+
|
59
|
+
Tod::TimeOfDay.parse "25" do |time_string|
|
60
|
+
Tod::TimeOfDay.new(time_string.to_i % 24)
|
61
|
+
end # => 01:00:00
|
62
|
+
|
57
63
|
Values can be tested with Tod::TimeOfDay.parsable? to see if they can be parsed.
|
58
64
|
|
59
65
|
Tod::TimeOfDay.parsable? "3:30pm" # => true
|
@@ -62,7 +68,7 @@ Values can be tested with Tod::TimeOfDay.parsable? to see if they can be parsed.
|
|
62
68
|
Adding or subtracting time
|
63
69
|
-----------------------------
|
64
70
|
|
65
|
-
Seconds can be added to or subtracted Tod::TimeOfDay objects. Time correctly wraps
|
71
|
+
Seconds can be added to or subtracted from Tod::TimeOfDay objects. Time correctly wraps
|
66
72
|
around midnight.
|
67
73
|
|
68
74
|
Tod::TimeOfDay.new(8) + 3600 # => 09:00:00
|
@@ -89,10 +95,33 @@ Format strings are passed to Time#strftime.
|
|
89
95
|
Tod::TimeOfDay.new(17,15).strftime("%I:%M %p") # => "05:15 PM"
|
90
96
|
Tod::TimeOfDay.new(22,5,15).strftime("%I:%M:%S %p") # => "10:05:15 PM"
|
91
97
|
|
98
|
+
Or a Rails style `to_formatted_s` is aliased to `to_s`.
|
99
|
+
|
100
|
+
Tod::TimeOfDay.new(8,30).to_s(:short) # => "8:30 am"
|
101
|
+
|
102
|
+
Or [i18n](https://github.com/svenfuchs/i18n) in a Rails ERB view.
|
103
|
+
|
104
|
+
<%= l Tod::TimeOfDay.new(8, 30), format: :short %>
|
105
|
+
|
106
|
+
Add new formatters to `Tod::TimeOfDay::FORMATS`.
|
107
|
+
|
108
|
+
Tod::TimeOfDay::FORMATS[:seconds_only] = "%S"
|
109
|
+
Tod::TimeOfDay.new(8,30,57).to_s(:seconds_only) # => "57"
|
110
|
+
|
111
|
+
Rounding
|
112
|
+
----------
|
113
|
+
|
114
|
+
Round to the given nearest number of seconds.
|
115
|
+
|
116
|
+
Tod::TimeOfDay.new(8,15,31).round(5) # => "08:15:30"
|
117
|
+
Tod::TimeOfDay.new(8,15,34).round(60) # => "08:16:00"
|
118
|
+
Tod::TimeOfDay.new(8,02,29).round(300) # => "08:00:00"
|
119
|
+
|
92
120
|
Convenience methods for dates and times
|
93
121
|
---------------------------------------
|
94
122
|
|
95
|
-
Pass a date to Tod::TimeOfDay#on and it will return a time with that date and time
|
123
|
+
Pass a date to Tod::TimeOfDay#on and it will return a time with that date and time,
|
124
|
+
in the time zone of the ruby runtime (`Time.now.zone`).
|
96
125
|
|
97
126
|
tod = Tod::TimeOfDay.new 8, 30 # => 08:30:00
|
98
127
|
tod.on Date.today # => 2010-12-29 08:30:00 -0600
|
@@ -184,16 +213,29 @@ Contains?
|
|
184
213
|
Rails Time Zone Support
|
185
214
|
=======================
|
186
215
|
|
187
|
-
If Rails time zone support is loaded, Date#on and Tod::TimeOfDay#at will automatically use Time.zone.
|
216
|
+
If Rails time zone support is loaded, Date#on and Tod::TimeOfDay#at (when given a Date) will automatically use Time.zone.
|
188
217
|
|
189
|
-
|
218
|
+
When Tod::TimeOfDay#on is given a `Time` or `Time`-like object like `ActiveSupport::TimeWithZone`,
|
219
|
+
Tod will ignore the specified timezone and return the time on that date in UTC. In order to
|
220
|
+
produce an object with the correct time and time zone, pass in an
|
221
|
+
`ActiveSupport::TimeZone` object. Date#at has analogous behavior.
|
222
|
+
|
223
|
+
time = Time.now.in_time_zone("US/Eastern") # => Mon, 24 Sep 2018 05:07:23 EDT -04:00
|
224
|
+
tod.on time # => Mon, 24 Sep 2018 08:30:00 UTC +00:00
|
225
|
+
tod.on time, time.time_zone # => Mon, 24 Sep 2018 08:30:00 EDT -04:00
|
226
|
+
tod.on time, Time.find_zone!("US/Mountain") # => Mon, 24 Sep 2018 08:30:00 MDT -06:00
|
227
|
+
Date.tomorrow.at tod, Time.find_zone!("US/Mountain") # => Tue, 25 Sep 2018 08:30:00 MDT -06:00
|
228
|
+
|
229
|
+
ActiveRecord Attribute Support
|
190
230
|
=======================
|
191
|
-
Tod::TimeOfDay
|
231
|
+
Tod::TimeOfDay can be used as an ActiveRecord attribute to store Tod::TimeOfDay directly
|
192
232
|
in a column of the time type.
|
233
|
+
|
193
234
|
Example:
|
235
|
+
|
194
236
|
```ruby
|
195
237
|
class Order < ActiveRecord::Base
|
196
|
-
|
238
|
+
attribute :time, :time_only
|
197
239
|
end
|
198
240
|
order = Order.create(time: Tod::TimeOfDay.new(9,30))
|
199
241
|
order.time # => 09:30:00
|
@@ -241,10 +283,10 @@ Compatibility
|
|
241
283
|
|
242
284
|
[](https://travis-ci.org/jackc/tod)
|
243
285
|
|
244
|
-
Tod is
|
286
|
+
Tod is tested against Ruby 2.6.x and Rails 6.x.
|
245
287
|
|
246
288
|
|
247
289
|
License
|
248
290
|
=======
|
249
291
|
|
250
|
-
Copyright (c) 2010-
|
292
|
+
Copyright (c) 2010-2021 Jack Christensen, released under the MIT license
|
data/lib/tod.rb
CHANGED
data/lib/tod/conversions.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Tod
|
2
|
-
def TimeOfDay(obj_or_string)
|
2
|
+
def TimeOfDay(obj_or_string, &block)
|
3
3
|
if obj_or_string.is_a?(TimeOfDay)
|
4
4
|
obj_or_string
|
5
5
|
elsif obj_or_string.respond_to?(:to_time_of_day)
|
@@ -9,7 +9,7 @@ module Tod
|
|
9
9
|
elsif obj_or_string.is_a?(Date)
|
10
10
|
TimeOfDay.new 0
|
11
11
|
else
|
12
|
-
TimeOfDay.parse(obj_or_string)
|
12
|
+
TimeOfDay.parse(obj_or_string, &block)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
data/lib/tod/date_extensions.rb
CHANGED
data/lib/tod/railtie.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "active_model/type"
|
2
|
+
require "active_record/type"
|
3
|
+
require "tod/time_of_day_type"
|
4
|
+
|
5
|
+
module Tod
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
initializer "tod.register_active_model_type" do
|
8
|
+
ActiveModel::Type.register(:time_only, Tod::TimeOfDayType)
|
9
|
+
ActiveRecord::Type.register(:time_only, Tod::TimeOfDayType)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/tod/shift.rb
CHANGED
@@ -33,9 +33,40 @@ module Tod
|
|
33
33
|
|
34
34
|
# Returns true if ranges overlap, false otherwise.
|
35
35
|
def overlaps?(other)
|
36
|
-
a, b = [self, other].map(&:range)
|
37
|
-
|
38
|
-
a
|
36
|
+
a, b = [self, other].map(&:range)
|
37
|
+
#
|
38
|
+
# Although a Shift which passes through midnight is stored
|
39
|
+
# internally as lasting more than TimeOfDay::NUM_SECONDS_IN_DAY
|
40
|
+
# seconds from midnight, that's not how it is meant to be
|
41
|
+
# handled. Rather, it consists of two chunks:
|
42
|
+
#
|
43
|
+
# range.first => Midnight
|
44
|
+
# Midnight => range.last
|
45
|
+
#
|
46
|
+
# The second one is *before* the first. None of it is more than
|
47
|
+
# TimeOfDay::NUM_SECONDS_IN_DAY after midnight. We thus need to shift
|
48
|
+
# each of our ranges to cover all overlapping possibilities.
|
49
|
+
#
|
50
|
+
one_day = TimeOfDay::NUM_SECONDS_IN_DAY
|
51
|
+
ashifted =
|
52
|
+
Range.new(a.first + one_day, a.last + one_day, a.exclude_end?)
|
53
|
+
bshifted =
|
54
|
+
Range.new(b.first + one_day, b.last + one_day, b.exclude_end?)
|
55
|
+
#
|
56
|
+
# For exclusive ranges we need:
|
57
|
+
#
|
58
|
+
# a.ending > b.beginning && b.ending > a.beginning
|
59
|
+
#
|
60
|
+
# and for inclusive we need:
|
61
|
+
#
|
62
|
+
# a.ending >= b.beginning && b.ending >= a.beginning
|
63
|
+
#
|
64
|
+
aop = a.exclude_end? ? :> : :>=
|
65
|
+
bop = b.exclude_end? ? :> : :>=
|
66
|
+
#
|
67
|
+
(a.last.send(aop, b.first) && b.last.send(bop, a.first)) ||
|
68
|
+
(ashifted.last.send(aop, b.first) && b.last.send(bop, ashifted.first)) ||
|
69
|
+
(a.last.send(aop, bshifted.first) && bshifted.last.send(bop, a.first))
|
39
70
|
end
|
40
71
|
|
41
72
|
def contains?(shift)
|
@@ -51,5 +82,22 @@ module Tod
|
|
51
82
|
def exclude_end?
|
52
83
|
@exclude_end
|
53
84
|
end
|
85
|
+
|
86
|
+
def ==(other)
|
87
|
+
@range == other.range
|
88
|
+
end
|
89
|
+
|
90
|
+
def eql?(other)
|
91
|
+
@range.eql?(other.range)
|
92
|
+
end
|
93
|
+
|
94
|
+
def hash
|
95
|
+
@range.hash
|
96
|
+
end
|
97
|
+
|
98
|
+
# Move start and end by a number of seconds and return new shift.
|
99
|
+
def slide(seconds)
|
100
|
+
self.class.new(beginning + seconds, ending + seconds, exclude_end?)
|
101
|
+
end
|
54
102
|
end
|
55
103
|
end
|
data/lib/tod/time_of_day.rb
CHANGED
@@ -9,7 +9,7 @@ module Tod
|
|
9
9
|
|
10
10
|
PARSE_24H_REGEX = /
|
11
11
|
\A
|
12
|
-
([01]?\d|2[0-
|
12
|
+
([01]?\d|2[0-4])
|
13
13
|
:?
|
14
14
|
([0-5]\d)?
|
15
15
|
:?
|
@@ -34,20 +34,29 @@ module Tod
|
|
34
34
|
/x
|
35
35
|
|
36
36
|
WORDS = {
|
37
|
-
"noon" => "12pm",
|
38
|
-
"midnight" => "12am"
|
37
|
+
"noon" => "12pm".freeze,
|
38
|
+
"midnight" => "12am".freeze
|
39
39
|
}
|
40
40
|
|
41
41
|
NUM_SECONDS_IN_DAY = 86400
|
42
42
|
NUM_SECONDS_IN_HOUR = 3600
|
43
43
|
NUM_SECONDS_IN_MINUTE = 60
|
44
44
|
|
45
|
+
FORMATS = {
|
46
|
+
short: "%-l:%M %P".freeze,
|
47
|
+
medium: "%-l:%M:%S %P".freeze,
|
48
|
+
time: "%H:%M".freeze
|
49
|
+
}
|
50
|
+
|
45
51
|
def initialize(h, m=0, s=0)
|
46
52
|
@hour = Integer(h)
|
47
53
|
@minute = Integer(m)
|
48
54
|
@second = Integer(s)
|
49
55
|
|
50
|
-
raise ArgumentError, "hour must be between 0 and
|
56
|
+
raise ArgumentError, "hour must be between 0 and 24" unless (0..24).include?(@hour)
|
57
|
+
if @hour == 24 && (@minute != 0 || @second != 0)
|
58
|
+
raise ArgumentError, "hour can only be 24 when minute and second are 0"
|
59
|
+
end
|
51
60
|
raise ArgumentError, "minute must be between 0 and 59" unless (0..59).include?(@minute)
|
52
61
|
raise ArgumentError, "second must be between 0 and 59" unless (0..59).include?(@second)
|
53
62
|
|
@@ -61,13 +70,44 @@ module Tod
|
|
61
70
|
@second_of_day <=> other.second_of_day
|
62
71
|
end
|
63
72
|
|
73
|
+
# Rounding to the given nearest number of seconds
|
74
|
+
def round(round_sec = 1)
|
75
|
+
down = self - (self.to_i % round_sec)
|
76
|
+
up = down + round_sec
|
77
|
+
|
78
|
+
difference_down = self - down
|
79
|
+
difference_up = up - self
|
80
|
+
|
81
|
+
if (difference_down < difference_up)
|
82
|
+
return down
|
83
|
+
else
|
84
|
+
return up
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
64
88
|
# Formats identically to Time#strftime
|
65
89
|
def strftime(format_string)
|
90
|
+
# Special case 2400 because strftime will load TimeOfDay into Time which
|
91
|
+
# will convert 24 to 0
|
92
|
+
format_string = format_string.gsub(/%H|%k/, '24') if @hour == 24
|
66
93
|
Time.local(2000,1,1, @hour, @minute, @second).strftime(format_string)
|
67
94
|
end
|
68
95
|
|
69
|
-
def
|
70
|
-
|
96
|
+
def to_formatted_s(format = :default)
|
97
|
+
if formatter = FORMATS[format]
|
98
|
+
if formatter.respond_to?(:call)
|
99
|
+
formatter.call(self).to_s
|
100
|
+
else
|
101
|
+
strftime(formatter)
|
102
|
+
end
|
103
|
+
else
|
104
|
+
strftime "%H:%M:%S"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
alias_method :to_s, :to_formatted_s
|
108
|
+
|
109
|
+
def value_for_database
|
110
|
+
to_s
|
71
111
|
end
|
72
112
|
|
73
113
|
# Return a new TimeOfDay num_seconds greater than self. It will wrap around
|
@@ -78,8 +118,12 @@ module Tod
|
|
78
118
|
|
79
119
|
# Return a new TimeOfDay num_seconds less than self. It will wrap around
|
80
120
|
# at midnight.
|
81
|
-
def -(
|
82
|
-
TimeOfDay
|
121
|
+
def -(other)
|
122
|
+
if other.instance_of?(TimeOfDay)
|
123
|
+
TimeOfDay.from_second_of_day @second_of_day - other.second_of_day
|
124
|
+
else
|
125
|
+
TimeOfDay.from_second_of_day @second_of_day - other
|
126
|
+
end
|
83
127
|
end
|
84
128
|
|
85
129
|
# Returns a Time instance on date using self as the time of day
|
@@ -92,6 +136,8 @@ module Tod
|
|
92
136
|
#
|
93
137
|
# TimeOfDay.from_second_of_day(3600) == TimeOfDay.new(1) # => true
|
94
138
|
def self.from_second_of_day(second_of_day)
|
139
|
+
second_of_day = Integer(second_of_day)
|
140
|
+
return new 24 if second_of_day == NUM_SECONDS_IN_DAY
|
95
141
|
remaining_seconds = second_of_day % NUM_SECONDS_IN_DAY
|
96
142
|
hour = remaining_seconds / NUM_SECONDS_IN_HOUR
|
97
143
|
remaining_seconds -= hour * NUM_SECONDS_IN_HOUR
|
@@ -118,8 +164,10 @@ module Tod
|
|
118
164
|
# TimeOfDay.parse "3:25:58" # => 03:25:58
|
119
165
|
# TimeOfDay.parse "515p" # => 17:15:00
|
120
166
|
# TimeOfDay.parse "151253" # => 15:12:53
|
167
|
+
# You can give a block, that is called with the input if the string is not parsable.
|
168
|
+
# If no block is given an ArgumentError is raised if try_parse returns nil.
|
121
169
|
def self.parse(tod_string)
|
122
|
-
try_parse(tod_string) || (raise
|
170
|
+
try_parse(tod_string) || (block_given? ? yield(tod_string) : raise(ArgumentError, "Invalid time of day string"))
|
123
171
|
end
|
124
172
|
|
125
173
|
# Same as parse(), but return nil if not parsable (instead of raising an error)
|
@@ -156,21 +204,5 @@ module Tod
|
|
156
204
|
def self.time_zone
|
157
205
|
(Time.respond_to?(:zone) && Time.zone) || Time
|
158
206
|
end
|
159
|
-
|
160
|
-
def self.dump(time_of_day)
|
161
|
-
if time_of_day.to_s == ''
|
162
|
-
nil
|
163
|
-
else
|
164
|
-
time_of_day.to_s
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def self.load(time)
|
169
|
-
if time.respond_to?(:to_time_of_day)
|
170
|
-
time.to_time_of_day
|
171
|
-
else
|
172
|
-
TimeOfDay.parse(time) if time && !time.empty?
|
173
|
-
end
|
174
|
-
end
|
175
207
|
end
|
176
208
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Tod
|
2
|
+
class TimeOfDayType < ActiveModel::Type::Value
|
3
|
+
def cast(value)
|
4
|
+
if value.is_a? Hash
|
5
|
+
# rails multiparam attribute
|
6
|
+
# get hour, minute and second and construct new TimeOfDay object
|
7
|
+
::Tod::TimeOfDay.new(value[4], value[5], value[6])
|
8
|
+
else
|
9
|
+
# return nil, if input is not parsable
|
10
|
+
Tod::TimeOfDay(value){}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def serialize(value)
|
15
|
+
value.to_s if value.present?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/tod/version.rb
CHANGED
data/test/tod/conversion_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative '../test_helper'
|
2
2
|
|
3
3
|
describe "TimeOfDay()" do
|
4
4
|
it "handles Tod::TimeOfDay" do
|
@@ -81,4 +81,11 @@ describe "TimeOfDay()" do
|
|
81
81
|
|
82
82
|
assert_equal(tod, Tod::TimeOfDay.new(0, 00, 00))
|
83
83
|
end
|
84
|
+
|
85
|
+
it "parses 24:00:00" do
|
86
|
+
t = "24:00:00"
|
87
|
+
tod = Tod::TimeOfDay(t)
|
88
|
+
|
89
|
+
assert_equal(tod, Tod::TimeOfDay.new(24, 00, 00))
|
90
|
+
end
|
84
91
|
end
|
data/test/tod/date_test.rb
CHANGED
data/test/tod/shift_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative '../test_helper'
|
2
2
|
|
3
3
|
describe "Shift" do
|
4
4
|
describe "#initialize" do
|
@@ -43,12 +43,49 @@ describe "Shift" do
|
|
43
43
|
shift1 = Tod::Shift.new(Tod::TimeOfDay.new(12), Tod::TimeOfDay.new(18))
|
44
44
|
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(13), Tod::TimeOfDay.new(15))
|
45
45
|
assert shift1.overlaps?(shift2)
|
46
|
+
|
47
|
+
# Additional Testing for Shifts that span from one day to another
|
48
|
+
cases = [
|
49
|
+
[5, 8, 7, 2],
|
50
|
+
[7, 2, 1, 8],
|
51
|
+
[7, 2, 5, 8],
|
52
|
+
[4, 8, 1, 5],
|
53
|
+
[1, 5, 4, 8],
|
54
|
+
[7, 2, 1, 4],
|
55
|
+
[1, 4, 7, 2],
|
56
|
+
[1, 4, 3, 2],
|
57
|
+
[5, 8, 7, 2],
|
58
|
+
[7, 2, 8, 3],
|
59
|
+
[7, 2, 6, 3],
|
60
|
+
[7, 2, 1, 8]
|
61
|
+
]
|
62
|
+
|
63
|
+
cases.each do |c|
|
64
|
+
shift1 = Tod::Shift.new(Tod::TimeOfDay.new(c[0]), Tod::TimeOfDay.new(c[1]))
|
65
|
+
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(c[2]), Tod::TimeOfDay.new(c[3]))
|
66
|
+
assert shift1.overlaps?(shift2), "Failed with args: #{c}"
|
67
|
+
end
|
46
68
|
end
|
47
69
|
|
48
70
|
it "is false when shifts don't overlap" do
|
49
71
|
shift1 = Tod::Shift.new(Tod::TimeOfDay.new(1), Tod::TimeOfDay.new(5))
|
50
72
|
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(12))
|
51
73
|
refute shift1.overlaps?(shift2)
|
74
|
+
|
75
|
+
# Additional Testing for Shifts that span from one day to another
|
76
|
+
cases = [
|
77
|
+
[7, 8, 1, 5],
|
78
|
+
[1, 5, 7, 8],
|
79
|
+
[7, 2, 3, 4],
|
80
|
+
[3, 4, 5, 2],
|
81
|
+
[1, 5, 9, 12]
|
82
|
+
]
|
83
|
+
|
84
|
+
cases.each do |c|
|
85
|
+
shift1 = Tod::Shift.new(Tod::TimeOfDay.new(c[0]), Tod::TimeOfDay.new(c[1]))
|
86
|
+
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(c[2]), Tod::TimeOfDay.new(c[3]))
|
87
|
+
refute shift1.overlaps?(shift2), "Failed with args: #{c}"
|
88
|
+
end
|
52
89
|
end
|
53
90
|
|
54
91
|
it "is true when shifts touch with inclusive end" do
|
@@ -62,6 +99,49 @@ describe "Shift" do
|
|
62
99
|
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(5), Tod::TimeOfDay.new(12), true)
|
63
100
|
refute shift1.overlaps?(shift2)
|
64
101
|
end
|
102
|
+
|
103
|
+
it "copes correctly with mixed shifts" do
|
104
|
+
shift1 = Tod::Shift.new(Tod::TimeOfDay.new(1), Tod::TimeOfDay.new(5))
|
105
|
+
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(5), Tod::TimeOfDay.new(12), true)
|
106
|
+
assert shift1.overlaps?(shift2)
|
107
|
+
assert shift2.overlaps?(shift1)
|
108
|
+
shift1 = Tod::Shift.new(Tod::TimeOfDay.new(1), Tod::TimeOfDay.new(5), true)
|
109
|
+
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(5), Tod::TimeOfDay.new(12))
|
110
|
+
refute shift1.overlaps?(shift2)
|
111
|
+
refute shift2.overlaps?(shift1)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "copes correctly with zero length inclusive end shifts" do
|
115
|
+
shift1 = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17))
|
116
|
+
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(10), Tod::TimeOfDay.new(10))
|
117
|
+
shift3 = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(9))
|
118
|
+
shift4 = Tod::Shift.new(Tod::TimeOfDay.new(17), Tod::TimeOfDay.new(17))
|
119
|
+
assert shift1.overlaps?(shift2)
|
120
|
+
assert shift2.overlaps?(shift1)
|
121
|
+
|
122
|
+
assert shift1.overlaps?(shift3)
|
123
|
+
assert shift3.overlaps?(shift1)
|
124
|
+
|
125
|
+
assert shift1.overlaps?(shift4)
|
126
|
+
assert shift4.overlaps?(shift1)
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
it "copes correctly with zero length exclusive end shifts" do
|
131
|
+
shift1 = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(17), true)
|
132
|
+
shift2 = Tod::Shift.new(Tod::TimeOfDay.new(10), Tod::TimeOfDay.new(10), true)
|
133
|
+
shift3 = Tod::Shift.new(Tod::TimeOfDay.new(9), Tod::TimeOfDay.new(9), true)
|
134
|
+
shift4 = Tod::Shift.new(Tod::TimeOfDay.new(17), Tod::TimeOfDay.new(17), true)
|
135
|
+
assert shift1.overlaps?(shift2)
|
136
|
+
assert shift2.overlaps?(shift1)
|
137
|
+
|
138
|
+
refute shift1.overlaps?(shift3)
|
139
|
+
refute shift3.overlaps?(shift1)
|
140
|
+
|
141
|
+
refute shift1.overlaps?(shift4)
|
142
|
+
refute shift4.overlaps?(shift1)
|
143
|
+
|
144
|
+
end
|
65
145
|
end
|
66
146
|
|
67
147
|
describe "contains?" do
|
@@ -166,4 +246,153 @@ describe "Shift" do
|
|
166
246
|
assert shift.include?(value)
|
167
247
|
end
|
168
248
|
end
|
249
|
+
|
250
|
+
describe "#==" do
|
251
|
+
it "is true when the beginning time, end time, and exclude end are the same" do
|
252
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
253
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
254
|
+
shift1 = Tod::Shift.new tod1, tod2
|
255
|
+
shift2 = Tod::Shift.new tod1, tod2
|
256
|
+
assert shift1 == shift2
|
257
|
+
end
|
258
|
+
|
259
|
+
it "is false when the beginning time is different" do
|
260
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
261
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
262
|
+
shift1 = Tod::Shift.new tod1, tod2
|
263
|
+
shift2 = Tod::Shift.new tod1, Tod::TimeOfDay.new(14,00)
|
264
|
+
assert !(shift1 == shift2)
|
265
|
+
end
|
266
|
+
|
267
|
+
it "is false when the ending time is different" do
|
268
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
269
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
270
|
+
shift1 = Tod::Shift.new tod1, tod2
|
271
|
+
shift2 = Tod::Shift.new Tod::TimeOfDay.new(9,30), tod2
|
272
|
+
assert !(shift1 == shift2)
|
273
|
+
end
|
274
|
+
|
275
|
+
it "is false when exclude end is different" do
|
276
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
277
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
278
|
+
shift1 = Tod::Shift.new tod1, tod2
|
279
|
+
shift2 = Tod::Shift.new tod1, tod2, true
|
280
|
+
assert !(shift1 == shift2)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe "#eql?" do
|
285
|
+
it "is true when the beginning time, end time, and exclude end are the same" do
|
286
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
287
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
288
|
+
shift1 = Tod::Shift.new tod1, tod2
|
289
|
+
shift2 = Tod::Shift.new tod1, tod2
|
290
|
+
assert shift1.eql?(shift2)
|
291
|
+
end
|
292
|
+
|
293
|
+
it "is false when the beginning time is different" do
|
294
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
295
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
296
|
+
shift1 = Tod::Shift.new tod1, tod2
|
297
|
+
shift2 = Tod::Shift.new tod1, Tod::TimeOfDay.new(14,00)
|
298
|
+
assert !shift1.eql?(shift2)
|
299
|
+
end
|
300
|
+
|
301
|
+
it "is false when the ending time is different" do
|
302
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
303
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
304
|
+
shift1 = Tod::Shift.new tod1, tod2
|
305
|
+
shift2 = Tod::Shift.new Tod::TimeOfDay.new(9,30), tod2
|
306
|
+
assert !shift1.eql?(shift2)
|
307
|
+
end
|
308
|
+
|
309
|
+
it "is false when exclude end is different" do
|
310
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
311
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
312
|
+
shift1 = Tod::Shift.new tod1, tod2
|
313
|
+
shift2 = Tod::Shift.new tod1, tod2, true
|
314
|
+
assert !shift1.eql?(shift2)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
describe "#hash" do
|
319
|
+
it "is the same when the beginning time, end time, and exclude end are the same" do
|
320
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
321
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
322
|
+
shift1 = Tod::Shift.new tod1, tod2
|
323
|
+
shift2 = Tod::Shift.new tod1, tod2
|
324
|
+
assert_equal shift1.hash, shift2.hash
|
325
|
+
end
|
326
|
+
|
327
|
+
it "is usually different when the beginning time is different" do
|
328
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
329
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
330
|
+
shift1 = Tod::Shift.new tod1, tod2
|
331
|
+
shift2 = Tod::Shift.new tod1, Tod::TimeOfDay.new(14,00)
|
332
|
+
assert shift1.hash != shift2.hash
|
333
|
+
end
|
334
|
+
|
335
|
+
it "is usually different when the ending time is different" do
|
336
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
337
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
338
|
+
shift1 = Tod::Shift.new tod1, tod2
|
339
|
+
shift2 = Tod::Shift.new Tod::TimeOfDay.new(9,30), tod2
|
340
|
+
assert shift1.hash != shift2.hash
|
341
|
+
end
|
342
|
+
|
343
|
+
it "is usually different when exclude end is different" do
|
344
|
+
tod1 = Tod::TimeOfDay.new 8,30
|
345
|
+
tod2 = Tod::TimeOfDay.new 13,00,30
|
346
|
+
shift1 = Tod::Shift.new tod1, tod2
|
347
|
+
shift2 = Tod::Shift.new tod1, tod2, true
|
348
|
+
assert shift1.hash != shift2.hash
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
describe "slide" do
|
353
|
+
it "handles positive numbers" do
|
354
|
+
slide = 30 * 60 # 30 minutes in seconds
|
355
|
+
|
356
|
+
beginning_expected = Tod::TimeOfDay.new 10, 30
|
357
|
+
ending_expected = Tod::TimeOfDay.new 16, 30
|
358
|
+
|
359
|
+
beginning = Tod::TimeOfDay.new 10
|
360
|
+
ending = Tod::TimeOfDay.new 16
|
361
|
+
shift = Tod::Shift.new(beginning, ending, false).slide(slide)
|
362
|
+
|
363
|
+
assert_equal beginning_expected, shift.beginning
|
364
|
+
assert_equal ending_expected, shift.ending
|
365
|
+
refute shift.exclude_end?
|
366
|
+
end
|
367
|
+
|
368
|
+
it "handles negative numbers" do
|
369
|
+
slide = -30 * 60 # -30 minutes in seconds
|
370
|
+
|
371
|
+
beginning_expected = Tod::TimeOfDay.new 9, 30
|
372
|
+
ending_expected = Tod::TimeOfDay.new 15, 30
|
373
|
+
|
374
|
+
beginning = Tod::TimeOfDay.new 10
|
375
|
+
ending = Tod::TimeOfDay.new 16
|
376
|
+
shift = Tod::Shift.new(beginning, ending, false).slide(slide)
|
377
|
+
|
378
|
+
assert_equal beginning_expected, shift.beginning
|
379
|
+
assert_equal ending_expected, shift.ending
|
380
|
+
refute shift.exclude_end?
|
381
|
+
end
|
382
|
+
|
383
|
+
it "handles ActiveSupport::Duration" do
|
384
|
+
slide = 30.minutes
|
385
|
+
|
386
|
+
beginning_expected = Tod::TimeOfDay.new 10, 30
|
387
|
+
ending_expected = Tod::TimeOfDay.new 16, 30
|
388
|
+
|
389
|
+
beginning = Tod::TimeOfDay.new 10
|
390
|
+
ending = Tod::TimeOfDay.new 16
|
391
|
+
shift = Tod::Shift.new(beginning, ending, false).slide(slide)
|
392
|
+
|
393
|
+
assert_equal beginning_expected, shift.beginning
|
394
|
+
assert_equal ending_expected, shift.ending
|
395
|
+
refute shift.exclude_end?
|
396
|
+
end
|
397
|
+
end
|
169
398
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__),'..','support/active_record'))
|
3
|
+
|
4
|
+
require 'tod/time_of_day_type'
|
5
|
+
|
6
|
+
ActiveModel::Type.register(:time_only, Tod::TimeOfDayType)
|
7
|
+
ActiveRecord::Type.register(:time_only, Tod::TimeOfDayType)
|
8
|
+
|
9
|
+
class Order < ActiveRecord::Base
|
10
|
+
attribute :time, :time_only
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "TimeOfDay with ActiveRecord Attribute" do
|
14
|
+
it "sets time of day" do
|
15
|
+
Order.create!(time: Tod::TimeOfDay.new(9, 30))
|
16
|
+
end
|
17
|
+
|
18
|
+
it "sets nil as value" do
|
19
|
+
Order.create!(time: nil)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "works with multiparam time arguments" do
|
23
|
+
order = Order.create!({"time(4i)" => "8", "time(5i)" => "6", "time(6i)" => "5"})
|
24
|
+
assert_equal Tod::TimeOfDay.new(8,6,5), order.time
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not raise Exception on access of unparsable values" do
|
28
|
+
order = Order.new(time: 'unparsable')
|
29
|
+
order.time
|
30
|
+
assert order.valid?
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should dump unparsable values to nil" do
|
34
|
+
assert_nil Order.new(time: '').time
|
35
|
+
assert_nil Order.new(time: 'unparsable').time
|
36
|
+
assert_nil Order.new(time: nil).time
|
37
|
+
end
|
38
|
+
|
39
|
+
it "loads set Tod::TimeOfDay" do
|
40
|
+
time_of_day = Tod::TimeOfDay.new(9, 30)
|
41
|
+
order = Order.create!(time: time_of_day)
|
42
|
+
order.reload
|
43
|
+
assert_equal order.time, time_of_day
|
44
|
+
end
|
45
|
+
|
46
|
+
it "loads set Time" do
|
47
|
+
time_of_day = Time.new(2015, 10, 21, 16, 29, 0, "-07:00")
|
48
|
+
order = Order.create!(time: time_of_day)
|
49
|
+
order.reload
|
50
|
+
assert_equal order.time, Tod::TimeOfDay.new(16, 29)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns nil if time is not set" do
|
54
|
+
order = Order.create!(time: nil)
|
55
|
+
order.reload
|
56
|
+
assert_nil order.time
|
57
|
+
end
|
58
|
+
|
59
|
+
it "dump 24:00:00 and get it back" do
|
60
|
+
time_of_day = Tod::TimeOfDay.new(24, 0, 0)
|
61
|
+
order = Order.create!(time: time_of_day)
|
62
|
+
order.reload
|
63
|
+
assert_equal Tod::TimeOfDay.new(24), order.time
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "Order.where" do
|
67
|
+
it "handles TimeOfDay as a parameter" do
|
68
|
+
tod = Tod::TimeOfDay.new(11, 11)
|
69
|
+
expected = Order.create!(time: tod)
|
70
|
+
actual = Order.where(time: tod).first
|
71
|
+
assert_equal expected, actual
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
|
1
|
+
require_relative '../test_helper'
|
2
2
|
|
3
3
|
describe "TimeOfDay" do
|
4
4
|
describe "#initialize" do
|
5
5
|
it "blocks invalid hours" do
|
6
|
-
assert_raises(ArgumentError) { Tod::TimeOfDay.new
|
7
|
-
assert_raises(ArgumentError) { Tod::TimeOfDay.new
|
6
|
+
assert_raises(ArgumentError) { Tod::TimeOfDay.new(-1) }
|
7
|
+
assert_raises(ArgumentError) { Tod::TimeOfDay.new 25 }
|
8
8
|
end
|
9
9
|
|
10
10
|
it "blocks invalid minutes" do
|
@@ -31,6 +31,10 @@ describe "TimeOfDay" do
|
|
31
31
|
assert_equal 86399, Tod::TimeOfDay.new(23,59,59).second_of_day
|
32
32
|
end
|
33
33
|
|
34
|
+
it "is 86400 at beginning of next day" do
|
35
|
+
assert_equal 86400, Tod::TimeOfDay.new(24,0,0).second_of_day
|
36
|
+
end
|
37
|
+
|
34
38
|
it "have alias to_i" do
|
35
39
|
tod = Tod::TimeOfDay.new(0,0,0)
|
36
40
|
assert_equal tod.method(:second_of_day), tod.method(:to_i)
|
@@ -50,7 +54,7 @@ describe "TimeOfDay" do
|
|
50
54
|
def self.should_not_parse(parse_string)
|
51
55
|
it "does not parse '#{parse_string}'" do
|
52
56
|
assert_equal false, Tod::TimeOfDay.parsable?(parse_string)
|
53
|
-
|
57
|
+
assert_nil Tod::TimeOfDay.try_parse(parse_string)
|
54
58
|
assert_raises(ArgumentError) { Tod::TimeOfDay.parse(parse_string) }
|
55
59
|
end
|
56
60
|
end
|
@@ -84,9 +88,12 @@ describe "TimeOfDay" do
|
|
84
88
|
should_parse "12a", 0, 0, 0
|
85
89
|
should_parse "12p", 12, 0, 0
|
86
90
|
|
91
|
+
should_parse "24:00:00", 24, 0, 0
|
92
|
+
should_parse "24:00", 24, 0, 0
|
93
|
+
should_parse "24", 24, 0, 0
|
94
|
+
should_parse "2400", 24, 0, 0
|
95
|
+
|
87
96
|
should_not_parse "-1:30"
|
88
|
-
should_not_parse "24:00:00"
|
89
|
-
should_not_parse "24"
|
90
97
|
should_not_parse "00:60"
|
91
98
|
should_not_parse "00:00:60"
|
92
99
|
should_not_parse "13a"
|
@@ -98,12 +105,17 @@ describe "TimeOfDay" do
|
|
98
105
|
|
99
106
|
it "does not parse 'nil'" do
|
100
107
|
assert_equal false, Tod::TimeOfDay.parsable?(nil)
|
101
|
-
|
108
|
+
assert_nil Tod::TimeOfDay.try_parse(nil)
|
102
109
|
assert_raises(ArgumentError) { Tod::TimeOfDay.parse(nil) }
|
103
110
|
end
|
104
111
|
|
112
|
+
it "executes block on parsing failure and return result instead" do
|
113
|
+
assert_equal Tod::TimeOfDay.new(1), Tod::TimeOfDay.parse('25:00:00') { Tod::TimeOfDay.new(1) }
|
114
|
+
assert_equal Tod::TimeOfDay.new(1), Tod::TimeOfDay.parse('25:00:00') { |time_string| Tod::TimeOfDay.parse(time_string.sub('25', '01')) }
|
115
|
+
end
|
116
|
+
|
105
117
|
it "provides spaceship operator" do
|
106
|
-
assert_equal
|
118
|
+
assert_equal(-1, Tod::TimeOfDay.new(8,0,0) <=> Tod::TimeOfDay.new(9,0,0))
|
107
119
|
assert_equal 0, Tod::TimeOfDay.new(9,0,0) <=> Tod::TimeOfDay.new(9,0,0)
|
108
120
|
assert_equal 1, Tod::TimeOfDay.new(10,0,0) <=> Tod::TimeOfDay.new(9,0,0)
|
109
121
|
end
|
@@ -112,6 +124,20 @@ describe "TimeOfDay" do
|
|
112
124
|
assert_equal Tod::TimeOfDay.new(8,0,0), Tod::TimeOfDay.new(8,0,0)
|
113
125
|
end
|
114
126
|
|
127
|
+
describe "round_nearest" do
|
128
|
+
it "rounds to the given nearest number of seconds" do
|
129
|
+
assert_equal Tod::TimeOfDay.new(8,15,30), Tod::TimeOfDay.new(8,15,31).round(5)
|
130
|
+
assert_equal Tod::TimeOfDay.new(8,15,35), Tod::TimeOfDay.new(8,15,33).round(5)
|
131
|
+
assert_equal Tod::TimeOfDay.new(8,16,0), Tod::TimeOfDay.new(8,15,34).round(60)
|
132
|
+
assert_equal Tod::TimeOfDay.new(8,15,0), Tod::TimeOfDay.new(8,15,15).round(60)
|
133
|
+
assert_equal Tod::TimeOfDay.new(8,15,0), Tod::TimeOfDay.new(8,17,15).round(300)
|
134
|
+
assert_equal Tod::TimeOfDay.new(8,20,0), Tod::TimeOfDay.new(8,18,15).round(300)
|
135
|
+
assert_equal Tod::TimeOfDay.new(8,20,0), Tod::TimeOfDay.new(8,20,00).round(300)
|
136
|
+
assert_equal Tod::TimeOfDay.new(9,0,0), Tod::TimeOfDay.new(8,58,00).round(300)
|
137
|
+
assert_equal Tod::TimeOfDay.new(8,0,0), Tod::TimeOfDay.new(8,02,29).round(300)
|
138
|
+
assert_equal Tod::TimeOfDay.new(24,0,0), Tod::TimeOfDay.new(23,58,29).round(300)
|
139
|
+
end
|
140
|
+
end
|
115
141
|
|
116
142
|
describe "strftime" do
|
117
143
|
it "accepts standard strftime format codes" do
|
@@ -119,10 +145,48 @@ describe "TimeOfDay" do
|
|
119
145
|
end
|
120
146
|
end
|
121
147
|
|
148
|
+
describe "to_formatted_s" do
|
149
|
+
it "has a default format" do
|
150
|
+
assert_equal "08:15:30", Tod::TimeOfDay.new(8,15,30).to_formatted_s
|
151
|
+
assert_equal "22:10:45", Tod::TimeOfDay.new(22,10,45).to_formatted_s
|
152
|
+
end
|
153
|
+
|
154
|
+
it "has a short format" do
|
155
|
+
assert_equal "8:15 am", Tod::TimeOfDay.new(8,15,30).to_formatted_s(:short)
|
156
|
+
assert_equal "10:10 pm", Tod::TimeOfDay.new(22,10,45).to_formatted_s(:short)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "has a medium format" do
|
160
|
+
assert_equal "8:15:30 am", Tod::TimeOfDay.new(8,15,30).to_formatted_s(:medium)
|
161
|
+
assert_equal "10:10:45 pm", Tod::TimeOfDay.new(22,10,45).to_formatted_s(:medium)
|
162
|
+
end
|
163
|
+
|
164
|
+
it "has an ActiveSupport compatible time format" do
|
165
|
+
assert_equal "08:15", Tod::TimeOfDay.new(8,15,30).to_formatted_s(:time)
|
166
|
+
assert_equal "22:10", Tod::TimeOfDay.new(22,10,45).to_formatted_s(:time)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "evaluates custom lambda lambda formats" do
|
170
|
+
begin
|
171
|
+
Tod::TimeOfDay::FORMATS[:lambda] = -> (t) { t.strftime("%H%M 😎") }
|
172
|
+
assert_equal "1337 😎", Tod::TimeOfDay.new(13,37).to_formatted_s(:lambda)
|
173
|
+
ensure
|
174
|
+
Tod::TimeOfDay::FORMATS.delete(:lambda)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "value_for_database" do
|
180
|
+
it "returns a formatted value for database query serialization" do
|
181
|
+
t = Tod::TimeOfDay.new(12,15,05)
|
182
|
+
assert_equal "12:15:05", t.value_for_database
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
122
186
|
describe "to_s" do
|
123
|
-
it "
|
124
|
-
|
125
|
-
assert_equal
|
187
|
+
it "is aliased to to_formatted_s" do
|
188
|
+
t = Tod::TimeOfDay.new(8,15,30)
|
189
|
+
assert_equal t.to_formatted_s, t.to_s
|
126
190
|
end
|
127
191
|
end
|
128
192
|
|
@@ -151,6 +215,12 @@ describe "TimeOfDay" do
|
|
151
215
|
result = original + 15
|
152
216
|
refute_equal original.object_id, result.object_id
|
153
217
|
end
|
218
|
+
|
219
|
+
it "handles ActiveSupport::Duration" do
|
220
|
+
original = Tod::TimeOfDay.new(8,0,0)
|
221
|
+
result = original + 10.minutes
|
222
|
+
assert_equal Tod::TimeOfDay.new(8,10,0), result
|
223
|
+
end
|
154
224
|
end
|
155
225
|
|
156
226
|
describe "subtraction" do
|
@@ -171,6 +241,19 @@ describe "TimeOfDay" do
|
|
171
241
|
result = original - 15
|
172
242
|
refute_equal original.object_id, result.object_id
|
173
243
|
end
|
244
|
+
|
245
|
+
it "handles ActiveSupport::Duration" do
|
246
|
+
original = Tod::TimeOfDay.new(8,0,0)
|
247
|
+
result = original - 10.minutes
|
248
|
+
assert_equal Tod::TimeOfDay.new(7,50,0), result
|
249
|
+
end
|
250
|
+
|
251
|
+
it "subtracts Tod::TimeOfDay object" do
|
252
|
+
right_side = Tod::TimeOfDay.new(10,0,0)
|
253
|
+
left_side = Tod::TimeOfDay.new(12,0,30)
|
254
|
+
result = right_side - left_side
|
255
|
+
assert_equal Tod::TimeOfDay.new(21,59,30), result
|
256
|
+
end
|
174
257
|
end
|
175
258
|
|
176
259
|
describe "from_second_of_day" do
|
@@ -182,7 +265,7 @@ describe "TimeOfDay" do
|
|
182
265
|
end
|
183
266
|
|
184
267
|
it "handles positive numbers a day or more away" do
|
185
|
-
assert_equal Tod::TimeOfDay.new(
|
268
|
+
assert_equal Tod::TimeOfDay.new(24,0,0), Tod::TimeOfDay.from_second_of_day(86400)
|
186
269
|
assert_equal Tod::TimeOfDay.new(0,0,30), Tod::TimeOfDay.from_second_of_day(86430)
|
187
270
|
assert_equal Tod::TimeOfDay.new(0,1,30), Tod::TimeOfDay.from_second_of_day(86490)
|
188
271
|
assert_equal Tod::TimeOfDay.new(1,1,5), Tod::TimeOfDay.from_second_of_day(90065)
|
@@ -200,6 +283,10 @@ describe "TimeOfDay" do
|
|
200
283
|
it "has alias from_i" do
|
201
284
|
assert_equal Tod::TimeOfDay.method(:from_second_of_day), Tod::TimeOfDay.method(:from_i)
|
202
285
|
end
|
286
|
+
|
287
|
+
it "handles floats" do
|
288
|
+
assert_equal Tod::TimeOfDay.new(15,30,0), Tod::TimeOfDay.from_second_of_day(55800.0)
|
289
|
+
end
|
203
290
|
end
|
204
291
|
|
205
292
|
describe "on" do
|
data/test/tod/time_test.rb
CHANGED
data/tod.gemspec
CHANGED
@@ -16,9 +16,9 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.add_development_dependency "minitest"
|
17
17
|
s.add_development_dependency "tzinfo"
|
18
18
|
s.add_development_dependency "rake"
|
19
|
-
s.add_development_dependency "activerecord", ">=
|
19
|
+
s.add_development_dependency "activerecord", ">= 6.0.0"
|
20
20
|
s.add_development_dependency "sqlite3"
|
21
|
-
s.add_development_dependency "pry"
|
21
|
+
s.add_development_dependency "pry-byebug"
|
22
22
|
|
23
23
|
s.files = `git ls-files`.split("\n")
|
24
24
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tod
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Christensen
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 6.0.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 6.0.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: sqlite3
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,7 +81,7 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name: pry
|
84
|
+
name: pry-byebug
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
@@ -110,25 +110,23 @@ files:
|
|
110
110
|
- MIT-LICENSE
|
111
111
|
- README.markdown
|
112
112
|
- Rakefile
|
113
|
-
- gemfiles/3.2.gemfile
|
114
|
-
- gemfiles/4.0.gemfile
|
115
|
-
- gemfiles/4.1.gemfile
|
116
|
-
- gemfiles/4.2.gemfile
|
117
113
|
- lib/tod.rb
|
118
114
|
- lib/tod/conversions.rb
|
119
115
|
- lib/tod/core_extensions.rb
|
120
116
|
- lib/tod/date_extensions.rb
|
121
117
|
- lib/tod/mongoization.rb
|
118
|
+
- lib/tod/railtie.rb
|
122
119
|
- lib/tod/shift.rb
|
123
120
|
- lib/tod/time_extensions.rb
|
124
121
|
- lib/tod/time_of_day.rb
|
122
|
+
- lib/tod/time_of_day_type.rb
|
125
123
|
- lib/tod/version.rb
|
126
124
|
- test/support/active_record.rb
|
127
125
|
- test/test_helper.rb
|
128
126
|
- test/tod/conversion_test.rb
|
129
127
|
- test/tod/date_test.rb
|
130
128
|
- test/tod/shift_test.rb
|
131
|
-
- test/tod/
|
129
|
+
- test/tod/time_of_day_attribute_test.rb
|
132
130
|
- test/tod/time_of_day_test.rb
|
133
131
|
- test/tod/time_of_day_time_zone_with_active_support_test.rb
|
134
132
|
- test/tod/time_test.rb
|
@@ -137,7 +135,7 @@ homepage: https://github.com/JackC/tod
|
|
137
135
|
licenses:
|
138
136
|
- MIT
|
139
137
|
metadata: {}
|
140
|
-
post_install_message:
|
138
|
+
post_install_message:
|
141
139
|
rdoc_options: []
|
142
140
|
require_paths:
|
143
141
|
- lib
|
@@ -152,9 +150,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
152
150
|
- !ruby/object:Gem::Version
|
153
151
|
version: '0'
|
154
152
|
requirements: []
|
155
|
-
|
156
|
-
|
157
|
-
signing_key:
|
153
|
+
rubygems_version: 3.0.6
|
154
|
+
signing_key:
|
158
155
|
specification_version: 4
|
159
156
|
summary: Supplies TimeOfDay and Shift class
|
160
157
|
test_files:
|
@@ -163,7 +160,7 @@ test_files:
|
|
163
160
|
- test/tod/conversion_test.rb
|
164
161
|
- test/tod/date_test.rb
|
165
162
|
- test/tod/shift_test.rb
|
166
|
-
- test/tod/
|
163
|
+
- test/tod/time_of_day_attribute_test.rb
|
167
164
|
- test/tod/time_of_day_test.rb
|
168
165
|
- test/tod/time_of_day_time_zone_with_active_support_test.rb
|
169
166
|
- test/tod/time_test.rb
|
data/gemfiles/3.2.gemfile
DELETED
data/gemfiles/4.0.gemfile
DELETED
data/gemfiles/4.1.gemfile
DELETED
data/gemfiles/4.2.gemfile
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require File.expand_path(File.join(File.dirname(__FILE__),'..','test_helper'))
|
2
|
-
require File.expand_path(File.join(File.dirname(__FILE__),'..','support/active_record'))
|
3
|
-
|
4
|
-
class Order < ActiveRecord::Base
|
5
|
-
serialize :time, Tod::TimeOfDay
|
6
|
-
end
|
7
|
-
|
8
|
-
describe "TimeOfDay with ActiveRecord Serializable Attribute" do
|
9
|
-
describe ".dump" do
|
10
|
-
it "sets time of day" do
|
11
|
-
Order.create(time: Tod::TimeOfDay.new(9, 30))
|
12
|
-
end
|
13
|
-
|
14
|
-
it "sets nil as value" do
|
15
|
-
Order.create(time: nil)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
describe ".load" do
|
20
|
-
it "loads set time" do
|
21
|
-
time_of_day = Tod::TimeOfDay.new(9, 30)
|
22
|
-
order = Order.create(time: time_of_day)
|
23
|
-
assert_equal order.time, time_of_day
|
24
|
-
end
|
25
|
-
|
26
|
-
it "returns nil if time is not set" do
|
27
|
-
order = Order.create(time: nil)
|
28
|
-
assert_equal order.time, nil
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|