time_math2 0.0.4 → 0.0.5
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 +4 -4
- data/CHANGELOG.md +16 -3
- data/README.md +135 -40
- data/lib/time_math.rb +50 -11
- data/lib/time_math/backports.rb +8 -0
- data/lib/time_math/op.rb +209 -0
- data/lib/time_math/resamplers.rb +76 -0
- data/lib/time_math/sequence.rb +159 -49
- data/lib/time_math/units/base.rb +184 -70
- data/lib/time_math/units/day.rb +2 -2
- data/lib/time_math/units/month.rb +4 -4
- data/lib/time_math/units/simple.rb +1 -1
- data/lib/time_math/units/week.rb +9 -6
- data/lib/time_math/units/year.rb +2 -2
- data/lib/time_math/util.rb +10 -0
- data/lib/time_math/version.rb +1 -1
- metadata +6 -4
- data/lib/time_math/core_ext.rb +0 -52
- data/lib/time_math/span.rb +0 -53
| @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            module TimeMath
         | 
| 2 | 
            +
              # @private
         | 
| 3 | 
            +
              class Resampler
         | 
| 4 | 
            +
                class << self
         | 
| 5 | 
            +
                  def call(name, array_or_hash, symbol = nil, &block)
         | 
| 6 | 
            +
                    if array_or_hash.is_a?(Array) && array_or_hash.all?(&Util.method(:timey?))
         | 
| 7 | 
            +
                      ArrayResampler.new(name, array_or_hash).call
         | 
| 8 | 
            +
                    elsif array_or_hash.is_a?(Hash) && array_or_hash.keys.all?(&Util.method(:timey?))
         | 
| 9 | 
            +
                      HashResampler.new(name, array_or_hash).call(symbol, &block)
         | 
| 10 | 
            +
                    else
         | 
| 11 | 
            +
                      raise ArgumentError, "Array of timestamps or hash with timestamp keys, #{array_or_hash} got"
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initialize(unit)
         | 
| 17 | 
            +
                  @unit = Units.get(unit)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                private
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def sequence
         | 
| 23 | 
            +
                  @sequence ||= @unit.sequence(from...to, expand: true)
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def from
         | 
| 27 | 
            +
                  timestamps.min
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def to
         | 
| 31 | 
            +
                  @unit.next(timestamps.max)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              # @private
         | 
| 36 | 
            +
              class ArrayResampler < Resampler
         | 
| 37 | 
            +
                def initialize(unit, array)
         | 
| 38 | 
            +
                  super(unit)
         | 
| 39 | 
            +
                  @array = array
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def call
         | 
| 43 | 
            +
                  sequence.to_a
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                private
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def timestamps
         | 
| 49 | 
            +
                  @array
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              # @private
         | 
| 54 | 
            +
              class HashResampler < Resampler
         | 
| 55 | 
            +
                def initialize(unit, hash)
         | 
| 56 | 
            +
                  super(unit)
         | 
| 57 | 
            +
                  @hash = hash
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def call(symbol = nil, &block)
         | 
| 61 | 
            +
                  block = symbol.to_proc if symbol && !block
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  sequence.ranges.map do |r|
         | 
| 64 | 
            +
                    values = @hash.select { |k, _| r.cover?(k) }.map(&:last)
         | 
| 65 | 
            +
                    values = block.call(values) if block # rubocop:disable Performance/RedundantBlockCall
         | 
| 66 | 
            +
                    [r.begin, values]
         | 
| 67 | 
            +
                  end.to_h
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                private
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                def timestamps
         | 
| 73 | 
            +
                  @hash.keys
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
            end
         | 
    
        data/lib/time_math/sequence.rb
    CHANGED
    
    | @@ -8,27 +8,18 @@ module TimeMath | |
| 8 8 | 
             
              # ```ruby
         | 
| 9 9 | 
             
              # from = Time.parse('2016-05-01 13:30')
         | 
| 10 10 | 
             
              # to = Time.parse('2016-05-04 18:20')
         | 
| 11 | 
            -
              # seq = TimeMath.day.sequence(from | 
| 12 | 
            -
              # # => #<TimeMath::Sequence(2016-05-01 13:30:00 +0300 | 
| 11 | 
            +
              # seq = TimeMath.day.sequence(from...to)
         | 
| 12 | 
            +
              # # => #<TimeMath::Sequence(2016-05-01 13:30:00 +0300...2016-05-04 18:20:00 +0300)>
         | 
| 13 13 | 
             
              # ```
         | 
| 14 14 | 
             
              #
         | 
| 15 15 | 
             
              # Now, you can use it:
         | 
| 16 | 
            +
              #
         | 
| 16 17 | 
             
              # ```ruby
         | 
| 17 18 | 
             
              # seq.to_a
         | 
| 18 19 | 
             
              # # => [2016-05-01 13:30:00 +0300, 2016-05-02 13:30:00 +0300, 2016-05-03 13:30:00 +0300, 2016-05-04 13:30:00 +0300]
         | 
| 19 20 | 
             
              # ```
         | 
| 20 21 | 
             
              # -- it's an "each day start between from and to". As you can see,
         | 
| 21 | 
            -
              # the period start is the same as in `from`. | 
| 22 | 
            -
              # them to beginning of day with {#floor} method or `:floor` option:
         | 
| 23 | 
            -
              #
         | 
| 24 | 
            -
              # ```ruby
         | 
| 25 | 
            -
              # seq.floor.to_a
         | 
| 26 | 
            -
              # # => [2016-05-01 13:30:00 +0300, 2016-05-02 00:00:00 +0300, 2016-05-03 00:00:00 +0300, 2016-05-04 00:00:00 +0300]
         | 
| 27 | 
            -
              # # or:
         | 
| 28 | 
            -
              # seq = TimeMath.day.sequence(from, to, floor: true)
         | 
| 29 | 
            -
              # seq.to_a
         | 
| 30 | 
            -
              # ```
         | 
| 31 | 
            -
              # -- it floors all day starts except of `from`, which is preserved.
         | 
| 22 | 
            +
              # the period start is the same as in `from`.
         | 
| 32 23 | 
             
              #
         | 
| 33 24 | 
             
              # You can expand from and to to nearest round unit by {#expand} method
         | 
| 34 25 | 
             
              # or `:expand` option:
         | 
| @@ -37,16 +28,23 @@ module TimeMath | |
| 37 28 | 
             
              # seq.expand.to_a
         | 
| 38 29 | 
             
              # # => [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]
         | 
| 39 30 | 
             
              # # or:
         | 
| 40 | 
            -
              # seq = TimeMath.day.sequence(from | 
| 41 | 
            -
              # # => #<TimeMath::Sequence(2016-05-01 00:00:00 +0300 | 
| 31 | 
            +
              # seq = TimeMath.day.sequence(from...to, expand: true)
         | 
| 32 | 
            +
              # # => #<TimeMath::Sequence(2016-05-01 00:00:00 +0300...2016-05-05 00:00:00 +0300)>
         | 
| 33 | 
            +
              # seq.to_a
         | 
| 34 | 
            +
              # # => [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]
         | 
| 35 | 
            +
              # # ^ note that `to` is excluded.
         | 
| 36 | 
            +
              # # You can include it by creating sequence from including-end range:
         | 
| 37 | 
            +
              # seq = TimeMath.day.sequence(from..to, expand: true)
         | 
| 38 | 
            +
              # # => #<TimeMath::Sequence(:day, 2016-05-01 00:00:00 +0300..2016-05-05 00:00:00 +0300)>
         | 
| 42 39 | 
             
              # seq.to_a
         | 
| 40 | 
            +
              # # => [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]
         | 
| 43 41 | 
             
              # ```
         | 
| 44 42 | 
             
              #
         | 
| 45 43 | 
             
              # Besides each period beginning, you can also request pairs of begin/end
         | 
| 46 44 | 
             
              # of a period, either as an array of arrays, or array of ranges:
         | 
| 47 45 | 
             
              #
         | 
| 48 46 | 
             
              # ```ruby
         | 
| 49 | 
            -
              # seq = TimeMath.day.sequence(from | 
| 47 | 
            +
              # seq = TimeMath.day.sequence(from...to)
         | 
| 50 48 | 
             
              # seq.pairs
         | 
| 51 49 | 
             
              # # => [[2016-05-01 13:30:00 +0300, 2016-05-02 13:30:00 +0300], [2016-05-02 13:30:00 +0300, 2016-05-03 13:30:00 +0300], [2016-05-03 13:30:00 +0300, 2016-05-04 13:30:00 +0300], [2016-05-04 13:30:00 +0300, 2016-05-04 18:20:00 +0300]]
         | 
| 52 50 | 
             
              # seq.ranges
         | 
| @@ -56,47 +54,71 @@ module TimeMath | |
| 56 54 | 
             
              # It is pretty convenient for filtering data from databases or APIs,
         | 
| 57 55 | 
             
              # TimeMath creates list of filtering ranges in a blink.
         | 
| 58 56 | 
             
              #
         | 
| 57 | 
            +
              # Sequence also supports any item-updating operations in the same fashion
         | 
| 58 | 
            +
              # {Op} does:
         | 
| 59 | 
            +
              #
         | 
| 60 | 
            +
              # ```ruby
         | 
| 61 | 
            +
              # seq = TimeMath.day.sequence(from...to, expand: true).advance(:hour, 5).decrease(:min, 20)
         | 
| 62 | 
            +
              # # => #<TimeMath::Sequence(:day, 2016-05-01 00:00:00 +0300...2016-05-05 00:00:00 +0300).advance(:hour, 5).decrease(:min, 20)>
         | 
| 63 | 
            +
              # seq.to_a
         | 
| 64 | 
            +
              # # => [2016-05-01 04:40:00 +0300, 2016-05-02 04:40:00 +0300, 2016-05-03 04:40:00 +0300, 2016-05-04 04:40:00 +0300]
         | 
| 65 | 
            +
              # ```
         | 
| 66 | 
            +
              #
         | 
| 59 67 | 
             
              class Sequence
         | 
| 60 68 | 
             
                # Creates a sequence. Typically, it is easier to to it with {Units::Base#sequence},
         | 
| 61 69 | 
             
                # like this:
         | 
| 62 70 | 
             
                #
         | 
| 63 71 | 
             
                # ```ruby
         | 
| 64 | 
            -
                # TimeMath.day.sequence(from | 
| 72 | 
            +
                # TimeMath.day.sequence(from...to)
         | 
| 65 73 | 
             
                # ```
         | 
| 66 74 | 
             
                #
         | 
| 67 75 | 
             
                # @param unit [Symbol] one of {TimeMath.units};
         | 
| 68 | 
            -
                # @param  | 
| 69 | 
            -
                #  | 
| 76 | 
            +
                # @param range [Range] range of time-y values (Time, Date, DateTime);
         | 
| 77 | 
            +
                #   note that range with inclusive and exclusive and will produce
         | 
| 78 | 
            +
                #   different sequences.
         | 
| 70 79 | 
             
                # @param options [Hash]
         | 
| 71 80 | 
             
                # @option options [Boolean] :expand round sequence ends on creation
         | 
| 72 | 
            -
                #   (from is floored and to is ceiled);
         | 
| 73 | 
            -
                # @option options [Boolean] :floor sequence will be rounding'ing all
         | 
| 74 | 
            -
                #   the intermediate values.
         | 
| 81 | 
            +
                #   (`from` is floored and `to` is ceiled);
         | 
| 75 82 | 
             
                #
         | 
| 76 | 
            -
                def initialize(unit,  | 
| 83 | 
            +
                def initialize(unit, range, options = {})
         | 
| 77 84 | 
             
                  @unit = Units.get(unit)
         | 
| 78 | 
            -
                  @from, @to =  | 
| 85 | 
            +
                  @from, @to, @exclude_end = process_range(range)
         | 
| 79 86 | 
             
                  @options = options.dup
         | 
| 80 87 |  | 
| 81 88 | 
             
                  expand! if options[:expand]
         | 
| 82 | 
            -
                  @ | 
| 89 | 
            +
                  @op = Op.new
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                # @private
         | 
| 93 | 
            +
                def initialize_copy(other)
         | 
| 94 | 
            +
                  @unit = other.unit
         | 
| 95 | 
            +
                  @from, @to, @exclude_end = other.from, other.to, other.exclude_end?
         | 
| 96 | 
            +
                  @op = other.op.dup
         | 
| 83 97 | 
             
                end
         | 
| 84 98 |  | 
| 85 | 
            -
                attr_reader :from, :to, :unit
         | 
| 99 | 
            +
                attr_reader :from, :to, :unit, :op
         | 
| 86 100 |  | 
| 87 | 
            -
                 | 
| 101 | 
            +
                # Compares two sequences, considering their start, end, unit and
         | 
| 102 | 
            +
                # operations.
         | 
| 103 | 
            +
                #
         | 
| 104 | 
            +
                # @param other [Sequence]
         | 
| 105 | 
            +
                # @return [Boolean]
         | 
| 106 | 
            +
                def ==(other) # rubocop:disable Metrics/AbcSize
         | 
| 88 107 | 
             
                  self.class == other.class && unit == other.unit &&
         | 
| 89 | 
            -
                    from == other.from && to == other.to
         | 
| 108 | 
            +
                    from == other.from && to == other.to &&
         | 
| 109 | 
            +
                    exclude_end? == other.exclude_end? &&
         | 
| 110 | 
            +
                    op == other.op
         | 
| 90 111 | 
             
                end
         | 
| 91 112 |  | 
| 92 | 
            -
                #  | 
| 93 | 
            -
                 | 
| 94 | 
            -
             | 
| 113 | 
            +
                # Whether sequence was created from exclude-end range (and, therefore,
         | 
| 114 | 
            +
                # will exclude `to` when converted to array).
         | 
| 115 | 
            +
                def exclude_end?
         | 
| 116 | 
            +
                  @exclude_end
         | 
| 95 117 | 
             
                end
         | 
| 96 118 |  | 
| 97 119 | 
             
                # Expand sequence ends to nearest round unit.
         | 
| 98 120 | 
             
                #
         | 
| 99 | 
            -
                # @return self
         | 
| 121 | 
            +
                # @return [self]
         | 
| 100 122 | 
             
                def expand!
         | 
| 101 123 | 
             
                  @from = unit.floor(from)
         | 
| 102 124 | 
             
                  @to = unit.ceil(to)
         | 
| @@ -108,22 +130,104 @@ module TimeMath | |
| 108 130 | 
             
                #
         | 
| 109 131 | 
             
                # @return [Sequence]
         | 
| 110 132 | 
             
                def expand
         | 
| 111 | 
            -
                  dup. | 
| 133 | 
            +
                  dup.expand!
         | 
| 112 134 | 
             
                end
         | 
| 113 135 |  | 
| 114 | 
            -
                #  | 
| 136 | 
            +
                # @method floor!(unit, span = 1)
         | 
| 137 | 
            +
                #   Adds {Units::Base#floor} to list of operations to apply to sequence items.
         | 
| 115 138 | 
             
                #
         | 
| 116 | 
            -
                # | 
| 117 | 
            -
                 | 
| 118 | 
            -
             | 
| 119 | 
            -
                end
         | 
| 120 | 
            -
             | 
| 121 | 
            -
                # Creates new sequence with setting to floor all the intermediate
         | 
| 122 | 
            -
                # values.
         | 
| 139 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 140 | 
            +
                #   @param span [Numeric] how many units to floor to.
         | 
| 141 | 
            +
                #   @return [self]
         | 
| 123 142 | 
             
                #
         | 
| 124 | 
            -
                # @ | 
| 125 | 
            -
                 | 
| 126 | 
            -
             | 
| 143 | 
            +
                # @method floor(unit, span = 1)
         | 
| 144 | 
            +
                #   Non-destructive version of {#floor!}.
         | 
| 145 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 146 | 
            +
                #   @param span [Numeric] how many units to floor to.
         | 
| 147 | 
            +
                #   @return [Sequence]
         | 
| 148 | 
            +
                #
         | 
| 149 | 
            +
                # @method ceil!(unit, span = 1)
         | 
| 150 | 
            +
                #   Adds {Units::Base#ceil} to list of operations to apply to sequence items.
         | 
| 151 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 152 | 
            +
                #   @param span [Numeric] how many units to ceil to.
         | 
| 153 | 
            +
                #   @return [self]
         | 
| 154 | 
            +
                #
         | 
| 155 | 
            +
                # @method ceil(unit, span = 1)
         | 
| 156 | 
            +
                #   Non-destructive version of {#ceil!}.
         | 
| 157 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 158 | 
            +
                #   @param span [Numeric] how many units to ceil to.
         | 
| 159 | 
            +
                #   @return [Sequence]
         | 
| 160 | 
            +
                #
         | 
| 161 | 
            +
                # @method round!(unit, span = 1)
         | 
| 162 | 
            +
                #   Adds {Units::Base#round} to list of operations to apply to sequence items.
         | 
| 163 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 164 | 
            +
                #   @param span [Numeric] how many units to round to.
         | 
| 165 | 
            +
                #   @return [self]
         | 
| 166 | 
            +
                #
         | 
| 167 | 
            +
                # @method round(unit, span = 1)
         | 
| 168 | 
            +
                #   Non-destructive version of {#round!}.
         | 
| 169 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 170 | 
            +
                #   @param span [Numeric] how many units to round to.
         | 
| 171 | 
            +
                #   @return [Sequence]
         | 
| 172 | 
            +
                #
         | 
| 173 | 
            +
                # @method next!(unit, span = 1)
         | 
| 174 | 
            +
                #   Adds {Units::Base#next} to list of operations to apply to sequence items.
         | 
| 175 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 176 | 
            +
                #   @param span [Numeric] how many units to ceil to.
         | 
| 177 | 
            +
                #   @return [self]
         | 
| 178 | 
            +
                #
         | 
| 179 | 
            +
                # @method next(unit, span = 1)
         | 
| 180 | 
            +
                #   Non-destructive version of {#next!}.
         | 
| 181 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 182 | 
            +
                #   @param span [Numeric] how many units to ceil to.
         | 
| 183 | 
            +
                #   @return [Sequence]
         | 
| 184 | 
            +
                #
         | 
| 185 | 
            +
                # @method prev!(unit, span = 1)
         | 
| 186 | 
            +
                #   Adds {Units::Base#prev} to list of operations to apply to sequence items.
         | 
| 187 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 188 | 
            +
                #   @param span [Numeric] how many units to floor to.
         | 
| 189 | 
            +
                #   @return [self]
         | 
| 190 | 
            +
                #
         | 
| 191 | 
            +
                # @method prev(unit, span = 1)
         | 
| 192 | 
            +
                #   Non-destructive version of {#prev!}.
         | 
| 193 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 194 | 
            +
                #   @param span [Numeric] how many units to floor to.
         | 
| 195 | 
            +
                #   @return [Sequence]
         | 
| 196 | 
            +
                #
         | 
| 197 | 
            +
                # @method advance!(unit, amount = 1)
         | 
| 198 | 
            +
                #   Adds {Units::Base#advance} to list of operations to apply to sequence items.
         | 
| 199 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 200 | 
            +
                #   @param amount [Numeric] how many units to advance.
         | 
| 201 | 
            +
                #   @return [self]
         | 
| 202 | 
            +
                #
         | 
| 203 | 
            +
                # @method advance(unit, amount = 1)
         | 
| 204 | 
            +
                #   Non-destructive version of {#advance!}.
         | 
| 205 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 206 | 
            +
                #   @param amount [Numeric] how many units to advance.
         | 
| 207 | 
            +
                #   @return [Sequence]
         | 
| 208 | 
            +
                #
         | 
| 209 | 
            +
                # @method decrease!(unit, amount = 1)
         | 
| 210 | 
            +
                #   Adds {Units::Base#decrease} to list of operations to apply to sequence items.
         | 
| 211 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 212 | 
            +
                #   @param amount [Numeric] how many units to decrease.
         | 
| 213 | 
            +
                #   @return [self]
         | 
| 214 | 
            +
                #
         | 
| 215 | 
            +
                # @method decrease(unit, amount = 1)
         | 
| 216 | 
            +
                #   Non-destructive version of {#decrease!}.
         | 
| 217 | 
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         | 
| 218 | 
            +
                #   @param amount [Numeric] how many units to decrease.
         | 
| 219 | 
            +
                #   @return [Sequence]
         | 
| 220 | 
            +
                #
         | 
| 221 | 
            +
             | 
| 222 | 
            +
                Op::OPERATIONS.each do |operation|
         | 
| 223 | 
            +
                  define_method "#{operation}!" do |*arg|
         | 
| 224 | 
            +
                    @op.send("#{operation}!", *arg)
         | 
| 225 | 
            +
                    self
         | 
| 226 | 
            +
                  end
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                  define_method operation do |*arg|
         | 
| 229 | 
            +
                    dup.send("#{operation}!", *arg)
         | 
| 230 | 
            +
                  end
         | 
| 127 231 | 
             
                end
         | 
| 128 232 |  | 
| 129 233 | 
             
                # Creates an array of time unit starts between from and to. They will
         | 
| @@ -139,10 +243,11 @@ module TimeMath | |
| 139 243 | 
             
                  while iter < to
         | 
| 140 244 | 
             
                    seq << iter
         | 
| 141 245 |  | 
| 142 | 
            -
                    iter =  | 
| 246 | 
            +
                    iter = unit.advance(iter)
         | 
| 143 247 | 
             
                  end
         | 
| 248 | 
            +
                  seq << to unless exclude_end?
         | 
| 144 249 |  | 
| 145 | 
            -
                  seq
         | 
| 250 | 
            +
                  op.call(seq)
         | 
| 146 251 | 
             
                end
         | 
| 147 252 |  | 
| 148 253 | 
             
                # Creates an array of pairs (time unit start, time unit end) between
         | 
| @@ -163,13 +268,18 @@ module TimeMath | |
| 163 268 | 
             
                end
         | 
| 164 269 |  | 
| 165 270 | 
             
                def inspect
         | 
| 166 | 
            -
                   | 
| 271 | 
            +
                  ops = op.inspect_operations
         | 
| 272 | 
            +
                  ops = '.' + ops unless ops.empty?
         | 
| 273 | 
            +
                  "#<#{self.class}(#{unit.name.inspect}, #{from}#{exclude_end? ? '...' : '..'}#{to})#{ops}>"
         | 
| 167 274 | 
             
                end
         | 
| 168 275 |  | 
| 169 276 | 
             
                private
         | 
| 170 277 |  | 
| 171 | 
            -
                def  | 
| 172 | 
            -
                   | 
| 278 | 
            +
                def process_range(range)
         | 
| 279 | 
            +
                  range.is_a?(Range) && Util.timey?(range.begin) && Util.timey?(range.end) or
         | 
| 280 | 
            +
                    raise ArgumentError, "Range of time-y values expected, #{range} got"
         | 
| 281 | 
            +
             | 
| 282 | 
            +
                  [range.begin, range.end, range.exclude_end?]
         | 
| 173 283 | 
             
                end
         | 
| 174 284 | 
             
              end
         | 
| 175 285 | 
             
            end
         | 
    
        data/lib/time_math/units/base.rb
    CHANGED
    
    | @@ -8,6 +8,13 @@ module TimeMath | |
| 8 8 | 
             
                # TimeMath.day.advance(tm, 5) # advances tm by 5 days
         | 
| 9 9 | 
             
                # ```
         | 
| 10 10 | 
             
                #
         | 
| 11 | 
            +
                # See also {TimeMath::Op} for performing multiple operations in
         | 
| 12 | 
            +
                # concise & DRY manner, like this:
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                # ```ruby
         | 
| 15 | 
            +
                # TimeMath().advance(:day, 5).floor(:hour).advance(:min, 20).call(tm)
         | 
| 16 | 
            +
                # ```
         | 
| 17 | 
            +
                #
         | 
| 11 18 | 
             
                class Base
         | 
| 12 19 | 
             
                  # Creates unit of time. Typically you don't need it, as it is
         | 
| 13 20 | 
             
                  # easier to do `TimeMath.day` or `TimeMath[:day]` to obtain it.
         | 
| @@ -22,41 +29,59 @@ module TimeMath | |
| 22 29 | 
             
                  # Rounds `tm` down to nearest unit (this means, `TimeMath.day.floor(tm)`
         | 
| 23 30 | 
             
                  # will return beginning of `tm`-s day, and so on).
         | 
| 24 31 | 
             
                  #
         | 
| 25 | 
            -
                  #  | 
| 26 | 
            -
                  #  | 
| 27 | 
            -
                  # | 
| 28 | 
            -
                   | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 32 | 
            +
                  # An optional second argument allows you to floor to arbitrary
         | 
| 33 | 
            +
                  # number of units, like to "each 3-hour" mark:
         | 
| 34 | 
            +
                  #
         | 
| 35 | 
            +
                  # ```ruby
         | 
| 36 | 
            +
                  # TimeMath.hour.floor(Time.parse('14:00'), 3)
         | 
| 37 | 
            +
                  # # => 2016-06-23 12:00:00 +0300
         | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  # # works well with float/rational spans
         | 
| 40 | 
            +
                  # TimeMath.hour.floor(Time.parse('14:15'), 1/2r)
         | 
| 41 | 
            +
                  # # => 2016-06-23 14:00:00 +0300
         | 
| 42 | 
            +
                  # TimeMath.hour.floor(Time.parse('14:45'), 1/2r)
         | 
| 43 | 
            +
                  # # => 2016-06-23 14:30:00 +0300
         | 
| 44 | 
            +
                  # ```
         | 
| 45 | 
            +
                  #
         | 
| 46 | 
            +
                  # @param tm [Time,Date,DateTime] time value to floor.
         | 
| 47 | 
            +
                  # @param span [Numeric] how many units to floor to. For units
         | 
| 48 | 
            +
                  #   less than week supports float/rational values.
         | 
| 49 | 
            +
                  # @return [Time,Date,DateTime] floored time value; class and timezone
         | 
| 50 | 
            +
                  #   info of origin would be preserved.
         | 
| 51 | 
            +
                  def floor(tm, span = 1)
         | 
| 52 | 
            +
                    int_floor = advance(floor_1(tm), (tm.send(name) / span.to_f).floor * span - tm.send(name))
         | 
| 53 | 
            +
                    float_fix(tm, int_floor, span % 1)
         | 
| 37 54 | 
             
                  end
         | 
| 38 55 |  | 
| 39 56 | 
             
                  # Rounds `tm` up to nearest unit (this means, `TimeMath.day.ceil(tm)`
         | 
| 40 57 | 
             
                  # will return beginning of day next after `tm`, and so on).
         | 
| 58 | 
            +
                  # An optional second argument allows to ceil to arbitrary
         | 
| 59 | 
            +
                  # amount of units (see {#floor} for more detailed explanation).
         | 
| 41 60 | 
             
                  #
         | 
| 42 | 
            -
                  # @param tm [Time,DateTime] time value to ceil.
         | 
| 43 | 
            -
                  # @ | 
| 61 | 
            +
                  # @param tm [Time,Date,DateTime] time value to ceil.
         | 
| 62 | 
            +
                  # @param span [Numeric] how many units to ceil to. For units
         | 
| 63 | 
            +
                  #   less than week supports float/rational values.
         | 
| 64 | 
            +
                  # @return [Time,Date,DateTime] ceiled time value; class and timezone info
         | 
| 44 65 | 
             
                  #   of origin would be preserved.
         | 
| 45 | 
            -
                  def ceil(tm)
         | 
| 46 | 
            -
                    f = floor(tm)
         | 
| 66 | 
            +
                  def ceil(tm, span = 1)
         | 
| 67 | 
            +
                    f = floor(tm, span)
         | 
| 47 68 |  | 
| 48 | 
            -
                    f == tm ? f : advance(f)
         | 
| 69 | 
            +
                    f == tm ? f : advance(f, span)
         | 
| 49 70 | 
             
                  end
         | 
| 50 71 |  | 
| 51 72 | 
             
                  # Rounds `tm` up or down to nearest unit (this means, `TimeMath.day.round(tm)`
         | 
| 52 73 | 
             
                  # will return beginning of `tm` day if `tm` is before noon, and
         | 
| 53 74 | 
             
                  # day next after `tm` if it is after, and so on).
         | 
| 75 | 
            +
                  # An optional second argument allows to round to arbitrary
         | 
| 76 | 
            +
                  # amount of units (see {#floor} for more detailed explanation).
         | 
| 54 77 | 
             
                  #
         | 
| 55 | 
            -
                  # @param tm [Time,DateTime] time value to round.
         | 
| 56 | 
            -
                  # @ | 
| 78 | 
            +
                  # @param tm [Time,Date,DateTime] time value to round.
         | 
| 79 | 
            +
                  # @param span [Numeric] how many units to round to. For units
         | 
| 80 | 
            +
                  #   less than week supports float/rational values.
         | 
| 81 | 
            +
                  # @return [Time,Date,DateTime] rounded time value; class and timezone info
         | 
| 57 82 | 
             
                  #   of origin would be preserved.
         | 
| 58 | 
            -
                  def round(tm)
         | 
| 59 | 
            -
                    f, c = floor(tm), ceil(tm)
         | 
| 83 | 
            +
                  def round(tm, span = 1)
         | 
| 84 | 
            +
                    f, c = floor(tm, span), ceil(tm, span)
         | 
| 60 85 |  | 
| 61 86 | 
             
                    (tm - f).abs < (tm - c).abs ? f : c
         | 
| 62 87 | 
             
                  end
         | 
| @@ -64,41 +89,52 @@ module TimeMath | |
| 64 89 | 
             
                  # Like {#floor}, but always return value lower than `tm` (e.g. if
         | 
| 65 90 | 
             
                  # `tm` is exactly midnight, then `TimeMath.day.prev(tm)` will return
         | 
| 66 91 | 
             
                  # _previous midnight_).
         | 
| 92 | 
            +
                  # An optional second argument allows to floor to arbitrary
         | 
| 93 | 
            +
                  # amount of units (see {#floor} for more detailed explanation).
         | 
| 67 94 | 
             
                  #
         | 
| 68 | 
            -
                  # @param tm [Time,DateTime] time value to calculate prev on.
         | 
| 69 | 
            -
                  # @ | 
| 95 | 
            +
                  # @param tm [Time,Date,DateTime] time value to calculate prev on.
         | 
| 96 | 
            +
                  # @param span [Numeric] how many units to floor to. For units
         | 
| 97 | 
            +
                  #   less than week supports float/rational values.
         | 
| 98 | 
            +
                  # @return [Time,Date,DateTime] prev time value; class and timezone info
         | 
| 70 99 | 
             
                  #   of origin would be preserved.
         | 
| 71 | 
            -
                  def prev(tm)
         | 
| 72 | 
            -
                    f = floor(tm)
         | 
| 73 | 
            -
                    f == tm ? decrease(f) : f
         | 
| 100 | 
            +
                  def prev(tm, span = 1)
         | 
| 101 | 
            +
                    f = floor(tm, span)
         | 
| 102 | 
            +
                    f == tm ? decrease(f, span) : f
         | 
| 74 103 | 
             
                  end
         | 
| 75 104 |  | 
| 76 105 | 
             
                  # Like {#ceil}, but always return value greater than `tm` (e.g. if
         | 
| 77 106 | 
             
                  # `tm` is exactly midnight, then `TimeMath.day.next(tm)` will return
         | 
| 78 107 | 
             
                  # _next midnight_).
         | 
| 108 | 
            +
                  # An optional second argument allows to ceil to arbitrary
         | 
| 109 | 
            +
                  # amount of units (see {#floor} for more detailed explanation).
         | 
| 79 110 | 
             
                  #
         | 
| 80 | 
            -
                  # @param tm [Time,DateTime] time value to calculate next on.
         | 
| 81 | 
            -
                  # @ | 
| 111 | 
            +
                  # @param tm [Time,Date,DateTime] time value to calculate next on.
         | 
| 112 | 
            +
                  # @param span [Numeric] how many units to ceil to. For units
         | 
| 113 | 
            +
                  #   less than week supports float/rational values.
         | 
| 114 | 
            +
                  # @return [Time,Date,DateTime] next time value; class and timezone info
         | 
| 82 115 | 
             
                  #   of origin would be preserved.
         | 
| 83 | 
            -
                  def next(tm)
         | 
| 84 | 
            -
                    c = ceil(tm)
         | 
| 85 | 
            -
                    c == tm ? advance(c) : c
         | 
| 116 | 
            +
                  def next(tm, span = 1)
         | 
| 117 | 
            +
                    c = ceil(tm, span)
         | 
| 118 | 
            +
                    c == tm ? advance(c, span) : c
         | 
| 86 119 | 
             
                  end
         | 
| 87 120 |  | 
| 88 121 | 
             
                  # Checks if `tm` is exactly rounded to unit.
         | 
| 89 122 | 
             
                  #
         | 
| 90 | 
            -
                  # @param tm [Time,DateTime] time value to check.
         | 
| 123 | 
            +
                  # @param tm [Time,Date,DateTime] time value to check.
         | 
| 124 | 
            +
                  # @param span [Numeric] how many units to check round at. For units
         | 
| 125 | 
            +
                  #   less than week supports float/rational values.
         | 
| 91 126 | 
             
                  # @return [Boolean] whether `tm` is exactly round to unit.
         | 
| 92 | 
            -
                  def round?(tm)
         | 
| 93 | 
            -
                    floor(tm) == tm
         | 
| 127 | 
            +
                  def round?(tm, span = 1)
         | 
| 128 | 
            +
                    floor(tm, span) == tm
         | 
| 94 129 | 
             
                  end
         | 
| 95 130 |  | 
| 96 131 | 
             
                  # Advances `tm` by given amount of unit.
         | 
| 97 132 | 
             
                  #
         | 
| 98 | 
            -
                  # @param tm [Time,DateTime] time value to advance;
         | 
| 99 | 
            -
                  # @param amount [ | 
| 133 | 
            +
                  # @param tm [Time,Date,DateTime] time value to advance;
         | 
| 134 | 
            +
                  # @param amount [Numeric] how many units forward to go. For units
         | 
| 135 | 
            +
                  #   less than week supports float/rational values.
         | 
| 100 136 | 
             
                  #
         | 
| 101 | 
            -
                  # @return [Time,DateTime] advanced time value; class and timezone info
         | 
| 137 | 
            +
                  # @return [Time,Date,DateTime] advanced time value; class and timezone info
         | 
| 102 138 | 
             
                  #   of origin would be preserved.
         | 
| 103 139 | 
             
                  def advance(tm, amount = 1)
         | 
| 104 140 | 
             
                    return decrease(tm, -amount) if amount < 0
         | 
| @@ -107,10 +143,11 @@ module TimeMath | |
| 107 143 |  | 
| 108 144 | 
             
                  # Decreases `tm` by given amount of unit.
         | 
| 109 145 | 
             
                  #
         | 
| 110 | 
            -
                  # @param tm [Time,DateTime] time value to decrease;
         | 
| 111 | 
            -
                  # @param amount [Integer] how many units forward to go.
         | 
| 146 | 
            +
                  # @param tm [Time,Date,DateTime] time value to decrease;
         | 
| 147 | 
            +
                  # @param amount [Integer] how many units forward to go. For units
         | 
| 148 | 
            +
                  #   less than week supports float/rational values.
         | 
| 112 149 | 
             
                  #
         | 
| 113 | 
            -
                  # @return [Time,DateTime] decrease time value; class and timezone info
         | 
| 150 | 
            +
                  # @return [Time,Date,DateTime] decrease time value; class and timezone info
         | 
| 114 151 | 
             
                  #   of origin would be preserved.
         | 
| 115 152 | 
             
                  def decrease(tm, amount = 1)
         | 
| 116 153 | 
             
                    return advance(tm, -amount) if amount < 0
         | 
| @@ -125,7 +162,7 @@ module TimeMath | |
| 125 162 | 
             
                  # # => 2016-05-28 16:30:00 +0300...2016-06-02 16:30:00 +0300
         | 
| 126 163 | 
             
                  # ```
         | 
| 127 164 | 
             
                  #
         | 
| 128 | 
            -
                  # @param tm [Time,DateTime] time value to create range from;
         | 
| 165 | 
            +
                  # @param tm [Time,Date,DateTime] time value to create range from;
         | 
| 129 166 | 
             
                  # @param amount [Integer] how many units should be between range
         | 
| 130 167 | 
             
                  #   start and end.
         | 
| 131 168 | 
             
                  #
         | 
| @@ -142,7 +179,7 @@ module TimeMath | |
| 142 179 | 
             
                  # # => 2016-05-23 16:30:00 +0300...2016-05-28 16:30:00 +0300
         | 
| 143 180 | 
             
                  # ```
         | 
| 144 181 | 
             
                  #
         | 
| 145 | 
            -
                  # @param tm [Time,DateTime] time value to create range from;
         | 
| 182 | 
            +
                  # @param tm [Time,Date,DateTime] time value to create range from;
         | 
| 146 183 | 
             
                  # @param amount [Integer] how many units should be between range
         | 
| 147 184 | 
             
                  #   start and end.
         | 
| 148 185 | 
             
                  #
         | 
| @@ -153,8 +190,8 @@ module TimeMath | |
| 153 190 |  | 
| 154 191 | 
             
                  # Measures distance between `from` and `to` in units of this class.
         | 
| 155 192 | 
             
                  #
         | 
| 156 | 
            -
                  # @param from [Time,DateTime] start of period;
         | 
| 157 | 
            -
                  # @param to [Time,DateTime] end of period.
         | 
| 193 | 
            +
                  # @param from [Time,Date,DateTime] start of period;
         | 
| 194 | 
            +
                  # @param to [Time,Date,DateTime] end of period.
         | 
| 158 195 | 
             
                  #
         | 
| 159 196 | 
             
                  # @return [Integer] how many full units are inside the period.
         | 
| 160 197 | 
             
                  # :nocov:
         | 
| @@ -175,8 +212,8 @@ module TimeMath | |
| 175 212 | 
             
                  # # => [26, 2016-05-27 16:20:00 +0300]
         | 
| 176 213 | 
             
                  # ```
         | 
| 177 214 | 
             
                  #
         | 
| 178 | 
            -
                  # @param from [Time,DateTime] start of period;
         | 
| 179 | 
            -
                  # @param to [Time,DateTime] end of period.
         | 
| 215 | 
            +
                  # @param from [Time,Date,DateTime] start of period;
         | 
| 216 | 
            +
                  # @param to [Time,Date,DateTime] end of period.
         | 
| 180 217 | 
             
                  #
         | 
| 181 218 | 
             
                  # @return [Array<Integer, Time or DateTime>] how many full units
         | 
| 182 219 | 
             
                  #   are inside the period; exact value of `from` + full units.
         | 
| @@ -185,30 +222,12 @@ module TimeMath | |
| 185 222 | 
             
                    [m, advance(from, m)]
         | 
| 186 223 | 
             
                  end
         | 
| 187 224 |  | 
| 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 225 | 
             
                  # Creates {Sequence} instance for producing all time units between
         | 
| 207 226 | 
             
                  # from and too. See {Sequence} class documentation for available
         | 
| 208 227 | 
             
                  # options and functionality.
         | 
| 209 228 | 
             
                  #
         | 
| 210 | 
            -
                  # @param from [Time,DateTime] start of sequence;
         | 
| 211 | 
            -
                  # @param to [Time,DateTime] upper limit of sequence;
         | 
| 229 | 
            +
                  # @param from [Time,Date,DateTime] start of sequence;
         | 
| 230 | 
            +
                  # @param to [Time,Date,DateTime] upper limit of sequence;
         | 
| 212 231 | 
             
                  # @param options [Hash]
         | 
| 213 232 | 
             
                  # @option options [Boolean] :expand round sequence ends on creation
         | 
| 214 233 | 
             
                  #   (from is floored and to is ceiled);
         | 
| @@ -216,8 +235,65 @@ module TimeMath | |
| 216 235 | 
             
                  #   the intermediate values.
         | 
| 217 236 | 
             
                  #
         | 
| 218 237 | 
             
                  # @return [Sequence]
         | 
| 219 | 
            -
                  def sequence( | 
| 220 | 
            -
                    TimeMath::Sequence.new(name,  | 
| 238 | 
            +
                  def sequence(range, options = {})
         | 
| 239 | 
            +
                    TimeMath::Sequence.new(name, range, options)
         | 
| 240 | 
            +
                  end
         | 
| 241 | 
            +
             | 
| 242 | 
            +
                  # Converts input timestamps list to regular list of timestamps
         | 
| 243 | 
            +
                  # over current unit.
         | 
| 244 | 
            +
                  #
         | 
| 245 | 
            +
                  # Like this:
         | 
| 246 | 
            +
                  #
         | 
| 247 | 
            +
                  # ```ruby
         | 
| 248 | 
            +
                  # times = [Time.parse('2016-05-01'), Time.parse('2016-05-03'), Time.parse('2016-05-08')]
         | 
| 249 | 
            +
                  # TimeMath.day.resample(times)
         | 
| 250 | 
            +
                  # # =>  => [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]
         | 
| 251 | 
            +
                  # ```
         | 
| 252 | 
            +
                  #
         | 
| 253 | 
            +
                  # The best way about resampling it also works for hashes with time
         | 
| 254 | 
            +
                  # keys. Like this:
         | 
| 255 | 
            +
                  #
         | 
| 256 | 
            +
                  # ```ruby
         | 
| 257 | 
            +
                  # h = {Date.parse('Wed, 01 Jun 2016')=>1, Date.parse('Tue, 07 Jun 2016')=>3, Date.parse('Thu, 09 Jun 2016')=>1}
         | 
| 258 | 
            +
                  # # => {#<Date: 2016-06-01>=>1, #<Date: 2016-06-07>=>3, #<Date: 2016-06-09>=>1}
         | 
| 259 | 
            +
                  #
         | 
| 260 | 
            +
                  # pp TimeMath.day.resample(h)
         | 
| 261 | 
            +
                  # # {#<Date: 2016-06-01>=>[1],
         | 
| 262 | 
            +
                  # #  #<Date: 2016-06-02>=>[],
         | 
| 263 | 
            +
                  # #  #<Date: 2016-06-03>=>[],
         | 
| 264 | 
            +
                  # #  #<Date: 2016-06-04>=>[],
         | 
| 265 | 
            +
                  # #  #<Date: 2016-06-05>=>[],
         | 
| 266 | 
            +
                  # #  #<Date: 2016-06-06>=>[],
         | 
| 267 | 
            +
                  # #  #<Date: 2016-06-07>=>[3],
         | 
| 268 | 
            +
                  # #  #<Date: 2016-06-08>=>[],
         | 
| 269 | 
            +
                  # #  #<Date: 2016-06-09>=>[1]}
         | 
| 270 | 
            +
                  #
         | 
| 271 | 
            +
                  # # The default resample just groups all related values in arrays
         | 
| 272 | 
            +
                  # # You can pass block or symbol, to have the values you need:
         | 
| 273 | 
            +
                  # pp TimeMath.day.resample(h,&:first)
         | 
| 274 | 
            +
                  # # {#<Date: 2016-06-01>=>1,
         | 
| 275 | 
            +
                  # #  #<Date: 2016-06-02>=>nil,
         | 
| 276 | 
            +
                  # #  #<Date: 2016-06-03>=>nil,
         | 
| 277 | 
            +
                  # #  #<Date: 2016-06-04>=>nil,
         | 
| 278 | 
            +
                  # #  #<Date: 2016-06-05>=>nil,
         | 
| 279 | 
            +
                  # #  #<Date: 2016-06-06>=>nil,
         | 
| 280 | 
            +
                  # #  #<Date: 2016-06-07>=>3,
         | 
| 281 | 
            +
                  # #  #<Date: 2016-06-08>=>nil,
         | 
| 282 | 
            +
                  # #  #<Date: 2016-06-09>=>1}
         | 
| 283 | 
            +
                  # ```
         | 
| 284 | 
            +
                  #
         | 
| 285 | 
            +
                  # @param array_or_hash array of time-y values (Time/Date/DateTime)
         | 
| 286 | 
            +
                  #   or hash with time-y keys.
         | 
| 287 | 
            +
                  # @param symbol in case of first param being a hash -- method to
         | 
| 288 | 
            +
                  #   call on key arrays while grouping.
         | 
| 289 | 
            +
                  # @param block in  case of first param being a hash -- block to
         | 
| 290 | 
            +
                  #   call on key arrays while grouping.
         | 
| 291 | 
            +
                  #
         | 
| 292 | 
            +
                  # @return array or hash spread regular by unit; if first param was
         | 
| 293 | 
            +
                  #   hash, keys corresponding to each period are grouped into arrays;
         | 
| 294 | 
            +
                  #   this array could be further processed with block/symbol provided.
         | 
| 295 | 
            +
                  def resample(array_or_hash, symbol = nil, &block)
         | 
| 296 | 
            +
                    Resampler.call(name, array_or_hash, symbol, &block)
         | 
| 221 297 | 
             
                  end
         | 
| 222 298 |  | 
| 223 299 | 
             
                  def inspect
         | 
| @@ -252,13 +328,51 @@ module TimeMath | |
| 252 328 | 
             
                    components = EMPTY_VALUES.zip(components).map { |d, c| c || d }
         | 
| 253 329 | 
             
                    case origin
         | 
| 254 330 | 
             
                    when Time
         | 
| 255 | 
            -
                      Time.mktime(*components.reverse, nil, nil, nil, origin.zone)
         | 
| 331 | 
            +
                      res = Time.mktime(*components.reverse, nil, nil, nil, origin.zone)
         | 
| 332 | 
            +
                      fix_no_zone(res, origin, *components)
         | 
| 256 333 | 
             
                    when DateTime
         | 
| 257 334 | 
             
                      DateTime.new(*components, origin.zone)
         | 
| 335 | 
            +
                    when Date
         | 
| 336 | 
            +
                      Date.new(*components.first(3))
         | 
| 337 | 
            +
                    else
         | 
| 338 | 
            +
                      raise ArgumentError, "Expected Time, Date or DateTime, got #{origin.class}"
         | 
| 258 339 | 
             
                    end
         | 
| 259 340 | 
             
                  end
         | 
| 260 341 |  | 
| 261 | 
            -
                   | 
| 342 | 
            +
                  def to_components(tm)
         | 
| 343 | 
            +
                    case tm
         | 
| 344 | 
            +
                    when Time, DateTime
         | 
| 345 | 
            +
                      [tm.year, tm.month, tm.day, tm.hour, tm.min, tm.sec]
         | 
| 346 | 
            +
                    when Date
         | 
| 347 | 
            +
                      [tm.year, tm.month, tm.day]
         | 
| 348 | 
            +
                    else
         | 
| 349 | 
            +
                      raise ArgumentError, "Expected Time, Date or DateTime, got #{tm.class}"
         | 
| 350 | 
            +
                    end
         | 
| 351 | 
            +
                  end
         | 
| 352 | 
            +
             | 
| 353 | 
            +
                  def floor_1(tm)
         | 
| 354 | 
            +
                    components = to_components(tm).first(index + 1)
         | 
| 355 | 
            +
                    new_from_components(tm, *components)
         | 
| 356 | 
            +
                  end
         | 
| 357 | 
            +
             | 
| 358 | 
            +
                  def float_fix(tm, floored, float_span_part)
         | 
| 359 | 
            +
                    if float_span_part.zero?
         | 
| 360 | 
            +
                      floored
         | 
| 361 | 
            +
                    else
         | 
| 362 | 
            +
                      float_floored = advance(floored, float_span_part)
         | 
| 363 | 
            +
                      float_floored > tm ? floored : float_floored
         | 
| 364 | 
            +
                    end
         | 
| 365 | 
            +
                  end
         | 
| 366 | 
            +
             | 
| 367 | 
            +
                  def fix_no_zone(tm, origin, *components)
         | 
| 368 | 
            +
                    if origin.zone != 'UTC' && tm.zone == 'UTC'
         | 
| 369 | 
            +
                      # Fixes things like this one: https://github.com/jruby/jruby/issues/3978
         | 
| 370 | 
            +
                      # ...by falling back to use of UTC offset instead of timezone abbr
         | 
| 371 | 
            +
                      Time.new(*components, origin.utc_offset)
         | 
| 372 | 
            +
                    else
         | 
| 373 | 
            +
                      tm
         | 
| 374 | 
            +
                    end
         | 
| 375 | 
            +
                  end
         | 
| 262 376 | 
             
                end
         | 
| 263 377 | 
             
              end
         | 
| 264 378 | 
             
            end
         |