hour-ruby 0.0.1
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 +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: []
|