time_math2 0.0.3
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/.yardopts +2 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +316 -0
- data/lib/time_math/core_ext.rb +52 -0
- data/lib/time_math/measure.rb +33 -0
- data/lib/time_math/sequence.rb +175 -0
- data/lib/time_math/span.rb +53 -0
- data/lib/time_math/units/base.rb +264 -0
- data/lib/time_math/units/day.rb +34 -0
- data/lib/time_math/units/month.rb +48 -0
- data/lib/time_math/units/simple.rb +58 -0
- data/lib/time_math/units/week.rb +30 -0
- data/lib/time_math/units/year.rb +28 -0
- data/lib/time_math/units.rb +29 -0
- data/lib/time_math/version.rb +4 -0
- data/lib/time_math.rb +99 -0
- data/time_math2.gemspec +39 -0
- metadata +163 -0
@@ -0,0 +1,264 @@
|
|
1
|
+
module TimeMath
|
2
|
+
module Units
|
3
|
+
# It is a main class representing most of TimeMath functionality.
|
4
|
+
# It (or rather its descendants) represents "unit of time" and
|
5
|
+
# connected calculations logic. Typical usage:
|
6
|
+
#
|
7
|
+
# ```ruby
|
8
|
+
# TimeMath.day.advance(tm, 5) # advances tm by 5 days
|
9
|
+
# ```
|
10
|
+
#
|
11
|
+
class Base
|
12
|
+
# Creates unit of time. Typically you don't need it, as it is
|
13
|
+
# easier to do `TimeMath.day` or `TimeMath[:day]` to obtain it.
|
14
|
+
#
|
15
|
+
# @param name [Symbol] one of {TimeMath.units}.
|
16
|
+
def initialize(name)
|
17
|
+
@name = name
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :name
|
21
|
+
|
22
|
+
# Rounds `tm` down to nearest unit (this means, `TimeMath.day.floor(tm)`
|
23
|
+
# will return beginning of `tm`-s day, and so on).
|
24
|
+
#
|
25
|
+
# @param tm [Time,DateTime] time value to floor.
|
26
|
+
# @return [Time,DateTime] floored time value; class and timezone info
|
27
|
+
# of origin would be preserved.
|
28
|
+
def floor(tm)
|
29
|
+
components = [tm.year,
|
30
|
+
tm.month,
|
31
|
+
tm.day,
|
32
|
+
tm.hour,
|
33
|
+
tm.min,
|
34
|
+
tm.sec].first(index + 1)
|
35
|
+
|
36
|
+
new_from_components(tm, *components)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Rounds `tm` up to nearest unit (this means, `TimeMath.day.ceil(tm)`
|
40
|
+
# will return beginning of day next after `tm`, and so on).
|
41
|
+
#
|
42
|
+
# @param tm [Time,DateTime] time value to ceil.
|
43
|
+
# @return [Time,DateTime] ceiled time value; class and timezone info
|
44
|
+
# of origin would be preserved.
|
45
|
+
def ceil(tm)
|
46
|
+
f = floor(tm)
|
47
|
+
|
48
|
+
f == tm ? f : advance(f)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Rounds `tm` up or down to nearest unit (this means, `TimeMath.day.round(tm)`
|
52
|
+
# will return beginning of `tm` day if `tm` is before noon, and
|
53
|
+
# day next after `tm` if it is after, and so on).
|
54
|
+
#
|
55
|
+
# @param tm [Time,DateTime] time value to round.
|
56
|
+
# @return [Time,DateTime] rounded time value; class and timezone info
|
57
|
+
# of origin would be preserved.
|
58
|
+
def round(tm)
|
59
|
+
f, c = floor(tm), ceil(tm)
|
60
|
+
|
61
|
+
(tm - f).abs < (tm - c).abs ? f : c
|
62
|
+
end
|
63
|
+
|
64
|
+
# Like {#floor}, but always return value lower than `tm` (e.g. if
|
65
|
+
# `tm` is exactly midnight, then `TimeMath.day.prev(tm)` will return
|
66
|
+
# _previous midnight_).
|
67
|
+
#
|
68
|
+
# @param tm [Time,DateTime] time value to calculate prev on.
|
69
|
+
# @return [Time,DateTime] prev time value; class and timezone info
|
70
|
+
# of origin would be preserved.
|
71
|
+
def prev(tm)
|
72
|
+
f = floor(tm)
|
73
|
+
f == tm ? decrease(f) : f
|
74
|
+
end
|
75
|
+
|
76
|
+
# Like {#ceil}, but always return value greater than `tm` (e.g. if
|
77
|
+
# `tm` is exactly midnight, then `TimeMath.day.next(tm)` will return
|
78
|
+
# _next midnight_).
|
79
|
+
#
|
80
|
+
# @param tm [Time,DateTime] time value to calculate next on.
|
81
|
+
# @return [Time,DateTime] next time value; class and timezone info
|
82
|
+
# of origin would be preserved.
|
83
|
+
def next(tm)
|
84
|
+
c = ceil(tm)
|
85
|
+
c == tm ? advance(c) : c
|
86
|
+
end
|
87
|
+
|
88
|
+
# Checks if `tm` is exactly rounded to unit.
|
89
|
+
#
|
90
|
+
# @param tm [Time,DateTime] time value to check.
|
91
|
+
# @return [Boolean] whether `tm` is exactly round to unit.
|
92
|
+
def round?(tm)
|
93
|
+
floor(tm) == tm
|
94
|
+
end
|
95
|
+
|
96
|
+
# Advances `tm` by given amount of unit.
|
97
|
+
#
|
98
|
+
# @param tm [Time,DateTime] time value to advance;
|
99
|
+
# @param amount [Integer] how many units forward to go.
|
100
|
+
#
|
101
|
+
# @return [Time,DateTime] advanced time value; class and timezone info
|
102
|
+
# of origin would be preserved.
|
103
|
+
def advance(tm, amount = 1)
|
104
|
+
return decrease(tm, -amount) if amount < 0
|
105
|
+
_advance(tm, amount)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Decreases `tm` by given amount of unit.
|
109
|
+
#
|
110
|
+
# @param tm [Time,DateTime] time value to decrease;
|
111
|
+
# @param amount [Integer] how many units forward to go.
|
112
|
+
#
|
113
|
+
# @return [Time,DateTime] decrease time value; class and timezone info
|
114
|
+
# of origin would be preserved.
|
115
|
+
def decrease(tm, amount = 1)
|
116
|
+
return advance(tm, -amount) if amount < 0
|
117
|
+
_decrease(tm, amount)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Creates range from `tm` to `tm` increased by amount of units.
|
121
|
+
#
|
122
|
+
# ```ruby
|
123
|
+
# tm = Time.parse('2016-05-28 16:30')
|
124
|
+
# TimeMath.day.range(tm, 5)
|
125
|
+
# # => 2016-05-28 16:30:00 +0300...2016-06-02 16:30:00 +0300
|
126
|
+
# ```
|
127
|
+
#
|
128
|
+
# @param tm [Time,DateTime] time value to create range from;
|
129
|
+
# @param amount [Integer] how many units should be between range
|
130
|
+
# start and end.
|
131
|
+
#
|
132
|
+
# @return [Range]
|
133
|
+
def range(tm, amount = 1)
|
134
|
+
(tm...advance(tm, amount))
|
135
|
+
end
|
136
|
+
|
137
|
+
# Creates range from `tm` decreased by amount of units to `tm`.
|
138
|
+
#
|
139
|
+
# ```ruby
|
140
|
+
# tm = Time.parse('2016-05-28 16:30')
|
141
|
+
# TimeMath.day.range_back(tm, 5)
|
142
|
+
# # => 2016-05-23 16:30:00 +0300...2016-05-28 16:30:00 +0300
|
143
|
+
# ```
|
144
|
+
#
|
145
|
+
# @param tm [Time,DateTime] time value to create range from;
|
146
|
+
# @param amount [Integer] how many units should be between range
|
147
|
+
# start and end.
|
148
|
+
#
|
149
|
+
# @return [Range]
|
150
|
+
def range_back(tm, amount = 1)
|
151
|
+
(decrease(tm, amount)...tm)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Measures distance between `from` and `to` in units of this class.
|
155
|
+
#
|
156
|
+
# @param from [Time,DateTime] start of period;
|
157
|
+
# @param to [Time,DateTime] end of period.
|
158
|
+
#
|
159
|
+
# @return [Integer] how many full units are inside the period.
|
160
|
+
# :nocov:
|
161
|
+
def measure(from, to) # rubocop:disable Lint/UnusedMethodArgument
|
162
|
+
raise NotImplementedError,
|
163
|
+
'#measure should be implemented in subclasses'
|
164
|
+
end
|
165
|
+
# :nocov:
|
166
|
+
|
167
|
+
# Like {#measure} but also returns "remainder": the time where
|
168
|
+
# it would be **exactly** returned amount of units between `from`
|
169
|
+
# and `to`:
|
170
|
+
#
|
171
|
+
# ```ruby
|
172
|
+
# TimeMath.day.measure(Time.parse('2016-05-01 16:20'), Time.parse('2016-05-28 15:00'))
|
173
|
+
# # => 26
|
174
|
+
# TimeMath.day.measure_rem(Time.parse('2016-05-01 16:20'), Time.parse('2016-05-28 15:00'))
|
175
|
+
# # => [26, 2016-05-27 16:20:00 +0300]
|
176
|
+
# ```
|
177
|
+
#
|
178
|
+
# @param from [Time,DateTime] start of period;
|
179
|
+
# @param to [Time,DateTime] end of period.
|
180
|
+
#
|
181
|
+
# @return [Array<Integer, Time or DateTime>] how many full units
|
182
|
+
# are inside the period; exact value of `from` + full units.
|
183
|
+
def measure_rem(from, to)
|
184
|
+
m = measure(from, to)
|
185
|
+
[m, advance(from, m)]
|
186
|
+
end
|
187
|
+
|
188
|
+
# Creates {Span} instance representing amount of units.
|
189
|
+
#
|
190
|
+
# Use it like this:
|
191
|
+
#
|
192
|
+
# ```ruby
|
193
|
+
# span = TimeMath.day.span(5) # => #<TimeMath::Span(day): +5>
|
194
|
+
# # now you can save this variable or path it to the methods...
|
195
|
+
# # and then:
|
196
|
+
# span.before(Time.parse('2016-05-01')) # => 2016-04-26 00:00:00 +0300
|
197
|
+
# span.after(Time.parse('2016-05-01')) # => 2016-05-06 00:00:00 +0300
|
198
|
+
# ```
|
199
|
+
#
|
200
|
+
# @param amount [Integer]
|
201
|
+
# @return [Span]
|
202
|
+
def span(amount = 1)
|
203
|
+
TimeMath::Span.new(name, amount)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Creates {Sequence} instance for producing all time units between
|
207
|
+
# from and too. See {Sequence} class documentation for available
|
208
|
+
# options and functionality.
|
209
|
+
#
|
210
|
+
# @param from [Time,DateTime] start of sequence;
|
211
|
+
# @param to [Time,DateTime] upper limit of sequence;
|
212
|
+
# @param options [Hash]
|
213
|
+
# @option options [Boolean] :expand round sequence ends on creation
|
214
|
+
# (from is floored and to is ceiled);
|
215
|
+
# @option options [Boolean] :floor sequence will be rounding'ing all
|
216
|
+
# the intermediate values.
|
217
|
+
#
|
218
|
+
# @return [Sequence]
|
219
|
+
def sequence(from, to, options = {})
|
220
|
+
TimeMath::Sequence.new(name, from, to, options)
|
221
|
+
end
|
222
|
+
|
223
|
+
def inspect
|
224
|
+
"#<#{self.class}>"
|
225
|
+
end
|
226
|
+
|
227
|
+
protected
|
228
|
+
|
229
|
+
# all except :week
|
230
|
+
NATURAL_UNITS = [:year, :month, :day, :hour, :min, :sec].freeze
|
231
|
+
EMPTY_VALUES = [nil, 1, 1, 0, 0, 0].freeze
|
232
|
+
|
233
|
+
def index
|
234
|
+
NATURAL_UNITS.index(name) or
|
235
|
+
raise NotImplementedError, "Can not be used for #{name}"
|
236
|
+
end
|
237
|
+
|
238
|
+
def generate(tm, replacements = {})
|
239
|
+
hash_to_tm(tm, tm_to_hash(tm).merge(replacements))
|
240
|
+
end
|
241
|
+
|
242
|
+
def tm_to_hash(tm)
|
243
|
+
Hash[*NATURAL_UNITS.flat_map { |s| [s, tm.send(s)] }]
|
244
|
+
end
|
245
|
+
|
246
|
+
def hash_to_tm(origin, hash)
|
247
|
+
components = NATURAL_UNITS.map { |s| hash[s] || 0 }
|
248
|
+
new_from_components(origin, *components)
|
249
|
+
end
|
250
|
+
|
251
|
+
def new_from_components(origin, *components)
|
252
|
+
components = EMPTY_VALUES.zip(components).map { |d, c| c || d }
|
253
|
+
case origin
|
254
|
+
when Time
|
255
|
+
Time.mktime(*components.reverse, nil, nil, nil, origin.zone)
|
256
|
+
when DateTime
|
257
|
+
DateTime.new(*components, origin.zone)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
include TimeMath # now we can use something like #day inside other units
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module TimeMath
|
2
|
+
module Units
|
3
|
+
# @private
|
4
|
+
class Day < Simple
|
5
|
+
def initialize
|
6
|
+
super(:day)
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def _advance(tm, steps)
|
12
|
+
fix_dst(super(tm, steps), tm)
|
13
|
+
end
|
14
|
+
|
15
|
+
def _decrease(tm, steps)
|
16
|
+
fix_dst(super(tm, steps), tm)
|
17
|
+
end
|
18
|
+
|
19
|
+
# :nocov: - somehow Travis env thinks other things about DST
|
20
|
+
def fix_dst(res, src)
|
21
|
+
return res unless res.is_a?(Time)
|
22
|
+
|
23
|
+
if res.dst? && !src.dst?
|
24
|
+
hour.decrease(res)
|
25
|
+
elsif !res.dst? && src.dst?
|
26
|
+
hour.advance(res)
|
27
|
+
else
|
28
|
+
res
|
29
|
+
end
|
30
|
+
end
|
31
|
+
# :nocov:
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TimeMath
|
3
|
+
module Units
|
4
|
+
# @private
|
5
|
+
class Month < Base
|
6
|
+
def initialize
|
7
|
+
super(:month)
|
8
|
+
end
|
9
|
+
|
10
|
+
def measure(from, to)
|
11
|
+
ydiff = to.year - from.year
|
12
|
+
mdiff = to.month - from.month
|
13
|
+
|
14
|
+
to.day >= from.day ? (ydiff * 12 + mdiff) : (ydiff * 12 + mdiff - 1)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def succ(tm)
|
20
|
+
return generate(tm, year: tm.year + 1, month: 1) if tm.month == 12
|
21
|
+
|
22
|
+
t = generate(tm, month: tm.month + 1)
|
23
|
+
fix_month(t, t.month + 1)
|
24
|
+
end
|
25
|
+
|
26
|
+
def prev(tm)
|
27
|
+
return generate(tm, year: tm.year - 1, month: 12) if tm.month == 1
|
28
|
+
|
29
|
+
t = generate(tm, month: tm.month - 1)
|
30
|
+
fix_month(t, t.month - 1)
|
31
|
+
end
|
32
|
+
|
33
|
+
def _advance(tm, steps)
|
34
|
+
steps.times.inject(tm) { |t| succ(t) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def _decrease(tm, steps)
|
38
|
+
steps.times.inject(tm) { |t| prev(t) }
|
39
|
+
end
|
40
|
+
|
41
|
+
# fix for too far advance/insufficient decrease:
|
42
|
+
# Time.new(2013,2,31) #=> 2013-03-02 00:00:00 +0200
|
43
|
+
def fix_month(t, expected)
|
44
|
+
t.month == expected ? day.decrease(t, t.day) : t
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module TimeMath
|
2
|
+
module Units
|
3
|
+
# @private
|
4
|
+
class Simple < Base
|
5
|
+
def to_seconds(sz = 1)
|
6
|
+
sz * MULTIPLIERS[index..-1].inject(:*)
|
7
|
+
end
|
8
|
+
|
9
|
+
def measure(from, to)
|
10
|
+
((to.to_time - from.to_time) / to_seconds).to_i
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def _advance(tm, steps)
|
16
|
+
_shift(tm, to_seconds(steps))
|
17
|
+
end
|
18
|
+
|
19
|
+
def _decrease(tm, steps)
|
20
|
+
_shift(tm, -to_seconds(steps))
|
21
|
+
end
|
22
|
+
|
23
|
+
def _shift(tm, seconds)
|
24
|
+
case tm
|
25
|
+
when Time
|
26
|
+
tm + seconds
|
27
|
+
when DateTime
|
28
|
+
tm + Rational(seconds, 86_400)
|
29
|
+
else
|
30
|
+
raise ArgumentError, "Expected Time or DateTime, got #{tm.class}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
MULTIPLIERS = [12, 30, 24, 60, 60, 1].freeze
|
35
|
+
end
|
36
|
+
|
37
|
+
# @private
|
38
|
+
class Sec < Simple
|
39
|
+
def initialize
|
40
|
+
super(:sec)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @private
|
45
|
+
class Min < Simple
|
46
|
+
def initialize
|
47
|
+
super(:min)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @private
|
52
|
+
class Hour < Simple
|
53
|
+
def initialize
|
54
|
+
super(:hour)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module TimeMath
|
2
|
+
module Units
|
3
|
+
# @private
|
4
|
+
class Week < Simple
|
5
|
+
def initialize
|
6
|
+
super(:week)
|
7
|
+
end
|
8
|
+
|
9
|
+
def floor(tm)
|
10
|
+
f = day.floor(tm)
|
11
|
+
extra_days = tm.wday == 0 ? 6 : tm.wday - 1
|
12
|
+
day.decrease(f, extra_days)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_seconds(sz = 1)
|
16
|
+
day.to_seconds(sz * 7)
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def _advance(tm, steps)
|
22
|
+
day.advance(tm, steps * 7)
|
23
|
+
end
|
24
|
+
|
25
|
+
def _decrease(tm, steps)
|
26
|
+
day.decrease(tm, steps * 7)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module TimeMath
|
2
|
+
module Units
|
3
|
+
# @private
|
4
|
+
class Year < Base
|
5
|
+
def initialize
|
6
|
+
super(:year)
|
7
|
+
end
|
8
|
+
|
9
|
+
def measure(from, to)
|
10
|
+
if generate(from, year: to.year) < to
|
11
|
+
to.year - from.year
|
12
|
+
else
|
13
|
+
to.year - from.year - 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def _advance(tm, steps)
|
20
|
+
generate(tm, year: tm.year + steps)
|
21
|
+
end
|
22
|
+
|
23
|
+
def _decrease(tm, steps)
|
24
|
+
generate(tm, year: tm.year - steps)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'units/base'
|
2
|
+
require_relative 'units/simple'
|
3
|
+
require_relative 'units/day'
|
4
|
+
require_relative 'units/week'
|
5
|
+
require_relative 'units/month'
|
6
|
+
require_relative 'units/year'
|
7
|
+
|
8
|
+
module TimeMath
|
9
|
+
# See {Units::Base} for detailed description of all units functionality.
|
10
|
+
module Units
|
11
|
+
# @private
|
12
|
+
UNITS = {
|
13
|
+
sec: Units::Sec.new, min: Units::Min.new, hour: Units::Hour.new,
|
14
|
+
day: Units::Day.new, week: Units::Week.new, month: Units::Month.new,
|
15
|
+
year: Units::Year.new
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
# @private
|
19
|
+
def self.names
|
20
|
+
UNITS.keys
|
21
|
+
end
|
22
|
+
|
23
|
+
# @private
|
24
|
+
def self.get(name)
|
25
|
+
UNITS[name] or
|
26
|
+
raise ArgumentError, "Unsupported unit: #{name}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/time_math.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
require_relative './time_math/units'
|
4
|
+
require_relative './time_math/sequence'
|
5
|
+
require_relative './time_math/measure'
|
6
|
+
require_relative './time_math/span'
|
7
|
+
|
8
|
+
# TimeMath is a small library for easy time units arithmetics (like "floor
|
9
|
+
# the timestamp to the nearest hour", "advance the time value by 3 days"
|
10
|
+
# and so on).
|
11
|
+
#
|
12
|
+
# It has clean and easy-to-remember API, just like this:
|
13
|
+
#
|
14
|
+
# ```ruby
|
15
|
+
# TimeMath.day.floor(Time.now)
|
16
|
+
# # or
|
17
|
+
# TimeMath[:day].floor(Time.now)
|
18
|
+
# ```
|
19
|
+
#
|
20
|
+
# `TimeMath[unit]` and `TimeMath.<unit>` give you an instance of
|
21
|
+
# time unit, which incapsulates most of the functionality. Refer to
|
22
|
+
# {Units::Base} to see what you can get of it.
|
23
|
+
#
|
24
|
+
module TimeMath
|
25
|
+
# rubocop:disable Style/ModuleFunction
|
26
|
+
extend self
|
27
|
+
# rubocop:enable Style/ModuleFunction
|
28
|
+
|
29
|
+
# List all unit names known.
|
30
|
+
#
|
31
|
+
# @return [Array<Symbol>]
|
32
|
+
def units
|
33
|
+
Units.names
|
34
|
+
end
|
35
|
+
|
36
|
+
# Main method to do something with TimeMath. Returns an object
|
37
|
+
# representing some time measurement unit. See {Units::Base} documentation
|
38
|
+
# to know what you can do with it.
|
39
|
+
#
|
40
|
+
# @return [Units::Base]
|
41
|
+
def [](unit)
|
42
|
+
Units.get(unit)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!method sec
|
46
|
+
# Shortcut to get second unit.
|
47
|
+
# @return [Units::Base]
|
48
|
+
#
|
49
|
+
# @!method min
|
50
|
+
# Shortcut to get minute unit.
|
51
|
+
# @return [Units::Base]
|
52
|
+
#
|
53
|
+
# @!method hour
|
54
|
+
# Shortcut to get hour unit.
|
55
|
+
# @return [Units::Base]
|
56
|
+
#
|
57
|
+
# @!method day
|
58
|
+
# Shortcut to get day unit.
|
59
|
+
# @return [Units::Base]
|
60
|
+
#
|
61
|
+
# @!method week
|
62
|
+
# Shortcut to get week unit.
|
63
|
+
# @return [Units::Base]
|
64
|
+
#
|
65
|
+
# @!method month
|
66
|
+
# Shortcut to get month unit.
|
67
|
+
# @return [Units::Base]
|
68
|
+
#
|
69
|
+
# @!method year
|
70
|
+
# Shortcut to get year unit.
|
71
|
+
# @return [Units::Base]
|
72
|
+
#
|
73
|
+
Units.names.each do |unit|
|
74
|
+
define_method(unit) { Units.get(unit) }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Measures distance between two time values in all units at once.
|
78
|
+
#
|
79
|
+
# Just like this:
|
80
|
+
#
|
81
|
+
# ```ruby
|
82
|
+
# birthday = Time.parse('1983-02-14 13:30')
|
83
|
+
#
|
84
|
+
# TimeMath.measure(birthday, Time.now)
|
85
|
+
# # => {:years=>33, :months=>3, :weeks=>2, :days=>0, :hours=>1, :minutes=>25, :seconds=>52}
|
86
|
+
# ```
|
87
|
+
#
|
88
|
+
# @param from [Time,DateTime]
|
89
|
+
# @param to [Time,DateTime]
|
90
|
+
# @param options [Hash] options
|
91
|
+
# @option options [Boolean] :weeks pass `false` to exclude weeks from calculation;
|
92
|
+
# @option options [Symbol] :upto pass max unit to use (e.g. if you'll
|
93
|
+
# pass `:day`, period would be measured in days, hours, minutes and seconds).
|
94
|
+
#
|
95
|
+
# @return [Hash]
|
96
|
+
def measure(from, to, options = {})
|
97
|
+
Measure.measure(from, to, options)
|
98
|
+
end
|
99
|
+
end
|
data/time_math2.gemspec
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require './lib/time_math/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'time_math2'
|
5
|
+
s.version = TimeMath::VERSION
|
6
|
+
s.authors = ['Victor Shepelev']
|
7
|
+
s.email = 'zverok.offline@gmail.com'
|
8
|
+
s.homepage = 'https://github.com/zverok/time_math2'
|
9
|
+
|
10
|
+
s.summary = 'Easy time math'
|
11
|
+
s.description = <<-EOF
|
12
|
+
TimeMath is small, no-dependencies library attemting to make work with
|
13
|
+
time units easier. It provides you with simple, easy remembered API, without
|
14
|
+
any monkey patching of core Ruby classes, so it can be used alongside
|
15
|
+
Rails or without it, for any purpose.
|
16
|
+
EOF
|
17
|
+
s.licenses = ['MIT']
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split($RS).reject do |file|
|
20
|
+
file =~ /^(?:
|
21
|
+
spec\/.*
|
22
|
+
|Gemfile
|
23
|
+
|Rakefile
|
24
|
+
|\.rspec
|
25
|
+
|\.gitignore
|
26
|
+
|\.rubocop.yml
|
27
|
+
|\.travis.yml
|
28
|
+
)$/x
|
29
|
+
end
|
30
|
+
s.require_paths = ["lib"]
|
31
|
+
|
32
|
+
s.add_development_dependency 'rubocop', '>= 0.30'
|
33
|
+
s.add_development_dependency 'rspec', '>= 3'
|
34
|
+
s.add_development_dependency 'rspec-its', '~> 1'
|
35
|
+
s.add_development_dependency 'simplecov', '~> 0.9'
|
36
|
+
s.add_development_dependency 'rake'
|
37
|
+
s.add_development_dependency 'rubygems-tasks'
|
38
|
+
s.add_development_dependency 'yard'
|
39
|
+
end
|