tod 2.0.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://travis-ci.org/jackc/tod.png)](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
|