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
# 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.



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

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



299
300
301
# File 'lib/time_math/units/base.rb', line 299

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

#measure(from, to) ⇒ Integer

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

:nocov:

Raises:

  • (NotImplementedError)


198
199
200
201
# File 'lib/time_math/units/base.rb', line 198

def measure(from, to) # rubocop:disable Lint/UnusedMethodArgument
  raise NotImplementedError,
        '#measure should be implemented in subclasses'
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]


220
221
222
223
# File 'lib/time_math/units/base.rb', line 220

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


170
171
172
# File 'lib/time_math/units/base.rb', line 170

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


187
188
189
# File 'lib/time_math/units/base.rb', line 187

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}


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

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, options = {}) ⇒ Sequence

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

Options Hash (options):

  • :expand (Boolean)

    round sequence ends on creation (from is floored and to is ceiled);

  • :floor (Boolean)

    sequence will be rounding'ing all the intermediate values.



238
239
240
# File 'lib/time_math/units/base.rb', line 238

def sequence(range, options = {})
  TimeMath::Sequence.new(name, range, options)
end