hour-ruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +34 -0
- data/lib/hour.rb +184 -0
- data/spec/hour_spec.rb +110 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b037de6a208ce2bf419a9e1cf5558ecbfc8885e3ee28c58e0f958242c0a75764
|
4
|
+
data.tar.gz: eec358d6872bc3414c6a5789db74a688cc211381aead80b12cad3eca0601b57f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: aa59a5c611500afb23db52439e8f43754f8e857f96dfd6b209352bb57328668421722e7be59268fd19fd6caa50b0d2b291bd5d3e7242555ff03a40885a7c82c5
|
7
|
+
data.tar.gz: c70a2bf14a4f3bddf1e3cdbb69a0c391041549b85d2b26a60806cdd94edbd73511e6629ebb94e740599ff610e1d078b4b5cb68d386a68f6f70d9ef6a6504f315
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# About
|
2
|
+
[![Build status][BS img]][Build status]
|
3
|
+
|
4
|
+
Hour class to work with hours, minutes and seconds, convert between various units and format the output.
|
5
|
+
|
6
|
+
# Installation
|
7
|
+
|
8
|
+
```yaml
|
9
|
+
gem install hour-ruby
|
10
|
+
```
|
11
|
+
|
12
|
+
# Usage
|
13
|
+
|
14
|
+
```crystal
|
15
|
+
require "hour"
|
16
|
+
|
17
|
+
hour = Hour.from(minutes: 85)
|
18
|
+
puts "It's #{hour.hours.value}:#{hour.minutes.value}!"
|
19
|
+
|
20
|
+
hour = Hour.new(1, 25) + Hour.new(s: 10)
|
21
|
+
puts "It's #{hour.to_s}!"
|
22
|
+
|
23
|
+
puts "The system time is #{Hour.now}!"
|
24
|
+
```
|
25
|
+
|
26
|
+
# TODO
|
27
|
+
|
28
|
+
- Rubocop.
|
29
|
+
- Travis.
|
30
|
+
- Link hour-crystal.
|
31
|
+
- Release version 0.1.
|
32
|
+
|
33
|
+
[Build status]: https://travis-ci.org/botanicus/hour-ruby
|
34
|
+
[BS img]: https://travis-ci.org/botanicus/hour-ruby.svg?branch=master
|
data/lib/hour.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Hour
|
4
|
+
# Abstract time unit class.
|
5
|
+
#
|
6
|
+
# Subclasses are decorating the `Hour` class with functionality
|
7
|
+
# specific for their particular type (hours, minutes and seconds).
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Unit
|
11
|
+
def value
|
12
|
+
raise NotImplementedError.new("Override in subclasses.")
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(hour)
|
16
|
+
@hour = hour
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class HourUnit < Unit
|
21
|
+
def value
|
22
|
+
@hour.h
|
23
|
+
end
|
24
|
+
|
25
|
+
def round
|
26
|
+
self.value + ((0..29).include?(@hour.m) ? 0 : 1)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class MinuteUnit < Unit
|
31
|
+
def value
|
32
|
+
@hour.m
|
33
|
+
end
|
34
|
+
|
35
|
+
def round
|
36
|
+
self.value + ((0..29).include?(@hour.s) ? 0 : 1)
|
37
|
+
end
|
38
|
+
|
39
|
+
def total
|
40
|
+
@hour.h * 60 + self.value
|
41
|
+
end
|
42
|
+
|
43
|
+
def round_total
|
44
|
+
self.total + ((0..29).include?(@hour.s) ? 0 : 1)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class SecondUnit < Unit
|
49
|
+
def value
|
50
|
+
@hour.s
|
51
|
+
end
|
52
|
+
|
53
|
+
def total
|
54
|
+
@hour.h * 60 * 60 + @hour.m * 60 + self.value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: Test me and document me.
|
59
|
+
def self.now
|
60
|
+
self.from_time(Time.now)
|
61
|
+
end
|
62
|
+
|
63
|
+
# TODO: document and write tests.
|
64
|
+
def self.from_time(time)
|
65
|
+
self.new(h: time.hours, m: time.minutes, s: time.seconds)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Build an hour instance from an hour string.
|
69
|
+
#
|
70
|
+
# Hour.parse("1:00:00")
|
71
|
+
# Hour.parse("1:00", "%h:%m?") # Will work with "1:00" or just "1".
|
72
|
+
#
|
73
|
+
# TODO: Implement me, test me and document me.
|
74
|
+
def self.parse(string, formatting_string = nil)
|
75
|
+
argument_array = serialised_hour.split(':').map(&:to_i)
|
76
|
+
|
77
|
+
case argument_array.size
|
78
|
+
when 3
|
79
|
+
self.new(*argument_array)
|
80
|
+
when (0..2)
|
81
|
+
# TODO: if formatting_string ...
|
82
|
+
raise ArgumentError.new("If format is not H:M:S, formatting string must be provided.")
|
83
|
+
when (4..Float::INFINITY) # Until we have infinite ranges like (4..) in Ruby 2.6.
|
84
|
+
raise ArgumentError, "Too many arguments."
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Build an hour instance from *either* **minutes** *or* **seconds**.
|
89
|
+
# Unlike `.new`, either of these values can be over 60.
|
90
|
+
#
|
91
|
+
# Hour.from(minutes: 85) # => Hour.new(h: 1, m: 25)
|
92
|
+
# Hour.from(seconds: 120) # => Hour.new(m: 2)
|
93
|
+
def self.from(minutes: 0, seconds: 0)
|
94
|
+
if minutes != 0 && seconds != 0
|
95
|
+
raise ArgumentError.new("Use either minutes OR seconds, not both.")
|
96
|
+
end
|
97
|
+
|
98
|
+
if minutes > 0
|
99
|
+
self.new(h: minutes / 60, m: minutes % 60)
|
100
|
+
else
|
101
|
+
self.from(minutes: seconds / 60) + self.new(s: seconds % 60)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
attr_reader :h, :m, :s
|
106
|
+
|
107
|
+
# Build an hour instance from *h*, *m* and *s*.
|
108
|
+
# Raises an argument error if *m* or *s* is a value over 60.
|
109
|
+
#
|
110
|
+
# For instantiating this class from a *minutes* or *seconds* value over 60, use `.from`.
|
111
|
+
def initialize(*args)
|
112
|
+
if args.length == 1 && args.first.is_a?(Hash)
|
113
|
+
initialize_from_keyword_args(**args.first)
|
114
|
+
else
|
115
|
+
# Pad with 0s.
|
116
|
+
args = args + Array.new(3 - args.length, 0)
|
117
|
+
@h, @m, @s = args
|
118
|
+
end
|
119
|
+
|
120
|
+
if @m > 60
|
121
|
+
raise ArgumentError.new("Minutes must be a number between 0 and 60.")
|
122
|
+
end
|
123
|
+
|
124
|
+
if @s > 60
|
125
|
+
raise ArgumentError.new("Seconds must be a number between 0 and 60.")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns a new Hour instance returning the total time of the two hour instances.
|
130
|
+
#
|
131
|
+
# Hour.new(m: 25, s: 10) + Hour.new(h: 1) # => Hour.new(1, 25, 10)
|
132
|
+
def +(other)
|
133
|
+
hours = @h + other.h + (@m + other.m + ((@s + other.s) / 60)) / 60
|
134
|
+
minutes = (@m + other.m + ((@s + other.s) / 60)) % 60
|
135
|
+
seconds = (@s + other.s) % 60
|
136
|
+
self.class.new(hours, minutes, seconds)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns a decorator providing convenience methods for working with hours.
|
140
|
+
#
|
141
|
+
# Hour.new(1, 25).hours.round # => 1
|
142
|
+
# Hour.new(1, 45).hours.round # => 2
|
143
|
+
def hours
|
144
|
+
HourUnit.new(self)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns a decorator providing convenience methods for working with minutes.
|
148
|
+
#
|
149
|
+
# Hour.new(1, 25, 52).minutes.value # => 25
|
150
|
+
# Hour.new(1, 25, 52).minutes.round # => 26
|
151
|
+
# Hour.new(1, 25, 52).minutes.total # => 85
|
152
|
+
# Hour.new(1, 25, 52).minutes.round_total # => 86
|
153
|
+
def minutes
|
154
|
+
MinuteUnit.new(self)
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns a decorator providing convenience methods for working with seconds.
|
158
|
+
#
|
159
|
+
# Hour.new(m: 1, s: 10).seconds.value # => 10
|
160
|
+
# Hour.new(1, 45, 10 ).seconds.total # => (1 * 60 * 60) + (45 * 60) + 10
|
161
|
+
def seconds
|
162
|
+
SecondUnit.new(self)
|
163
|
+
end
|
164
|
+
|
165
|
+
# TODO: Add formatting string support.
|
166
|
+
# TODO: Pad 0s. I. e. "#{self.hours}:#{format('%02d', self.minutes_over_the_hour)}"
|
167
|
+
def to_s(format = nil)
|
168
|
+
"#{@h}:#{@m}:#{@s}"
|
169
|
+
end
|
170
|
+
|
171
|
+
alias_method :inspect, :to_s
|
172
|
+
|
173
|
+
def to_time(today = Time.now)
|
174
|
+
Time.new(today.year, today.month, today.day, self.hours, self.minutes_over_the_hour)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
def initialize_from_keyword_args(h: 0, m: 0, s: 0)
|
179
|
+
@h, @m, @s = h, m, s
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# https://github.com/botanicus/commonjs_modules
|
184
|
+
export { Hour } if defined?(export)
|
data/spec/hour_spec.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require "hour"
|
2
|
+
|
3
|
+
describe Hour do
|
4
|
+
describe ".parse" do
|
5
|
+
pending "implement me" #do
|
6
|
+
# TODO
|
7
|
+
# p Hour.parse("1:02:30")
|
8
|
+
#end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe ".now" do
|
12
|
+
pending "implement me" #do
|
13
|
+
# TODO
|
14
|
+
#end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#to_s" do
|
18
|
+
pending "implement me" #do
|
19
|
+
# TODO
|
20
|
+
#end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#+" do
|
24
|
+
it "returns a new Hour instance returning the total time of the two hour instances" do
|
25
|
+
hour = Hour.new(m: 25, s: 10) + Hour.new(h: 1)
|
26
|
+
expect(hour.hours.value).to eq(1)
|
27
|
+
expect(hour.minutes.value).to eq(25)
|
28
|
+
expect(hour.seconds.value).to eq(10)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe ".from" do
|
33
|
+
context "minutes" do
|
34
|
+
it "returns a new Hour instance" do
|
35
|
+
hour = Hour.from(minutes: 85)
|
36
|
+
expect(hour.hours.value).to eq(1)
|
37
|
+
expect(hour.minutes.value).to eq(25)
|
38
|
+
expect(hour.seconds.value).to eq(0)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "seconds" do
|
43
|
+
it "returns a new Hour instance" do
|
44
|
+
hour = Hour.from(seconds: 2 * 60 * 60 + 25 * 60 + 5)
|
45
|
+
expect(hour.hours.value).to eq(2)
|
46
|
+
expect(hour.minutes.value).to eq(25)
|
47
|
+
expect(hour.seconds.value).to eq(5)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#hours" do
|
53
|
+
describe "#value" do
|
54
|
+
it "returns the exact hour value" do
|
55
|
+
expect(Hour.new(1, 10).hours.value).to eq(1)
|
56
|
+
expect(Hour.new(1, 59).hours.value).to eq(1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#round" do
|
61
|
+
it "returns the rounded hour" do
|
62
|
+
expect(Hour.new(1, 10).hours.round).to eq(1)
|
63
|
+
expect(Hour.new(1, 29, 59).hours.round).to eq(1)
|
64
|
+
expect(Hour.new(1, 30).hours.round).to eq(2)
|
65
|
+
expect(Hour.new(1, 59).hours.round).to eq(2)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#minutes" do
|
71
|
+
describe "#value" do
|
72
|
+
it "returns the exact minute value" do
|
73
|
+
expect(Hour.new(1, 25, 52).minutes.value).to eq(25)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#round" do
|
78
|
+
it "returns the rounded minutes" do
|
79
|
+
expect(Hour.new(1, 25, 52).minutes.round).to eq(26)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#total" do
|
84
|
+
it "returns the total number of minutes" do
|
85
|
+
expect(Hour.new(1, 25, 52).minutes.total).to eq(85)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#round_total" do
|
90
|
+
it "returns the rounded total number of minutes" do
|
91
|
+
expect(Hour.new(1, 25, 52).minutes.round_total).to eq(86)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#seconds" do
|
97
|
+
describe "#value" do
|
98
|
+
it "returns the exact second value" do
|
99
|
+
expect(Hour.new(m: 1, s: 25).seconds.value).to eq(25)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#total" do
|
104
|
+
it "returns the exact total number of seconds" do
|
105
|
+
expect(Hour.new(m: 1, s: 25).seconds.total).to eq(85)
|
106
|
+
expect(Hour.new(1, 10, 25).seconds.total).to eq(1 * 60 * 60 + 10 * 60 + 25)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hour-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James C Russell
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-21 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: "."
|
14
|
+
email: james@101ideas.cz
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- lib/hour.rb
|
21
|
+
- spec/hour_spec.rb
|
22
|
+
homepage: http://github.com/botanicus/hour-ruby
|
23
|
+
licenses:
|
24
|
+
- MIT
|
25
|
+
metadata:
|
26
|
+
yard.run: yri
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '0'
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubyforge_project:
|
43
|
+
rubygems_version: 2.7.6
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: ''
|
47
|
+
test_files: []
|