Class: TimeMath::Units::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/time_math/units/base.rb

Overview

It is a main class representing most of TimeMath functionality. It (or rather its descendants) represents "unit of time" and connected calculations logic. Typical usage:

TimeMath.day.advance(tm, 5) # advances tm by 5 days

See also Op for performing multiple operations in concise & DRY manner, like this:

TimeMath().advance(:day, 5).floor(:hour).advance(:min, 20).call(tm)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Base

Creates unit of time. Typically you don't need it, as it is easier to do TimeMath.day or TimeMath[:day] to obtain it.



23
24
25
# File 'lib/time_math/units/base.rb', line 23

def initialize(name)
  @name = name
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



27
28
29
# File 'lib/time_math/units/base.rb', line 27

def name
  @name
end

Instance Method Details

#advance(tm, amount = 1) ⇒ Time, ...

Advances tm by given amount of unit.



139
140
141
142
143
# File 'lib/time_math/units/base.rb', line 139

def advance(tm, amount = 1)
  return decrease(tm, -amount) if amount < 0

  _advance(tm, amount)
end

#ceil(tm, span = 1) ⇒ Time, ...

Rounds tm up to nearest unit (this means, TimeMath.day.ceil(tm) will return beginning of day next after tm, and so on). An optional second argument allows to ceil to arbitrary amount of units (see #floor for more detailed explanation).



66
67
68
69
70
# File 'lib/time_math/units/base.rb', line 66

def ceil(tm, span = 1)
  f = floor(tm, span)

  f == tm ? f : advance(f, span)
end

#decrease(tm, amount = 1) ⇒ Time, ...

Decreases tm by given amount of unit.



153
154
155
156
157
# File 'lib/time_math/units/base.rb', line 153

def decrease(tm, amount = 1)
  return advance(tm, -amount) if amount < 0

  _decrease(tm, amount)
end

#floor(tm, span = 1) ⇒ Time, ...

Rounds tm down to nearest unit (this means, TimeMath.day.floor(tm) will return beginning of tm-s day, and so on).

An optional second argument allows you to floor to arbitrary number of units, like to "each 3-hour" mark:

TimeMath.hour.floor(Time.parse('14:00'), 3)
# => 2016-06-23 12:00:00 +0300

# works well with float/rational spans
TimeMath.hour.floor(Time.parse('14:15'), 1/2r)
# => 2016-06-23 14:00:00 +0300
TimeMath.hour.floor(Time.parse('14:45'), 1/2r)
# => 2016-06-23 14:30:00 +0300


51
52
53
54
# File 'lib/time_math/units/base.rb', line 51

def floor(tm, span = 1)
  int_floor = advance(floor_1(tm), (tm.send(name) / span.to_f).floor * span - tm.send(name))
  float_fix(tm, int_floor, span % 1)
end

#inspectObject



294
295
296
# File 'lib/time_math/units/base.rb', line 294

def inspect
  "#<#{self.class}>"
end

#measure(from, to) ⇒ Integer

Measures distance between from and to in units of this class.

:nocov:



200
201
202
203
# File 'lib/time_math/units/base.rb', line 200

def measure(from, to)
  from, to = from.to_time, to.to_time unless from.class == to.class
  from <= to ? _measure(from, to) : -_measure(to, from)
end

#measure_rem(from, to) ⇒ Array<Integer, Time or DateTime>

Like #measure but also returns "remainder": the time where it would be exactly returned amount of units between from and to:

TimeMath.day.measure(Time.parse('2016-05-01 16:20'), Time.parse('2016-05-28 15:00'))
# => 26
TimeMath.day.measure_rem(Time.parse('2016-05-01 16:20'), Time.parse('2016-05-28 15:00'))
# => [26, 2016-05-27 16:20:00 +0300]


222
223
224
225
# File 'lib/time_math/units/base.rb', line 222

def measure_rem(from, to)
  m = measure(from, to)
  [m, advance(from, m)]
end

#next(tm, span = 1) ⇒ Time, ...

Like #ceil, but always return value greater than tm (e.g. if tm is exactly midnight, then TimeMath.day.next(tm) will return next midnight). An optional second argument allows to ceil to arbitrary amount of units (see #floor for more detailed explanation).



116
117
118
119
# File 'lib/time_math/units/base.rb', line 116

def next(tm, span = 1)
  c = ceil(tm, span)
  c == tm ? advance(c, span) : c
end

#prev(tm, span = 1) ⇒ Time, ...

Like #floor, but always return value lower than tm (e.g. if tm is exactly midnight, then TimeMath.day.prev(tm) will return previous midnight). An optional second argument allows to floor to arbitrary amount of units (see #floor for more detailed explanation).



100
101
102
103
# File 'lib/time_math/units/base.rb', line 100

def prev(tm, span = 1)
  f = floor(tm, span)
  f == tm ? decrease(f, span) : f
end

#range(tm, amount = 1) ⇒ Range

Creates range from tm to tm increased by amount of units.

tm = Time.parse('2016-05-28 16:30')
TimeMath.day.range(tm, 5)
# => 2016-05-28 16:30:00 +0300...2016-06-02 16:30:00 +0300


172
173
174
# File 'lib/time_math/units/base.rb', line 172

def range(tm, amount = 1)
  (tm...advance(tm, amount))
end

#range_back(tm, amount = 1) ⇒ Range

Creates range from tm decreased by amount of units to tm.

tm = Time.parse('2016-05-28 16:30')
TimeMath.day.range_back(tm, 5)
# => 2016-05-23 16:30:00 +0300...2016-05-28 16:30:00 +0300


189
190
191
# File 'lib/time_math/units/base.rb', line 189

def range_back(tm, amount = 1)
  (decrease(tm, amount)...tm)
end

#resample(array_or_hash, symbol = nil, &block) ⇒ Object

Converts input timestamps list to regular list of timestamps over current unit.

Like this:

times = [Time.parse('2016-05-01'), Time.parse('2016-05-03'), Time.parse('2016-05-08')]
TimeMath.day.resample(times)
# =>  => [2016-05-01 00:00:00 +0300, 2016-05-02 00:00:00 +0300, 2016-05-03 00:00:00 +0300, 2016-05-04 00:00:00 +0300, 2016-05-05 00:00:00 +0300, 2016-05-06 00:00:00 +0300, 2016-05-07 00:00:00 +0300, 2016-05-08 00:00:00 +0300]

The best way about resampling it also works for hashes with time keys. Like this:

h = {Date.parse('Wed, 01 Jun 2016')=>1, Date.parse('Tue, 07 Jun 2016')=>3, Date.parse('Thu, 09 Jun 2016')=>1}
# => {#<Date: 2016-06-01>=>1, #<Date: 2016-06-07>=>3, #<Date: 2016-06-09>=>1}

pp TimeMath.day.resample(h)
# {#<Date: 2016-06-01>=>[1],
#  #<Date: 2016-06-02>=>[],
#  #<Date: 2016-06-03>=>[],
#  #<Date: 2016-06-04>=>[],
#  #<Date: 2016-06-05>=>[],
#  #<Date: 2016-06-06>=>[],
#  #<Date: 2016-06-07>=>[3],
#  #<Date: 2016-06-08>=>[],
#  #<Date: 2016-06-09>=>[1]}

# The default resample just groups all related values in arrays
# You can pass block or symbol, to have the values you need:
pp TimeMath.day.resample(h,&:first)
# {#<Date: 2016-06-01>=>1,
#  #<Date: 2016-06-02>=>nil,
#  #<Date: 2016-06-03>=>nil,
#  #<Date: 2016-06-04>=>nil,
#  #<Date: 2016-06-05>=>nil,
#  #<Date: 2016-06-06>=>nil,
#  #<Date: 2016-06-07>=>3,
#  #<Date: 2016-06-08>=>nil,
#  #<Date: 2016-06-09>=>1}


290
291
292
# File 'lib/time_math/units/base.rb', line 290

def resample(array_or_hash, symbol = nil, &block)
  Resampler.call(name, array_or_hash, symbol, &block)
end

#round(tm, span = 1) ⇒ Time, ...

Rounds tm up or down to nearest unit (this means, TimeMath.day.round(tm) will return beginning of tm day if tm is before noon, and day next after tm if it is after, and so on). An optional second argument allows to round to arbitrary amount of units (see #floor for more detailed explanation).



83
84
85
86
87
# File 'lib/time_math/units/base.rb', line 83

def round(tm, span = 1)
  f, c = floor(tm, span), ceil(tm, span)

  (tm - f).abs < (tm - c).abs ? f : c
end

#round?(tm, span = 1) ⇒ Boolean

Checks if tm is exactly rounded to unit.



127
128
129
# File 'lib/time_math/units/base.rb', line 127

def round?(tm, span = 1)
  floor(tm, span) == tm
end

#sequence(range) ⇒ Sequence

Creates Sequence instance for producing all time units between from and too. See Sequence class documentation for detailed functionality description.



233
234
235
# File 'lib/time_math/units/base.rb', line 233

def sequence(range)
  TimeMath::Sequence.new(name, range)
end