time_only 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +59 -0
- data/Rakefile +8 -0
- data/lib/time_only.rb +260 -0
- data/lib/time_only/version.rb +3 -0
- data/spec/time_only_spec.rb +391 -0
- data/time_only.gemspec +21 -0
- metadata +116 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 OrgSync and Aaron Lasseigne
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# TimeOnly
|
2
|
+
|
3
|
+
A simple class for handling time and only time. No dates, no time zones, just good old time of day.
|
4
|
+
At the moment, `TimeOnly` only supports resolution to 1 second.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
gem 'time_only'
|
11
|
+
|
12
|
+
And then execute:
|
13
|
+
|
14
|
+
$ bundle
|
15
|
+
|
16
|
+
Or install it yourself as:
|
17
|
+
|
18
|
+
$ gem install time_only
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
`TimeOnly` attempts to implement the same functionality as `Time` where it makes sense to do so.
|
23
|
+
|
24
|
+
require 'time_only'
|
25
|
+
|
26
|
+
time = TimeOnly.new(8, 0, 2)
|
27
|
+
# => '08:00:02'
|
28
|
+
|
29
|
+
time.hour
|
30
|
+
# => 8
|
31
|
+
|
32
|
+
time.min
|
33
|
+
# => 0
|
34
|
+
|
35
|
+
time.sec
|
36
|
+
# => 2
|
37
|
+
|
38
|
+
time.strftime('The time is %-l:%M:%S %P.')
|
39
|
+
# => 'The time is 1:03:56 pm.'
|
40
|
+
|
41
|
+
# adding times rolls the time forward and returns a new TimeOnly
|
42
|
+
TimeOnly.new(23, 59, 59) + 3
|
43
|
+
# => '00:00:02'
|
44
|
+
|
45
|
+
morning_flights, afternoon_flights = departure_times.partition(&:am?)
|
46
|
+
|
47
|
+
current_time = TimeOnly.now
|
48
|
+
|
49
|
+
# and many more...
|
50
|
+
|
51
|
+
Method documentation can be found in the source.
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
1. Fork it
|
56
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
57
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
58
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
59
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/time_only.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
class TimeOnly
|
2
|
+
include Comparable
|
3
|
+
|
4
|
+
SECONDS_PER_MIN = 60
|
5
|
+
SECONDS_PER_HOUR = 60 * SECONDS_PER_MIN
|
6
|
+
SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR
|
7
|
+
|
8
|
+
# Public: Initialize a TimeOnly.
|
9
|
+
#
|
10
|
+
# seconds - The Integer number of seconds since midnight.
|
11
|
+
#
|
12
|
+
# Examples
|
13
|
+
#
|
14
|
+
# TimeOnly.at(4)
|
15
|
+
# # => '00:00:04'
|
16
|
+
def self.at(seconds)
|
17
|
+
new(seconds)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.now
|
21
|
+
time = Time.now
|
22
|
+
|
23
|
+
new(time.hour, time.min, time.sec)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Initialize a TimeOnly.
|
27
|
+
#
|
28
|
+
# seconds - The Integer number of seconds since midnight.
|
29
|
+
# OR
|
30
|
+
# hours - The Integer number of hours.
|
31
|
+
# minutes - The Integer number of minutes.
|
32
|
+
# seconds - The Integer number of seconds.
|
33
|
+
#
|
34
|
+
# Examples
|
35
|
+
#
|
36
|
+
# TimeOnly.new(4)
|
37
|
+
# # => '00:00:04'
|
38
|
+
#
|
39
|
+
# TimeOnly.new(13, 24, 56)
|
40
|
+
# # => '13:24:56'
|
41
|
+
def initialize(*args)
|
42
|
+
seconds = case args.size
|
43
|
+
when 1
|
44
|
+
args.first
|
45
|
+
when 3
|
46
|
+
hours, minutes, seconds = args
|
47
|
+
|
48
|
+
raise ArgumentError, 'hours must be between 0 and 23' if hours < 0 || hours > 23
|
49
|
+
raise ArgumentError, 'minutes must be between 0 and 59' if minutes < 0 || minutes > 59
|
50
|
+
raise ArgumentError, 'seconds must be between 0 and 59' if seconds < 0 || seconds > 59
|
51
|
+
|
52
|
+
(hours * SECONDS_PER_HOUR) + (minutes * SECONDS_PER_MIN) + seconds
|
53
|
+
else
|
54
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1 or 3)"
|
55
|
+
end
|
56
|
+
|
57
|
+
@seconds_since_midnight = mod_by_day(seconds)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Add seconds to the time and return that as a new value. If the value
|
61
|
+
# exceeds the number of seconds in a day the time will roll forwardd.
|
62
|
+
#
|
63
|
+
# seconds - The Integer number of seconds.
|
64
|
+
#
|
65
|
+
# Examples
|
66
|
+
#
|
67
|
+
# TimeOnly.new(4) + 3
|
68
|
+
# # => '00:00:07'
|
69
|
+
#
|
70
|
+
# TimeOnly.new(23, 59, 59) + 3
|
71
|
+
# # => '00:00:02'
|
72
|
+
#
|
73
|
+
# Returns a new TimeOnly.
|
74
|
+
def +(seconds)
|
75
|
+
self.class.new(mod_by_day(@seconds_since_midnight + seconds))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Subtract seconds from the time and return that as a new value. If the
|
79
|
+
# value is less than zero seconds in a day the time will roll backwards.
|
80
|
+
#
|
81
|
+
# seconds - The Integer number of seconds.
|
82
|
+
#
|
83
|
+
# Examples
|
84
|
+
#
|
85
|
+
# TimeOnly.new(4) - 3
|
86
|
+
# # => '00:00:01'
|
87
|
+
#
|
88
|
+
# TimeOnly.new(0, 0, 0) - 3
|
89
|
+
# # => '23:59:57'
|
90
|
+
#
|
91
|
+
# Returns a new TimeOnly.
|
92
|
+
def -(seconds)
|
93
|
+
self + (seconds * -1)
|
94
|
+
end
|
95
|
+
|
96
|
+
def ==(other)
|
97
|
+
to_i == other.to_i
|
98
|
+
end
|
99
|
+
alias_method :eql?, :==
|
100
|
+
|
101
|
+
def <=>(other)
|
102
|
+
to_i <=> other.to_i
|
103
|
+
end
|
104
|
+
|
105
|
+
def am?
|
106
|
+
hour < 12
|
107
|
+
end
|
108
|
+
|
109
|
+
def hour
|
110
|
+
@hour ||= @seconds_since_midnight / SECONDS_PER_HOUR
|
111
|
+
end
|
112
|
+
|
113
|
+
def min
|
114
|
+
@min ||= (@seconds_since_midnight % SECONDS_PER_HOUR) / SECONDS_PER_MIN
|
115
|
+
end
|
116
|
+
|
117
|
+
def pm?
|
118
|
+
!am?
|
119
|
+
end
|
120
|
+
|
121
|
+
def sec
|
122
|
+
@sec ||= @seconds_since_midnight % SECONDS_PER_MIN
|
123
|
+
end
|
124
|
+
|
125
|
+
# Public: Formats time according to the directives in the given format string.
|
126
|
+
# The directives begins with a percent (%) character. Any text not listed as a
|
127
|
+
# directive will be passed through to the output string.)
|
128
|
+
#
|
129
|
+
# The directive consists of a percent (%) character, zero or more flags and a
|
130
|
+
# conversion specifier as follows.
|
131
|
+
#
|
132
|
+
# %<flags><conversion>
|
133
|
+
#
|
134
|
+
# Flags:
|
135
|
+
# - don't pad a numerical output
|
136
|
+
#
|
137
|
+
# Directives:
|
138
|
+
# %H - Hour of the day, 24-hour clock, zero-padded (00..23)
|
139
|
+
# %k - Hour of the day, 24-hour clock, blank-padded ( 0..23)
|
140
|
+
# %I - Hour of the day, 12-hour clock, zero-padded (01..12)
|
141
|
+
# %l - Hour of the day, 12-hour clock, blank-padded ( 1..12)
|
142
|
+
# %P - Meridian indicator, lowercase (``am' or ``pm')
|
143
|
+
# %p - Meridian indicator, uppercase (``AM' or ``PM')
|
144
|
+
# %M - Minute of the hour (00..59)
|
145
|
+
# %S - Second of the minute (00..60)
|
146
|
+
#
|
147
|
+
# Literal strings:
|
148
|
+
# %n - Newline character (\n)
|
149
|
+
# %t - Tab character (\t)
|
150
|
+
# %% - Literal ``%'' character)
|
151
|
+
#
|
152
|
+
# Combinations:
|
153
|
+
# %X - Same as %T
|
154
|
+
# %r - 12-hour time (%I:%M:%S %p)
|
155
|
+
# %R - 24-hour time (%H:%M)
|
156
|
+
# %T - 24-hour time (%H:%M:%S)
|
157
|
+
#
|
158
|
+
# format - The String containing directives.
|
159
|
+
#
|
160
|
+
# Examples
|
161
|
+
#
|
162
|
+
# TimeOnly.new(12, 34, 56).strftime('%r')
|
163
|
+
# # => '12:34:56 PM'
|
164
|
+
#
|
165
|
+
# TimeOnly.new(1, 3, 56).strftime('The time is %-l:%M:%S %P.')
|
166
|
+
# # => 'The time is 1:03:56 pm.'
|
167
|
+
#
|
168
|
+
# Returns the formatted String.
|
169
|
+
def strftime(format)
|
170
|
+
format = format.dup
|
171
|
+
|
172
|
+
format.gsub!(/%[rRTX]/) do |token|
|
173
|
+
case token
|
174
|
+
when '%r'
|
175
|
+
'%I:%M:%S %p'
|
176
|
+
when '%R'
|
177
|
+
'%H:%M'
|
178
|
+
when '%T', '%X'
|
179
|
+
'%H:%M:%S'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
format.gsub(/%-?[HkIlPpMSnt%]/) do |token|
|
184
|
+
case token
|
185
|
+
when '%H'
|
186
|
+
zero_pad(hour)
|
187
|
+
when '%k'
|
188
|
+
blank_pad(hour)
|
189
|
+
when '%-H', '%-k'
|
190
|
+
hour
|
191
|
+
when '%I'
|
192
|
+
zero_pad(twelve_hour)
|
193
|
+
when '%l'
|
194
|
+
blank_pad(twelve_hour)
|
195
|
+
when '%-I', '%-l'
|
196
|
+
twelve_hour
|
197
|
+
when '%P'
|
198
|
+
am? ? 'am' : 'pm'
|
199
|
+
when '%p'
|
200
|
+
am? ? 'AM' : 'PM'
|
201
|
+
when '%M'
|
202
|
+
zero_pad(min)
|
203
|
+
when '%-M'
|
204
|
+
min
|
205
|
+
when '%S'
|
206
|
+
zero_pad(sec)
|
207
|
+
when '%-S'
|
208
|
+
sec
|
209
|
+
when '%n'
|
210
|
+
"\n"
|
211
|
+
when '%t'
|
212
|
+
"\t"
|
213
|
+
when '%%'
|
214
|
+
'%'
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def succ
|
220
|
+
self + 1
|
221
|
+
end
|
222
|
+
|
223
|
+
def to_a
|
224
|
+
[hour, min, sec]
|
225
|
+
end
|
226
|
+
|
227
|
+
def to_f
|
228
|
+
@seconds_since_midnight.to_f
|
229
|
+
end
|
230
|
+
|
231
|
+
def to_i
|
232
|
+
@seconds_since_midnight
|
233
|
+
end
|
234
|
+
alias_method :tv_sec, :to_i
|
235
|
+
|
236
|
+
def to_s
|
237
|
+
strftime('%T')
|
238
|
+
end
|
239
|
+
alias_method :asctime, :to_s
|
240
|
+
alias_method :ctime, :to_s
|
241
|
+
alias_method :inspect, :to_s
|
242
|
+
|
243
|
+
private
|
244
|
+
|
245
|
+
def mod_by_day(seconds)
|
246
|
+
seconds % SECONDS_PER_DAY
|
247
|
+
end
|
248
|
+
|
249
|
+
def twelve_hour
|
250
|
+
hour > 12 ? hour % 12 : hour
|
251
|
+
end
|
252
|
+
|
253
|
+
def zero_pad(number)
|
254
|
+
number < 10 ? "0#{number}" : number
|
255
|
+
end
|
256
|
+
|
257
|
+
def blank_pad(number)
|
258
|
+
number < 10 ? " #{number}" : number
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,391 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'timecop'
|
3
|
+
require 'time_only'
|
4
|
+
|
5
|
+
describe TimeOnly do
|
6
|
+
describe '.new(args)' do
|
7
|
+
context 'one arg' do
|
8
|
+
it 'creates as the seconds since midnight' do
|
9
|
+
expect(described_class.new(45296).to_i).to eq(45296)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "rolls over seconds that are greater than #{described_class::SECONDS_PER_DAY}" do
|
13
|
+
expect(described_class.new(described_class::SECONDS_PER_DAY * 2 + 1).to_i).to eq(1)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'handles negative seconds which rolls back' do
|
17
|
+
expect(described_class.new(-10).to_i).to eq(described_class::SECONDS_PER_DAY - 10)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'three argss' do
|
22
|
+
it 'creates based on hours, minutes and seconds' do
|
23
|
+
expect(described_class.new(12, 34, 56).to_i).to eq(45296)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'raises an error when the hours arg is outside of 0 - 23' do
|
27
|
+
expect{ described_class.new(24, 0, 0) }.to raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises an error when the minutes arg is outside of 0 - 59' do
|
31
|
+
expect{ described_class.new(0, 60, 0) }.to raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'raises an error when the seconds arg is outside of 0 - 59' do
|
35
|
+
expect{ described_class.new(0, 0, 60) }.to raise_error(ArgumentError)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'raises an error when the wrong number of arguments is passed' do
|
40
|
+
expect{ described_class.new(1, 2) }.to raise_error(ArgumentError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '.at(seconds)' do
|
45
|
+
it 'creates a time based on the number of seconds since midnight' do
|
46
|
+
expect(described_class.at(300).to_i).to eq(300)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "rolls over seconds that are greater than #{described_class::SECONDS_PER_DAY}" do
|
50
|
+
expect(described_class.at(described_class::SECONDS_PER_DAY * 2 + 1).to_i).to eq(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'handles negative seconds which rolls back' do
|
54
|
+
expect(described_class.at(-10).to_i).to eq(described_class::SECONDS_PER_DAY - 10)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '.now' do
|
59
|
+
it "returns a #{described_class} object for the current system time" do
|
60
|
+
hour, min, sec = 12, 34, 56
|
61
|
+
|
62
|
+
Timecop.freeze(Time.local(2012, 11, 30, hour, min, sec)) do
|
63
|
+
expect(described_class.now.to_s).to eq("#{hour}:#{min}:#{sec}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#+(seconds)' do
|
69
|
+
it "returns a new #{described_class} object with n seconds added to it" do
|
70
|
+
current_time = described_class.new(0)
|
71
|
+
new_time = current_time + 1
|
72
|
+
|
73
|
+
expect(current_time).not_to equal(new_time)
|
74
|
+
expect(new_time).to eq(described_class.new(1))
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'rolls over time when greater than 23:59:59' do
|
78
|
+
expect(described_class.new(23, 59, 59) + 2).to eq(described_class.new(0, 0, 1))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#-(seconds)' do
|
83
|
+
it "returns a new #{described_class} object with n seconds subtracted from it" do
|
84
|
+
current_time = described_class.new(1)
|
85
|
+
new_time = current_time - 1
|
86
|
+
|
87
|
+
expect(current_time).not_to equal(new_time)
|
88
|
+
expect(new_time).to eq(described_class.new(0))
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'rolls back time when less than 00:00:00' do
|
92
|
+
expect(described_class.new(00, 00, 01) - 2).to eq(described_class.new(23, 59, 59))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#==(other)' do # aliases: eql?
|
97
|
+
it 'returns true when the times are the same' do
|
98
|
+
expect(described_class.new(12, 34, 56) == described_class.new(12, 34, 56)).to be_true
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns false when the times are not the same' do
|
102
|
+
expect(described_class.new(12, 34, 56) == described_class.new(0, 0, 0)).to be_false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#<=>(other)' do
|
107
|
+
it 'returns 0 when the times are equal' do
|
108
|
+
other = described_class.new(0)
|
109
|
+
|
110
|
+
expect(described_class.new(0) <=> other).to eq(0)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns -1 when the times are equal' do
|
114
|
+
other = described_class.new(1)
|
115
|
+
|
116
|
+
expect(described_class.new(0) <=> other).to eq(-1)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'returns 1 when the times are equal' do
|
120
|
+
other = described_class.new(0)
|
121
|
+
|
122
|
+
expect(described_class.new(1) <=> other).to eq(1)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '#am?' do
|
127
|
+
context 'time is before noon' do
|
128
|
+
subject { described_class.new(2, 4, 6) }
|
129
|
+
|
130
|
+
its(:am?) { should be_true }
|
131
|
+
end
|
132
|
+
|
133
|
+
context 'time is after noon' do
|
134
|
+
subject { described_class.new(12, 4, 6) }
|
135
|
+
|
136
|
+
its(:am?) { should be_false }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#hour' do
|
141
|
+
subject { described_class.new(2, 4, 6) }
|
142
|
+
|
143
|
+
its(:hour) { should be 2 }
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#min' do
|
147
|
+
subject { described_class.new(2, 4, 6) }
|
148
|
+
|
149
|
+
its(:min) { should be 4 }
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '#pm?' do
|
153
|
+
context 'time is before noon' do
|
154
|
+
subject { described_class.new(2, 4, 6) }
|
155
|
+
|
156
|
+
its(:pm?) { should be_false }
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'time is after noon' do
|
160
|
+
subject { described_class.new(12, 4, 6) }
|
161
|
+
|
162
|
+
its(:pm?) { should be_true }
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe '#sec' do
|
167
|
+
subject { described_class.new(2, 4, 6) }
|
168
|
+
|
169
|
+
its(:sec) { should be 6 }
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#strftime' do
|
173
|
+
context 'flags' do
|
174
|
+
context "- don't pad a numerical output" do
|
175
|
+
context 'hour' do
|
176
|
+
%w(H k I l).each do |directive|
|
177
|
+
context "using %#{directive}" do
|
178
|
+
it 'hour is one digit' do
|
179
|
+
expect(described_class.new(8, 0, 0).strftime("%-#{directive}")).to eq('8')
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'hour is two digits' do
|
183
|
+
if directive == 'I' || directive == 'l'
|
184
|
+
hour, hour_s = 13, '1'
|
185
|
+
else
|
186
|
+
hour, hour_s = 12, '12'
|
187
|
+
end
|
188
|
+
|
189
|
+
expect(described_class.new(hour, 0, 0).strftime("%-#{directive}")).to eq(hour_s)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'min' do
|
196
|
+
it 'min is one digit' do
|
197
|
+
expect(described_class.new(0, 8, 0).strftime('%-M')).to eq('8')
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'min is two digits' do
|
201
|
+
expect(described_class.new(0, 12, 0).strftime('%-M')).to eq('12')
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'sec' do
|
206
|
+
it 'sec is one digit' do
|
207
|
+
expect(described_class.new(0, 0, 8).strftime('%-S')).to eq('8')
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'sec is two digits' do
|
211
|
+
expect(described_class.new(0, 0, 12).strftime('%-S')).to eq('12')
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'directives' do
|
218
|
+
context '%H - Hour of the day, 24-hour clock, zero-padded (00..23)' do
|
219
|
+
it 'hour is one digit' do
|
220
|
+
expect(described_class.new(8, 0, 0).strftime('%H')).to eq('08')
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'hour is two digits' do
|
224
|
+
expect(described_class.new(12, 0, 0).strftime('%H')).to eq('12')
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
context '%k - Hour of the day, 24-hour clock, blank-padded ( 0..23)' do
|
229
|
+
it 'hour is one digit' do
|
230
|
+
expect(described_class.new(8, 0, 0).strftime('%k')).to eq(' 8')
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'hour is two digits' do
|
234
|
+
expect(described_class.new(12, 0, 0).strftime('%k')).to eq('12')
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
context '%I - Hour of the day, 12-hour clock, zero-padded (01..12)' do
|
239
|
+
context 'before 1pm' do
|
240
|
+
it 'hour is one digit' do
|
241
|
+
expect(described_class.new(8, 0, 0).strftime('%I')).to eq('08')
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'hour is two digits' do
|
245
|
+
expect(described_class.new(12, 0, 0).strftime('%I')).to eq('12')
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'after or equal to 1pm' do
|
250
|
+
it 'hour is one digit' do
|
251
|
+
expect(described_class.new(13, 0, 0).strftime('%I')).to eq('01')
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'hour is two digits' do
|
255
|
+
expect(described_class.new(22, 0, 0).strftime('%I')).to eq('10')
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
context '%l - Hour of the day, 12-hour clock, blank-padded ( 1..12)' do
|
261
|
+
context 'before 1pm' do
|
262
|
+
it 'hour is one digit' do
|
263
|
+
expect(described_class.new(8, 0, 0).strftime('%l')).to eq(' 8')
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'hour is two digits' do
|
267
|
+
expect(described_class.new(12, 0, 0).strftime('%l')).to eq('12')
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context 'after or equal to 1pm' do
|
272
|
+
it 'hour is one digit' do
|
273
|
+
expect(described_class.new(13, 0, 0).strftime('%l')).to eq(' 1')
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'hour is two digits' do
|
277
|
+
expect(described_class.new(22, 0, 0).strftime('%l')).to eq('10')
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context '%P - Meridian indicator, lowercase ("am" or "pm")' do
|
283
|
+
it 'hours are before noon' do
|
284
|
+
expect(described_class.new(8, 0, 0).strftime('%P')).to eq('am')
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'hours are after noon' do
|
288
|
+
expect(described_class.new(13, 0, 0).strftime('%P')).to eq('pm')
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context '%p - Meridian indicator, uppercase ("AM" or "PM")' do
|
293
|
+
it 'hours are before noon' do
|
294
|
+
expect(described_class.new(8, 0, 0).strftime('%p')).to eq('AM')
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'hours are after noon' do
|
298
|
+
expect(described_class.new(13, 0, 0).strftime('%p')).to eq('PM')
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context '%M - Minute of the hour (00..59)' do
|
303
|
+
it 'min is one digit' do
|
304
|
+
expect(described_class.new(0, 8, 0).strftime('%M')).to eq('08')
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'min is two digits' do
|
308
|
+
expect(described_class.new(0, 12, 0).strftime('%M')).to eq('12')
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
context '%S - Second of the minute (00..60)' do
|
313
|
+
it 'sec is one digit' do
|
314
|
+
expect(described_class.new(0, 0, 8).strftime('%S')).to eq('08')
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'sec is two digits' do
|
318
|
+
expect(described_class.new(0, 0, 12).strftime('%S')).to eq('12')
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context 'literals' do
|
324
|
+
it '%n - Newline character (\n)' do
|
325
|
+
expect(described_class.at(0).strftime('%n')).to eq("\n")
|
326
|
+
end
|
327
|
+
|
328
|
+
it '%t - Tab character (\t)' do
|
329
|
+
expect(described_class.at(0).strftime('%t')).to eq("\t")
|
330
|
+
end
|
331
|
+
|
332
|
+
it '%% - Literal "%" character' do
|
333
|
+
expect(described_class.at(0).strftime('%%')).to eq('%')
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'combinations' do
|
338
|
+
it '%r - 12-hour time (%I:%M:%S %p)' do
|
339
|
+
expect(described_class.new(1, 0, 12).strftime('%r')).to eq('01:00:12 AM')
|
340
|
+
end
|
341
|
+
|
342
|
+
it '%R - 24-hour time (%H:%M)' do
|
343
|
+
expect(described_class.new(1, 0, 12).strftime('%R')).to eq('01:00')
|
344
|
+
end
|
345
|
+
|
346
|
+
%w(X T).each do |combination|
|
347
|
+
it "%#{combination} - 24-hour time (%H:%M:%S)" do
|
348
|
+
expect(described_class.new(1, 0, 12).strftime("%#{combination}")).to eq('01:00:12')
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe '#succ' do
|
355
|
+
it "returns a new #{described_class} object, one second later than the original" do
|
356
|
+
current_time = described_class.new(0)
|
357
|
+
new_time = current_time.succ
|
358
|
+
|
359
|
+
expect(current_time).not_to equal(new_time)
|
360
|
+
expect(new_time).to eq(described_class.new(1))
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'rolls over time when greater than 23:59:59' do
|
364
|
+
expect(described_class.new(23, 59, 59).succ).to eq(described_class.new(0, 0, 0))
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
describe '#to_a' do
|
369
|
+
subject { described_class.new(2, 3, 4) }
|
370
|
+
|
371
|
+
its(:to_a) { should == [2, 3, 4] }
|
372
|
+
end
|
373
|
+
|
374
|
+
describe '#to_f' do
|
375
|
+
subject { described_class.new(300) }
|
376
|
+
|
377
|
+
its(:to_f) { should == 300.0 }
|
378
|
+
end
|
379
|
+
|
380
|
+
describe '#to_i' do # aliases: tv_sec
|
381
|
+
subject { described_class.new(300) }
|
382
|
+
|
383
|
+
its(:to_i) { should == 300 }
|
384
|
+
end
|
385
|
+
|
386
|
+
describe '#to_s' do # aliases: asctime, ctime, inspect
|
387
|
+
subject { described_class.new(2, 4, 6) }
|
388
|
+
|
389
|
+
its(:to_s) { should == '02:04:06' }
|
390
|
+
end
|
391
|
+
end
|
data/time_only.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/time_only/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'time_only'
|
6
|
+
gem.version = TimeOnly::VERSION
|
7
|
+
gem.summary = 'A simple class for handling time and only time.'
|
8
|
+
gem.description = "#{gem.summary} No dates, no time zones, just good old time of day."
|
9
|
+
|
10
|
+
gem.authors = ['Aaron Lasseigne']
|
11
|
+
gem.email = ['aaron@orgsync.com']
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($\)
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.require_paths = ['lib']
|
17
|
+
|
18
|
+
gem.add_development_dependency 'rake'
|
19
|
+
gem.add_development_dependency 'rspec'
|
20
|
+
gem.add_development_dependency 'timecop'
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: time_only
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Aaron Lasseigne
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-12-04 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rake
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: 3
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
requirement: *id001
|
33
|
+
prerelease: false
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
hash: 3
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
version: "0"
|
45
|
+
type: :development
|
46
|
+
requirement: *id002
|
47
|
+
prerelease: false
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: timecop
|
50
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
hash: 3
|
56
|
+
segments:
|
57
|
+
- 0
|
58
|
+
version: "0"
|
59
|
+
type: :development
|
60
|
+
requirement: *id003
|
61
|
+
prerelease: false
|
62
|
+
description: A simple class for handling time and only time. No dates, no time zones, just good old time of day.
|
63
|
+
email:
|
64
|
+
- aaron@orgsync.com
|
65
|
+
executables: []
|
66
|
+
|
67
|
+
extensions: []
|
68
|
+
|
69
|
+
extra_rdoc_files: []
|
70
|
+
|
71
|
+
files:
|
72
|
+
- .gitignore
|
73
|
+
- .travis.yml
|
74
|
+
- Gemfile
|
75
|
+
- LICENSE
|
76
|
+
- README.md
|
77
|
+
- Rakefile
|
78
|
+
- lib/time_only.rb
|
79
|
+
- lib/time_only/version.rb
|
80
|
+
- spec/time_only_spec.rb
|
81
|
+
- time_only.gemspec
|
82
|
+
homepage:
|
83
|
+
licenses: []
|
84
|
+
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 3
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
none: false
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
hash: 3
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
version: "0"
|
108
|
+
requirements: []
|
109
|
+
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.8.24
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: A simple class for handling time and only time.
|
115
|
+
test_files:
|
116
|
+
- spec/time_only_spec.rb
|