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



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

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



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:

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.

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]

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.



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

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)


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

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)


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}

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.



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

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

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

Parameters:

  • from (Time, Date, DateTime)

    start of sequence;

  • to (Time, Date, DateTime)

    upper limit of sequence;

  • options (Hash) (defaults to: {})

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.

Returns:



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

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