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.

Parameters:


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.

Parameters:

  • tm (Time, Date, DateTime)

    time value to advance;

  • amount (Numeric) (defaults to: 1)

    how many units forward to go. For units less than week supports float/rational values.

Returns:

  • (Time, Date, DateTime)

    advanced time value; class and timezone offset of origin would be preserved.


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

Parameters:

  • tm (Time, Date, DateTime)

    time value to ceil.

  • span (Numeric) (defaults to: 1)

    how many units to ceil to. For units less than week supports float/rational values.

Returns:

  • (Time, Date, DateTime)

    ceiled time value; class and timezone offset of origin would be preserved.


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.

Parameters:

  • tm (Time, Date, DateTime)

    time value to decrease;

  • amount (Integer) (defaults to: 1)

    how many units forward to go. For units less than week supports float/rational values.

Returns:

  • (Time, Date, DateTime)

    decrease time value; class and timezone offset of origin would be preserved.


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

Parameters:

  • tm (Time, Date, DateTime)

    time value to floor.

  • span (Numeric) (defaults to: 1)

    how many units to floor to. For units less than week supports float/rational values.

Returns:

  • (Time, Date, DateTime)

    floored time value; class and timezone offset of origin would be preserved.


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:

Parameters:

  • from (Time, Date, DateTime)

    start of period;

  • to (Time, Date, DateTime)

    end of period.

Returns:

  • (Integer)

    how many full units are inside the period.


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]

Parameters:

  • from (Time, Date, DateTime)

    start of period;

  • to (Time, Date, DateTime)

    end of period.

Returns:

  • (Array<Integer, Time or DateTime>)

    how many full units are inside the period; exact value of from + full units.


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

Parameters:

  • tm (Time, Date, DateTime)

    time value to calculate next on.

  • span (Numeric) (defaults to: 1)

    how many units to ceil to. For units less than week supports float/rational values.

Returns:

  • (Time, Date, DateTime)

    next time value; class and timezone offset of origin would be preserved.


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

Parameters:

  • tm (Time, Date, DateTime)

    time value to calculate prev on.

  • span (Numeric) (defaults to: 1)

    how many units to floor to. For units less than week supports float/rational values.

Returns:

  • (Time, Date, DateTime)

    prev time value; class and timezone offset of origin would be preserved.


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

Parameters:

  • tm (Time, Date, DateTime)

    time value to create range from;

  • amount (Integer) (defaults to: 1)

    how many units should be between range start and end.

Returns:

  • (Range)

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

Parameters:

  • tm (Time, Date, DateTime)

    time value to create range from;

  • amount (Integer) (defaults to: 1)

    how many units should be between range start and end.

Returns:

  • (Range)

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}

Parameters:

  • array_or_hash

    array of time-y values (Time/Date/DateTime) or hash with time-y keys.

  • symbol (defaults to: nil)

    in case of first param being a hash -- method to call on key arrays while grouping.

  • block

    in case of first param being a hash -- block to call on key arrays while grouping.

Returns:

  • array or hash spread regular by unit; if first param was hash, keys corresponding to each period are grouped into arrays; this array could be further processed with block/symbol provided.


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

Parameters:

  • tm (Time, Date, DateTime)

    time value to round.

  • span (Numeric) (defaults to: 1)

    how many units to round to. For units less than week supports float/rational values.

Returns:

  • (Time, Date, DateTime)

    rounded time value; class and timezone offset of origin would be preserved.


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.

Parameters:

  • tm (Time, Date, DateTime)

    time value to check.

  • span (Numeric) (defaults to: 1)

    how many units to check round at. For units less than week supports float/rational values.

Returns:

  • (Boolean)

    whether tm is exactly round 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.

Parameters:

  • range (Range<Time,Date,DateTime>)

    start and end of sequence.

Returns:


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

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