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
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 8fe3eec624d634487ddc4b74d08a3d39ec3f34d5
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 87c691b0990e5e4a03f7bad4602e8bf100d1d526
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 8a7e17f5d2f9768c734649768b95c8ad63f168ca3c8686f2d52440caa952f8c7a20bc1af223cb5238eacfdec7e5924e814c0d8385887e83a5623da0024d15a55
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: a39344389ba42191d780257d97e4c53e6e2e84a5512e7deac17ba731a543e5decab9e01e833b266dc6f93efbbdec1d11135deff8cb78110071535ef382c713a7
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,5 +1,18 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            #  
     | 
| 
      
 1 
     | 
    
         
            +
            # TimeMath Changelog
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            # 0.0. 
     | 
| 
      
 3 
     | 
    
         
            +
            # 0.0.5 (2016-06-25)
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            * Add  
     | 
| 
      
 5 
     | 
    
         
            +
            * Add optional second argument to rounding functions (`floor`, `ceil` and
         
     | 
| 
      
 6 
     | 
    
         
            +
              so on), for "floor to 3-hour mark";
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Allow this argument, as well as in `advance`/`decrease`, to be non-integer;
         
     | 
| 
      
 8 
     | 
    
         
            +
              so, you can do `hour.advance(tm, 1/2r)` now;
         
     | 
| 
      
 9 
     | 
    
         
            +
            * Drop any `core_ext`s completely, even despite it was optional;
         
     | 
| 
      
 10 
     | 
    
         
            +
            * Add `Op` chainable operations concept (and drop `Span`, which
         
     | 
| 
      
 11 
     | 
    
         
            +
              is inferior to it);
         
     | 
| 
      
 12 
     | 
    
         
            +
            * Redesign `Sequence` creation, allow include/exclude end;
         
     | 
| 
      
 13 
     | 
    
         
            +
            * Add (experimental) resampling feature.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            # 0.0.4 (2016-05-28)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            * First "real" release with current name, `Time` and `DateTime` support,
         
     | 
| 
      
 18 
     | 
    
         
            +
              proper documentation and stuff.
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -11,23 +11,45 @@ arithmetics easier. It provides you with simple, easy-to-remember API, without 
     | 
|
| 
       11 
11 
     | 
    
         
             
            any monkey-patching of core Ruby classes, so it can be used alongside
         
     | 
| 
       12 
12 
     | 
    
         
             
            Rails or without it, for any purpose.
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
            ## Table Of Contents
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            * [Features](#features)
         
     | 
| 
      
 17 
     | 
    
         
            +
            * [Naming](#naming)
         
     | 
| 
      
 18 
     | 
    
         
            +
            * [Reasons](#reasons)
         
     | 
| 
      
 19 
     | 
    
         
            +
            * [Installation](#installation)
         
     | 
| 
      
 20 
     | 
    
         
            +
            * [Usage](#usage)
         
     | 
| 
      
 21 
     | 
    
         
            +
                - [Full list of simple arithmetic methods](#full-list-of-simple-arithmetic-methods)
         
     | 
| 
      
 22 
     | 
    
         
            +
                - [Set of operations as a value object](#set-of-operations-as-a-value-object)
         
     | 
| 
      
 23 
     | 
    
         
            +
                - [Time sequence abstraction](#time-sequence-abstraction)
         
     | 
| 
      
 24 
     | 
    
         
            +
                - [Measuring time periods](#measuring-time-periods)
         
     | 
| 
      
 25 
     | 
    
         
            +
                - [Resampling](#resampling)
         
     | 
| 
      
 26 
     | 
    
         
            +
            * [Notes on timezones](#notes-on-timezones)
         
     | 
| 
      
 27 
     | 
    
         
            +
            * [Compatibility notes](#compatibility-notes)
         
     | 
| 
      
 28 
     | 
    
         
            +
            * [Alternatives](#alternatives)
         
     | 
| 
      
 29 
     | 
    
         
            +
            * [Links](#links)
         
     | 
| 
      
 30 
     | 
    
         
            +
            * [Author](#author)
         
     | 
| 
      
 31 
     | 
    
         
            +
            * [License](#license)
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
       14 
33 
     | 
    
         
             
            ## Features
         
     | 
| 
       15 
34 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
            * No monkey-patching of core classes ( 
     | 
| 
       17 
     | 
    
         
            -
               
     | 
| 
       18 
     | 
    
         
            -
            * Works with Time and DateTime;
         
     | 
| 
      
 35 
     | 
    
         
            +
            * No monkey-patching of core classes (now **strict**; previously existing opt-in
         
     | 
| 
      
 36 
     | 
    
         
            +
              core ext removed in 0.0.5);
         
     | 
| 
      
 37 
     | 
    
         
            +
            * Works with Time, Date and DateTime;
         
     | 
| 
       19 
38 
     | 
    
         
             
            * Accurately preserves timezone info;
         
     | 
| 
       20 
39 
     | 
    
         
             
            * Simple arithmetics: floor/ceil/round to any time unit (second, hour, year
         
     | 
| 
       21 
40 
     | 
    
         
             
              or whatnot), advance/decrease by any unit;
         
     | 
| 
       22 
     | 
    
         
            -
            *  
     | 
| 
       23 
     | 
    
         
            -
               
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
            * Chainable [operations](#set-of-operations-as-a-value-object), including
         
     | 
| 
      
 42 
     | 
    
         
            +
              construction of "set of operations" value object (like "10:20 at next
         
     | 
| 
      
 43 
     | 
    
         
            +
              month first day"), clean and powerful;
         
     | 
| 
      
 44 
     | 
    
         
            +
            * Easy generation of [time sequences](#time-sequence-abstraction)
         
     | 
| 
      
 45 
     | 
    
         
            +
              (like "each day from _this_ to _that_ date");
         
     | 
| 
      
 46 
     | 
    
         
            +
            * Measuring of time distances between two timestamps in any units;
         
     | 
| 
      
 47 
     | 
    
         
            +
            * Powerful and flexible [resampling](#resampling) of arbitrary time value
         
     | 
| 
      
 48 
     | 
    
         
            +
              arrays/hashes into regular sequences.
         
     | 
| 
       27 
49 
     | 
    
         | 
| 
       28 
50 
     | 
    
         
             
            ## Naming
         
     | 
| 
       29 
51 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
            `TimeMath` is the  
     | 
| 
      
 52 
     | 
    
         
            +
            `TimeMath` is the best name I know for the task library does, yet
         
     | 
| 
       31 
53 
     | 
    
         
             
            it is [already taken](https://rubygems.org/gems/time_math). So, with no
         
     | 
| 
       32 
54 
     | 
    
         
             
            other thoughts I came with the ugly solution.
         
     | 
| 
       33 
55 
     | 
    
         | 
| 
         @@ -96,21 +118,55 @@ TimeMath.day.advance(Time.now, +10) # => 2016-06-07 14:06:57 +0300 
     | 
|
| 
       96 
118 
     | 
    
         
             
            * `<unit>.range(tm, amount)` -- creates range of `tm ... tm + amount <units>`;
         
     | 
| 
       97 
119 
     | 
    
         
             
            * `<unit>.range_back(tm, amount)` -- creates range of `tm - amount <units> ... tm`.
         
     | 
| 
       98 
120 
     | 
    
         | 
| 
      
 121 
     | 
    
         
            +
            **Things to note**:
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
            * rounding methods (`floor`, `ceil` and company) support optional second
         
     | 
| 
      
 124 
     | 
    
         
            +
              argument—amount of units to round to, like "each 3 hours": `hour.floor(tm, 3)`;
         
     | 
| 
      
 125 
     | 
    
         
            +
            * both rounding and advance/decrease methods allow their last argument to
         
     | 
| 
      
 126 
     | 
    
         
            +
              be float/rational, so you can `hour.advance(tm, 1/2r)` and this would
         
     | 
| 
      
 127 
     | 
    
         
            +
              work as you may expect. Non-integer arguments are only supported for
         
     | 
| 
      
 128 
     | 
    
         
            +
              units less than week (because "half of month" have no exact mathematical
         
     | 
| 
      
 129 
     | 
    
         
            +
              sense).
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
       99 
131 
     | 
    
         
             
            See also [Units::Base](http://www.rubydoc.info/gems/time_math2/TimeMath/Units/Base).
         
     | 
| 
       100 
132 
     | 
    
         | 
| 
       101 
     | 
    
         
            -
            ###  
     | 
| 
      
 133 
     | 
    
         
            +
            ### Set of operations as a value object
         
     | 
| 
       102 
134 
     | 
    
         | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
      
 135 
     | 
    
         
            +
            For example, you want "10 am at next monday". By using atomic time unit
         
     | 
| 
      
 136 
     | 
    
         
            +
            operations, you'll need the code like:
         
     | 
| 
       105 
137 
     | 
    
         | 
| 
       106 
138 
     | 
    
         
             
            ```ruby
         
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
            # => #<TimeMath::Span(day): +5>
         
     | 
| 
       109 
     | 
    
         
            -
            span.before(Time.now)
         
     | 
| 
       110 
     | 
    
         
            -
            # => 2016-05-23 17:46:13 +0300
         
     | 
| 
      
 139 
     | 
    
         
            +
            TimeMath.hour.advance(TimeMath.week.ceil(Time.now), 10)
         
     | 
| 
       111 
140 
     | 
    
         
             
            ```
         
     | 
| 
      
 141 
     | 
    
         
            +
            ...which is not really readable, to say the least. So, `TimeMath` provides
         
     | 
| 
      
 142 
     | 
    
         
            +
            one top-level method allowing to chain any operations you want:
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 145 
     | 
    
         
            +
            TimeMath(Time.now).ceil(:week).advance(:hour, 10).call
         
     | 
| 
      
 146 
     | 
    
         
            +
            ```
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
            Much more readable, huh?
         
     | 
| 
       112 
149 
     | 
    
         | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
      
 150 
     | 
    
         
            +
            The best thing about it, that you can prepare "operations list" value
         
     | 
| 
      
 151 
     | 
    
         
            +
            object, and then use it (or pass to methods, or
         
     | 
| 
      
 152 
     | 
    
         
            +
            serialize to YAML and deserialize in some Sidekiq task and so on):
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 155 
     | 
    
         
            +
            op = TimeMath().ceil(:week).advance(:hour, 10)
         
     | 
| 
      
 156 
     | 
    
         
            +
            # => #<TimeMath::Op ceil(:week).advance(:hour, 10)>
         
     | 
| 
      
 157 
     | 
    
         
            +
            op.call(Time.now)
         
     | 
| 
      
 158 
     | 
    
         
            +
            # => 2016-06-27 10:00:00 +0300
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
            # It also can be called on several arguments/array of arguments:
         
     | 
| 
      
 161 
     | 
    
         
            +
            op.call(tm1, tm2, tm3)
         
     | 
| 
      
 162 
     | 
    
         
            +
            op.call(array_of_timestamps)
         
     | 
| 
      
 163 
     | 
    
         
            +
            # ...or even used as a block-ish object:
         
     | 
| 
      
 164 
     | 
    
         
            +
            array_of_timestamps.map(&op)
         
     | 
| 
      
 165 
     | 
    
         
            +
            ```
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
            See also [TimeMath()](http://www.rubydoc.info/gems/time_math2/toplevel#TimeMath-instance_method)
         
     | 
| 
      
 168 
     | 
    
         
            +
            and underlying [TimeMath::Op](http://www.rubydoc.info/gems/time_math2/TimeMath/Op)
         
     | 
| 
      
 169 
     | 
    
         
            +
            class docs.
         
     | 
| 
       114 
170 
     | 
    
         | 
| 
       115 
171 
     | 
    
         
             
            ### Time sequence abstraction
         
     | 
| 
       116 
172 
     | 
    
         | 
| 
         @@ -122,8 +178,8 @@ to = Time.now 
     | 
|
| 
       122 
178 
     | 
    
         
             
            # => 2016-05-28 17:47:30 +0300
         
     | 
| 
       123 
179 
     | 
    
         
             
            from = TimeMath.day.floor(to)
         
     | 
| 
       124 
180 
     | 
    
         
             
            # => 2016-05-28 00:00:00 +0300
         
     | 
| 
       125 
     | 
    
         
            -
            seq = TimeMath.hour.sequence(from 
     | 
| 
       126 
     | 
    
         
            -
            # => #<TimeMath::Sequence(2016-05-28 00:00:00 +0300 
     | 
| 
      
 181 
     | 
    
         
            +
            seq = TimeMath.hour.sequence(from...to)
         
     | 
| 
      
 182 
     | 
    
         
            +
            # => #<TimeMath::Sequence(:hour, 2016-05-28 00:00:00 +0300...2016-05-28 17:47:30 +0300)>
         
     | 
| 
       127 
183 
     | 
    
         
             
            p(*seq)
         
     | 
| 
       128 
184 
     | 
    
         
             
            # 2016-05-28 00:00:00 +0300
         
     | 
| 
       129 
185 
     | 
    
         
             
            # 2016-05-28 01:00:00 +0300
         
     | 
| 
         @@ -136,11 +192,21 @@ p(*seq) 
     | 
|
| 
       136 
192 
     | 
    
         
             
            # ...and so on
         
     | 
| 
       137 
193 
     | 
    
         
             
            ```
         
     | 
| 
       138 
194 
     | 
    
         | 
| 
      
 195 
     | 
    
         
            +
            Note that sequence also play well with operation chain described above,
         
     | 
| 
      
 196 
     | 
    
         
            +
            so you can
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 199 
     | 
    
         
            +
            seq = TimeMath.day.sequence(Time.parse('2016-05-01')...Time.parse('2016-05-04')).advance(:hour, 10).decrease(:min, 5)
         
     | 
| 
      
 200 
     | 
    
         
            +
            # => #<TimeMath::Sequence(:day, 2016-05-01 00:00:00 +0300...2016-05-04 00:00:00 +0300).advance(:hour, 10).decrease(:min, 5)>
         
     | 
| 
      
 201 
     | 
    
         
            +
            seq.to_a
         
     | 
| 
      
 202 
     | 
    
         
            +
            # => [2016-05-01 09:55:00 +0300, 2016-05-02 09:55:00 +0300, 2016-05-03 09:55:00 +0300]
         
     | 
| 
      
 203 
     | 
    
         
            +
            ```
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
       139 
205 
     | 
    
         
             
            See also [Sequence YARD docs](http://www.rubydoc.info/gems/time_math2/TimeMath/Sequence).
         
     | 
| 
       140 
206 
     | 
    
         | 
| 
       141 
207 
     | 
    
         
             
            ### Measuring time periods
         
     | 
| 
       142 
208 
     | 
    
         | 
| 
       143 
     | 
    
         
            -
            Simple measure: just "how many `<unit>`s from date A to date B:
         
     | 
| 
      
 209 
     | 
    
         
            +
            Simple measure: just "how many `<unit>`s from date A to date B":
         
     | 
| 
       144 
210 
     | 
    
         | 
| 
       145 
211 
     | 
    
         
             
            ```ruby
         
     | 
| 
       146 
212 
     | 
    
         
             
            TimeMath.week.measure(Time.parse('2016-05-01'), Time.parse('2016-06-01'))
         
     | 
| 
         @@ -181,23 +247,53 @@ TimeMath.measure(birthday, Time.now, upto: :day) 
     | 
|
| 
       181 
247 
     | 
    
         
             
            # => {:days=>12157, :hours=>2, :minutes=>26, :seconds=>55}
         
     | 
| 
       182 
248 
     | 
    
         
             
            ```
         
     | 
| 
       183 
249 
     | 
    
         | 
| 
       184 
     | 
    
         
            -
            ###  
     | 
| 
      
 250 
     | 
    
         
            +
            ### Resampling
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
            **Resampling** is useful for situations when you have some timestamped
         
     | 
| 
      
 253 
     | 
    
         
            +
            data (with variable holes between values), and wantto make it regular,
         
     | 
| 
      
 254 
     | 
    
         
            +
            e.g. for charts drawing.
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
      
 256 
     | 
    
         
            +
            The most simple (and not very useful) resampling just turns array of
         
     | 
| 
      
 257 
     | 
    
         
            +
            irregular timestamps into regular one:
         
     | 
| 
      
 258 
     | 
    
         
            +
             
     | 
| 
      
 259 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 260 
     | 
    
         
            +
            dates = %w[2016-06-01 2016-06-03 2016-06-06].map(&Date.method(:parse))
         
     | 
| 
      
 261 
     | 
    
         
            +
            # => [#<Date: 2016-06-01>, #<Date: 2016-06-03>, #<Date: 2016-06-06>]
         
     | 
| 
      
 262 
     | 
    
         
            +
            TimeMath.day.resample(dates)
         
     | 
| 
      
 263 
     | 
    
         
            +
            # => [#<Date: 2016-06-01>, #<Date: 2016-06-02>, #<Date: 2016-06-03>, #<Date: 2016-06-04>, #<Date: 2016-06-05>, #<Date: 2016-06-06>]
         
     | 
| 
      
 264 
     | 
    
         
            +
            TimeMath.week.resample(dates)
         
     | 
| 
      
 265 
     | 
    
         
            +
            # => [#<Date: 2016-05-30>, #<Date: 2016-06-06>]
         
     | 
| 
      
 266 
     | 
    
         
            +
            TimeMath.month.resample(dates)
         
     | 
| 
      
 267 
     | 
    
         
            +
            # => [#<Date: 2016-06-01>]
         
     | 
| 
      
 268 
     | 
    
         
            +
            ```
         
     | 
| 
       185 
269 
     | 
    
         | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
       187 
     | 
    
         
            -
             
     | 
| 
       188 
     | 
    
         
            -
            it just adds a couple of new ones:
         
     | 
| 
      
 270 
     | 
    
         
            +
            Much more useful is _hash resampling_: when you have a hash of `{timestamp => value}`
         
     | 
| 
      
 271 
     | 
    
         
            +
            and...
         
     | 
| 
       189 
272 
     | 
    
         | 
| 
       190 
273 
     | 
    
         
             
            ```ruby
         
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
      
 274 
     | 
    
         
            +
            data = {Date.parse('2016-06-01') => 18, Date.parse('2016-06-03') => 8, Date.parse('2016-06-06') => -4}
         
     | 
| 
      
 275 
     | 
    
         
            +
            # => {#<Date: 2016-06-01>=>18, #<Date: 2016-06-03>=>8, #<Date: 2016-06-06>=>-4}
         
     | 
| 
      
 276 
     | 
    
         
            +
            TimeMath.day.resample(data)
         
     | 
| 
      
 277 
     | 
    
         
            +
            # => {#<Date: 2016-06-01>=>[18], #<Date: 2016-06-02>=>[], #<Date: 2016-06-03>=>[8], #<Date: 2016-06-04>=>[], #<Date: 2016-06-05>=>[], #<Date: 2016-06-06>=>[-4]}
         
     | 
| 
      
 278 
     | 
    
         
            +
            TimeMath.week.resample(data)
         
     | 
| 
      
 279 
     | 
    
         
            +
            # => {#<Date: 2016-05-30>=>[18, 8], #<Date: 2016-06-06>=>[-4]}
         
     | 
| 
      
 280 
     | 
    
         
            +
            TimeMath.month.resample(data)
         
     | 
| 
      
 281 
     | 
    
         
            +
            # => {#<Date: 2016-06-01>=>[18, 8, -4]}
         
     | 
| 
      
 282 
     | 
    
         
            +
            ```
         
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
      
 284 
     | 
    
         
            +
            For values grouping strategy, `resample` accepts symbol and block arguments:
         
     | 
| 
       192 
285 
     | 
    
         | 
| 
       193 
     | 
    
         
            -
             
     | 
| 
       194 
     | 
    
         
            -
             
     | 
| 
      
 286 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 287 
     | 
    
         
            +
            TimeMath.week.resample(data, :first)
         
     | 
| 
      
 288 
     | 
    
         
            +
            # => {#<Date: 2016-05-30>=>18, #<Date: 2016-06-06>=>-4}
         
     | 
| 
      
 289 
     | 
    
         
            +
            TimeMath.week.resample(data) { |vals| vals.inject(:+) }
         
     | 
| 
      
 290 
     | 
    
         
            +
             => {#<Date: 2016-05-30>=>26, #<Date: 2016-06-06>=>-4}
         
     | 
| 
       195 
291 
     | 
    
         
             
            ```
         
     | 
| 
       196 
292 
     | 
    
         | 
| 
       197 
     | 
    
         
            -
             
     | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
      
 293 
     | 
    
         
            +
            The functionality currently considered experimental, please notify me
         
     | 
| 
      
 294 
     | 
    
         
            +
            about your ideas and use cases via [GitHub issues](https://github.com/zverok/time_math2/issues)!
         
     | 
| 
       199 
295 
     | 
    
         | 
| 
       200 
     | 
    
         
            -
             
     | 
| 
      
 296 
     | 
    
         
            +
            ## Notes on timezones
         
     | 
| 
       201 
297 
     | 
    
         | 
| 
       202 
298 
     | 
    
         
             
            TimeMath tries its best to preserve timezones of original values. Currently,
         
     | 
| 
       203 
299 
     | 
    
         
             
            it means:
         
     | 
| 
         @@ -208,19 +304,18 @@ it means: 
     | 
|
| 
       208 
304 
     | 
    
         
             
              it is preserved by TimeMath (but be careful about jumping around DST,
         
     | 
| 
       209 
305 
     | 
    
         
             
              offset would not change).
         
     | 
| 
       210 
306 
     | 
    
         | 
| 
       211 
     | 
    
         
            -
            ##  
     | 
| 
      
 307 
     | 
    
         
            +
            ## Compatibility notes
         
     | 
| 
       212 
308 
     | 
    
         | 
| 
       213 
     | 
    
         
            -
            TimeMath  
     | 
| 
      
 309 
     | 
    
         
            +
            TimeMath is known to work on MRI Ruby >= 1.9.
         
     | 
| 
       214 
310 
     | 
    
         | 
| 
       215 
     | 
    
         
            -
             
     | 
| 
       216 
     | 
    
         
            -
             
     | 
| 
       217 
     | 
    
         
            -
             
     | 
| 
      
 311 
     | 
    
         
            +
            On JRuby it works, too, though there could be _slightly_ unexpected results,
         
     | 
| 
      
 312 
     | 
    
         
            +
            when JRuby fails to create time by timezone name (see [bug](https://github.com/jruby/jruby/issues/3978)).
         
     | 
| 
      
 313 
     | 
    
         
            +
            TimeMath in this case fallbacks to the same solution that used for `DateTime`,
         
     | 
| 
      
 314 
     | 
    
         
            +
            and at least preserves utc offset.
         
     | 
| 
       218 
315 
     | 
    
         | 
| 
       219 
     | 
    
         
            -
             
     | 
| 
       220 
     | 
    
         
            -
             
     | 
| 
       221 
     | 
    
         
            -
             
     | 
| 
       222 
     | 
    
         
            -
            end
         
     | 
| 
       223 
     | 
    
         
            -
            ```
         
     | 
| 
      
 316 
     | 
    
         
            +
            On Rubinius, some of tests fail and I haven't time to investigate it. If
         
     | 
| 
      
 317 
     | 
    
         
            +
            somebody still uses Rubinius and wants TimeMath to be working properly
         
     | 
| 
      
 318 
     | 
    
         
            +
            on it, please let me know.
         
     | 
| 
       224 
319 
     | 
    
         | 
| 
       225 
320 
     | 
    
         
             
            ## Alternatives
         
     | 
| 
       226 
321 
     | 
    
         | 
    
        data/lib/time_math.rb
    CHANGED
    
    | 
         @@ -1,10 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'time'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            require_relative './time_math/units'
         
     | 
| 
       4 
     | 
    
         
            -
            require_relative './time_math/sequence'
         
     | 
| 
       5 
     | 
    
         
            -
            require_relative './time_math/measure'
         
     | 
| 
       6 
     | 
    
         
            -
            require_relative './time_math/span'
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
3 
     | 
    
         
             
            # TimeMath is a small library for easy time units arithmetics (like "floor
         
     | 
| 
       9 
4 
     | 
    
         
             
            # the timestamp to the nearest hour", "advance the time value by 3 days"
         
     | 
| 
       10 
5 
     | 
    
         
             
            # and so on).
         
     | 
| 
         @@ -21,10 +16,18 @@ require_relative './time_math/span' 
     | 
|
| 
       21 
16 
     | 
    
         
             
            # time unit, which incapsulates most of the functionality. Refer to
         
     | 
| 
       22 
17 
     | 
    
         
             
            # {Units::Base} to see what you can get of it.
         
     | 
| 
       23 
18 
     | 
    
         
             
            #
         
     | 
| 
      
 19 
     | 
    
         
            +
            # See also `TimeMath()` method in global namespace, it is lot of fun!
         
     | 
| 
      
 20 
     | 
    
         
            +
            #
         
     | 
| 
       24 
21 
     | 
    
         
             
            module TimeMath
         
     | 
| 
       25 
     | 
    
         
            -
               
     | 
| 
       26 
     | 
    
         
            -
               
     | 
| 
       27 
     | 
    
         
            -
               
     | 
| 
      
 22 
     | 
    
         
            +
              require_relative './time_math/backports'
         
     | 
| 
      
 23 
     | 
    
         
            +
              require_relative './time_math/units'
         
     | 
| 
      
 24 
     | 
    
         
            +
              require_relative './time_math/op'
         
     | 
| 
      
 25 
     | 
    
         
            +
              require_relative './time_math/sequence'
         
     | 
| 
      
 26 
     | 
    
         
            +
              require_relative './time_math/measure'
         
     | 
| 
      
 27 
     | 
    
         
            +
              require_relative './time_math/resamplers'
         
     | 
| 
      
 28 
     | 
    
         
            +
              require_relative './time_math/util'
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              module_function
         
     | 
| 
       28 
31 
     | 
    
         | 
| 
       29 
32 
     | 
    
         
             
              # List all unit names known.
         
     | 
| 
       30 
33 
     | 
    
         
             
              #
         
     | 
| 
         @@ -71,7 +74,7 @@ module TimeMath 
     | 
|
| 
       71 
74 
     | 
    
         
             
              #   @return [Units::Base]
         
     | 
| 
       72 
75 
     | 
    
         
             
              #
         
     | 
| 
       73 
76 
     | 
    
         
             
              Units.names.each do |unit|
         
     | 
| 
       74 
     | 
    
         
            -
                 
     | 
| 
      
 77 
     | 
    
         
            +
                define_singleton_method(unit) { Units.get(unit) }
         
     | 
| 
       75 
78 
     | 
    
         
             
              end
         
     | 
| 
       76 
79 
     | 
    
         | 
| 
       77 
80 
     | 
    
         
             
              # Measures distance between two time values in all units at once.
         
     | 
| 
         @@ -85,8 +88,8 @@ module TimeMath 
     | 
|
| 
       85 
88 
     | 
    
         
             
              # # => {:years=>33, :months=>3, :weeks=>2, :days=>0, :hours=>1, :minutes=>25, :seconds=>52}
         
     | 
| 
       86 
89 
     | 
    
         
             
              # ```
         
     | 
| 
       87 
90 
     | 
    
         
             
              #
         
     | 
| 
       88 
     | 
    
         
            -
              # @param from [Time,DateTime]
         
     | 
| 
       89 
     | 
    
         
            -
              # @param to [Time,DateTime]
         
     | 
| 
      
 91 
     | 
    
         
            +
              # @param from [Time,Date,DateTime]
         
     | 
| 
      
 92 
     | 
    
         
            +
              # @param to [Time,Date,DateTime]
         
     | 
| 
       90 
93 
     | 
    
         
             
              # @param options [Hash] options
         
     | 
| 
       91 
94 
     | 
    
         
             
              # @option options [Boolean] :weeks pass `false` to exclude weeks from calculation;
         
     | 
| 
       92 
95 
     | 
    
         
             
              # @option options [Symbol] :upto pass max unit to use (e.g. if you'll
         
     | 
| 
         @@ -97,3 +100,39 @@ module TimeMath 
     | 
|
| 
       97 
100 
     | 
    
         
             
                Measure.measure(from, to, options)
         
     | 
| 
       98 
101 
     | 
    
         
             
              end
         
     | 
| 
       99 
102 
     | 
    
         
             
            end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
            # This method helps to create time arithmetics sequence as a value object.
         
     | 
| 
      
 105 
     | 
    
         
            +
            # Some examples:
         
     | 
| 
      
 106 
     | 
    
         
            +
            #
         
     | 
| 
      
 107 
     | 
    
         
            +
            # ```ruby
         
     | 
| 
      
 108 
     | 
    
         
            +
            # # 10 am at first weekday? Easy!
         
     | 
| 
      
 109 
     | 
    
         
            +
            # TimeMath(Time.now).floor(:week).advance(:hour, 10).call
         
     | 
| 
      
 110 
     | 
    
         
            +
            # # => 2016-06-20 10:00:00 +0300
         
     | 
| 
      
 111 
     | 
    
         
            +
            #
         
     | 
| 
      
 112 
     | 
    
         
            +
            # # For several time values? Nothing easier!
         
     | 
| 
      
 113 
     | 
    
         
            +
            # TimeMath(Time.local(2016,1,1), Time.local(2016,2,1), Time.local(2016,3,1)).floor(:week).advance(:hour, 10).call
         
     | 
| 
      
 114 
     | 
    
         
            +
            # # => [2015-12-28 10:00:00 +0200, 2016-02-01 10:00:00 +0200, 2016-02-29 10:00:00 +0200]
         
     | 
| 
      
 115 
     | 
    
         
            +
            #
         
     | 
| 
      
 116 
     | 
    
         
            +
            # # Or, the most fun, you can create complicated operation and call it
         
     | 
| 
      
 117 
     | 
    
         
            +
            # # later:
         
     | 
| 
      
 118 
     | 
    
         
            +
            # op = TimeMath().floor(:week).advance(:hour, 10)
         
     | 
| 
      
 119 
     | 
    
         
            +
            # # => #<TimeMath::Op floor(:week).advance(:hour, 10)>
         
     | 
| 
      
 120 
     | 
    
         
            +
            # op.call(Time.now)
         
     | 
| 
      
 121 
     | 
    
         
            +
            # # => 2016-06-20 10:00:00 +0300
         
     | 
| 
      
 122 
     | 
    
         
            +
            #
         
     | 
| 
      
 123 
     | 
    
         
            +
            # # or even as a lambda:
         
     | 
| 
      
 124 
     | 
    
         
            +
            # times = [Time.local(2016,1,1), Time.local(2016,2,1), Time.local(2016,3,1)]
         
     | 
| 
      
 125 
     | 
    
         
            +
            # times.map(&op)
         
     | 
| 
      
 126 
     | 
    
         
            +
            # # => [2015-12-28 10:00:00 +0200, 2016-02-01 10:00:00 +0200, 2016-02-29 10:00:00 +0200]
         
     | 
| 
      
 127 
     | 
    
         
            +
            # ```
         
     | 
| 
      
 128 
     | 
    
         
            +
            #
         
     | 
| 
      
 129 
     | 
    
         
            +
            # See also {TimeMath::Op} for list of operations available, but basically
         
     | 
| 
      
 130 
     | 
    
         
            +
            # they are all same you can call on {TimeMath::Unit}, just pass unit symbol
         
     | 
| 
      
 131 
     | 
    
         
            +
            # as a first argument.
         
     | 
| 
      
 132 
     | 
    
         
            +
            #
         
     | 
| 
      
 133 
     | 
    
         
            +
            # @param arguments time-y value, or list of them, or nothing
         
     | 
| 
      
 134 
     | 
    
         
            +
            #
         
     | 
| 
      
 135 
     | 
    
         
            +
            # @return [TimeMath::Op]
         
     | 
| 
      
 136 
     | 
    
         
            +
            def TimeMath(*arguments) # rubocop:disable Style/MethodName
         
     | 
| 
      
 137 
     | 
    
         
            +
              TimeMath::Op.new(*arguments)
         
     | 
| 
      
 138 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/time_math/op.rb
    ADDED
    
    | 
         @@ -0,0 +1,209 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module TimeMath
         
     | 
| 
      
 2 
     | 
    
         
            +
              # `Op` is value object, incapsulating several operations performed on
         
     | 
| 
      
 3 
     | 
    
         
            +
              # time unit. The names of operations are the same the single unit can
         
     | 
| 
      
 4 
     | 
    
         
            +
              # perform, first parameter is always a unit.
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Ops can be created by `TimeMath::Op.new` or with pretty shortcut
         
     | 
| 
      
 7 
     | 
    
         
            +
              # `TimeMath()`.
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Available usages:
         
     | 
| 
      
 10 
     | 
    
         
            +
              #
         
     | 
| 
      
 11 
     | 
    
         
            +
              # ```ruby
         
     | 
| 
      
 12 
     | 
    
         
            +
              # # 1. chain operations:
         
     | 
| 
      
 13 
     | 
    
         
            +
              # # without Op: 10:25 at first day of next week:
         
     | 
| 
      
 14 
     | 
    
         
            +
              # TimeMath.min.advance(TimeMath.hour.advance(TimeMath.week.ceil(tm), 10), 25)
         
     | 
| 
      
 15 
     | 
    
         
            +
              # # FOOOOOO
         
     | 
| 
      
 16 
     | 
    
         
            +
              # # ...but with Op:
         
     | 
| 
      
 17 
     | 
    
         
            +
              # TimeMath(tm).ceil(:week).advance(:hour, 10).advance(:min, 25).call
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              # # 2. chain operations on multiple objects:
         
     | 
| 
      
 20 
     | 
    
         
            +
              # TimeMath(tm1, tm2, tm3).ceil(:week).advance(:hour, 10).advance(:min, 25).call
         
     | 
| 
      
 21 
     | 
    
         
            +
              # # or
         
     | 
| 
      
 22 
     | 
    
         
            +
              # TimeMath([array_of_times]).ceil(:week).advance(:hour, 10).advance(:min, 25).call
         
     | 
| 
      
 23 
     | 
    
         
            +
              #
         
     | 
| 
      
 24 
     | 
    
         
            +
              # # 3. preparing operation to be used on any objects:
         
     | 
| 
      
 25 
     | 
    
         
            +
              # op = TimeMath().ceil(:week).advance(:hour, 10).advance(:min, 25)
         
     | 
| 
      
 26 
     | 
    
         
            +
              # op.call(tm)
         
     | 
| 
      
 27 
     | 
    
         
            +
              # op.call(tm1, tm2, tm3)
         
     | 
| 
      
 28 
     | 
    
         
            +
              # op.call(array_of_times)
         
     | 
| 
      
 29 
     | 
    
         
            +
              # # or even block-ish behavior:
         
     | 
| 
      
 30 
     | 
    
         
            +
              # [tm1, tm2, tm3].map(&op)
         
     | 
| 
      
 31 
     | 
    
         
            +
              # ```
         
     | 
| 
      
 32 
     | 
    
         
            +
              #
         
     | 
| 
      
 33 
     | 
    
         
            +
              # Note that Op also plays well with {Sequence} (see its docs for more).
         
     | 
| 
      
 34 
     | 
    
         
            +
              class Op
         
     | 
| 
      
 35 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 36 
     | 
    
         
            +
                OPERATIONS = [:floor, :ceil, :round, :next, :prev, :advance, :decrease].freeze
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                attr_reader :operations, :arguments
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                # Creates Op. Could (and recommended be also by its alias -- just
         
     | 
| 
      
 41 
     | 
    
         
            +
                # `TimeMath(*arguments)`.
         
     | 
| 
      
 42 
     | 
    
         
            +
                #
         
     | 
| 
      
 43 
     | 
    
         
            +
                # @param arguments one, or several, or an array of time-y values
         
     | 
| 
      
 44 
     | 
    
         
            +
                #   (Time, Date, DateTime).
         
     | 
| 
      
 45 
     | 
    
         
            +
                def initialize(*arguments)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @arguments = arguments
         
     | 
| 
      
 47 
     | 
    
         
            +
                  @operations = []
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 51 
     | 
    
         
            +
                def initialize_copy(other)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @arguments = other.arguments.dup
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @operations = other.operations.dup
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                # @method floor!(unit, span = 1)
         
     | 
| 
      
 57 
     | 
    
         
            +
                #   Adds {Units::Base#floor} to list of operations.
         
     | 
| 
      
 58 
     | 
    
         
            +
                #
         
     | 
| 
      
 59 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 60 
     | 
    
         
            +
                #   @param span [Numeric] how many units to floor to.
         
     | 
| 
      
 61 
     | 
    
         
            +
                #   @return [self]
         
     | 
| 
      
 62 
     | 
    
         
            +
                #
         
     | 
| 
      
 63 
     | 
    
         
            +
                # @method floor(unit, span = 1)
         
     | 
| 
      
 64 
     | 
    
         
            +
                #   Non-destructive version of {#floor!}.
         
     | 
| 
      
 65 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 66 
     | 
    
         
            +
                #   @param span [Numeric] how many units to floor to.
         
     | 
| 
      
 67 
     | 
    
         
            +
                #   @return [Op]
         
     | 
| 
      
 68 
     | 
    
         
            +
                #
         
     | 
| 
      
 69 
     | 
    
         
            +
                # @method ceil!(unit, span = 1)
         
     | 
| 
      
 70 
     | 
    
         
            +
                #   Adds {Units::Base#ceil} to list of operations.
         
     | 
| 
      
 71 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 72 
     | 
    
         
            +
                #   @param span [Numeric] how many units to ceil to.
         
     | 
| 
      
 73 
     | 
    
         
            +
                #   @return [self]
         
     | 
| 
      
 74 
     | 
    
         
            +
                #
         
     | 
| 
      
 75 
     | 
    
         
            +
                # @method ceil(unit, span = 1)
         
     | 
| 
      
 76 
     | 
    
         
            +
                #   Non-destructive version of {#ceil!}.
         
     | 
| 
      
 77 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 78 
     | 
    
         
            +
                #   @param span [Numeric] how many units to ceil to.
         
     | 
| 
      
 79 
     | 
    
         
            +
                #   @return [Op]
         
     | 
| 
      
 80 
     | 
    
         
            +
                #
         
     | 
| 
      
 81 
     | 
    
         
            +
                # @method round!(unit, span = 1)
         
     | 
| 
      
 82 
     | 
    
         
            +
                #   Adds {Units::Base#round} to list of operations.
         
     | 
| 
      
 83 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 84 
     | 
    
         
            +
                #   @param span [Numeric] how many units to round to.
         
     | 
| 
      
 85 
     | 
    
         
            +
                #   @return [self]
         
     | 
| 
      
 86 
     | 
    
         
            +
                #
         
     | 
| 
      
 87 
     | 
    
         
            +
                # @method round(unit, span = 1)
         
     | 
| 
      
 88 
     | 
    
         
            +
                #   Non-destructive version of {#round!}.
         
     | 
| 
      
 89 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 90 
     | 
    
         
            +
                #   @param span [Numeric] how many units to round to.
         
     | 
| 
      
 91 
     | 
    
         
            +
                #   @return [Op]
         
     | 
| 
      
 92 
     | 
    
         
            +
                #
         
     | 
| 
      
 93 
     | 
    
         
            +
                # @method next!(unit, span = 1)
         
     | 
| 
      
 94 
     | 
    
         
            +
                #   Adds {Units::Base#next} to list of operations.
         
     | 
| 
      
 95 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 96 
     | 
    
         
            +
                #   @param span [Numeric] how many units to ceil to.
         
     | 
| 
      
 97 
     | 
    
         
            +
                #   @return [self]
         
     | 
| 
      
 98 
     | 
    
         
            +
                #
         
     | 
| 
      
 99 
     | 
    
         
            +
                # @method next(unit, span = 1)
         
     | 
| 
      
 100 
     | 
    
         
            +
                #   Non-destructive version of {#next!}.
         
     | 
| 
      
 101 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 102 
     | 
    
         
            +
                #   @param span [Numeric] how many units to ceil to.
         
     | 
| 
      
 103 
     | 
    
         
            +
                #   @return [Op]
         
     | 
| 
      
 104 
     | 
    
         
            +
                #
         
     | 
| 
      
 105 
     | 
    
         
            +
                # @method prev!(unit, span = 1)
         
     | 
| 
      
 106 
     | 
    
         
            +
                #   Adds {Units::Base#prev} to list of operations.
         
     | 
| 
      
 107 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 108 
     | 
    
         
            +
                #   @param span [Numeric] how many units to floor to.
         
     | 
| 
      
 109 
     | 
    
         
            +
                #   @return [self]
         
     | 
| 
      
 110 
     | 
    
         
            +
                #
         
     | 
| 
      
 111 
     | 
    
         
            +
                # @method prev(unit, span = 1)
         
     | 
| 
      
 112 
     | 
    
         
            +
                #   Non-destructive version of {#prev!}.
         
     | 
| 
      
 113 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 114 
     | 
    
         
            +
                #   @param span [Numeric] how many units to floor to.
         
     | 
| 
      
 115 
     | 
    
         
            +
                #   @return [Op]
         
     | 
| 
      
 116 
     | 
    
         
            +
                #
         
     | 
| 
      
 117 
     | 
    
         
            +
                # @method advance!(unit, amount = 1)
         
     | 
| 
      
 118 
     | 
    
         
            +
                #   Adds {Units::Base#advance} to list of operations.
         
     | 
| 
      
 119 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 120 
     | 
    
         
            +
                #   @param amount [Numeric] how many units to advance.
         
     | 
| 
      
 121 
     | 
    
         
            +
                #   @return [self]
         
     | 
| 
      
 122 
     | 
    
         
            +
                #
         
     | 
| 
      
 123 
     | 
    
         
            +
                # @method advance(unit, amount = 1)
         
     | 
| 
      
 124 
     | 
    
         
            +
                #   Non-destructive version of {#advance!}.
         
     | 
| 
      
 125 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 126 
     | 
    
         
            +
                #   @param amount [Numeric] how many units to advance.
         
     | 
| 
      
 127 
     | 
    
         
            +
                #   @return [Op]
         
     | 
| 
      
 128 
     | 
    
         
            +
                #
         
     | 
| 
      
 129 
     | 
    
         
            +
                # @method decrease!(unit, amount = 1)
         
     | 
| 
      
 130 
     | 
    
         
            +
                #   Adds {Units::Base#decrease} to list of operations.
         
     | 
| 
      
 131 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 132 
     | 
    
         
            +
                #   @param amount [Numeric] how many units to decrease.
         
     | 
| 
      
 133 
     | 
    
         
            +
                #   @return [self]
         
     | 
| 
      
 134 
     | 
    
         
            +
                #
         
     | 
| 
      
 135 
     | 
    
         
            +
                # @method decrease(unit, amount = 1)
         
     | 
| 
      
 136 
     | 
    
         
            +
                #   Non-destructive version of {#decrease!}.
         
     | 
| 
      
 137 
     | 
    
         
            +
                #   @param unit [Symbol] One of {TimeMath.units}
         
     | 
| 
      
 138 
     | 
    
         
            +
                #   @param amount [Numeric] how many units to decrease.
         
     | 
| 
      
 139 
     | 
    
         
            +
                #   @return [Op]
         
     | 
| 
      
 140 
     | 
    
         
            +
                #
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                OPERATIONS.each do |op|
         
     | 
| 
      
 143 
     | 
    
         
            +
                  define_method "#{op}!" do |unit, *args|
         
     | 
| 
      
 144 
     | 
    
         
            +
                    Units.names.include?(unit) or raise(ArgumentError, "Unknown unit #{unit}")
         
     | 
| 
      
 145 
     | 
    
         
            +
                    @operations << [op, unit, args]
         
     | 
| 
      
 146 
     | 
    
         
            +
                    self
         
     | 
| 
      
 147 
     | 
    
         
            +
                  end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                  define_method op do |unit, *args|
         
     | 
| 
      
 150 
     | 
    
         
            +
                    dup.send("#{op}!", unit, *args)
         
     | 
| 
      
 151 
     | 
    
         
            +
                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
                end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                def inspect
         
     | 
| 
      
 155 
     | 
    
         
            +
                  "#<#{self.class}#{inspect_args}" + inspect_operations + '>'
         
     | 
| 
      
 156 
     | 
    
         
            +
                end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                # @private
         
     | 
| 
      
 159 
     | 
    
         
            +
                def inspect_operations
         
     | 
| 
      
 160 
     | 
    
         
            +
                  operations.map { |op, unit, args|
         
     | 
| 
      
 161 
     | 
    
         
            +
                    "#{op}(#{[unit, *args].map(&:inspect).join(', ')})"
         
     | 
| 
      
 162 
     | 
    
         
            +
                  }.join('.')
         
     | 
| 
      
 163 
     | 
    
         
            +
                end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                def ==(other)
         
     | 
| 
      
 166 
     | 
    
         
            +
                  self.class == other.class && operations == other.operations &&
         
     | 
| 
      
 167 
     | 
    
         
            +
                    arguments == other.arguments
         
     | 
| 
      
 168 
     | 
    
         
            +
                end
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                # Performs op. If an Op was created with arguments, just performs all
         
     | 
| 
      
 171 
     | 
    
         
            +
                # operations on them and returns the result. If it was created without
         
     | 
| 
      
 172 
     | 
    
         
            +
                # arguments, performs all operations on arguments provided to `call`.
         
     | 
| 
      
 173 
     | 
    
         
            +
                #
         
     | 
| 
      
 174 
     | 
    
         
            +
                # @param tms one, or several, or an array of time-y values; should not
         
     | 
| 
      
 175 
     | 
    
         
            +
                #   be passed if Op was created with arguments.
         
     | 
| 
      
 176 
     | 
    
         
            +
                # @return [Time,Date,DateTime,Array] one, or an array of processed arguments
         
     | 
| 
      
 177 
     | 
    
         
            +
                def call(*tms)
         
     | 
| 
      
 178 
     | 
    
         
            +
                  unless @arguments.empty?
         
     | 
| 
      
 179 
     | 
    
         
            +
                    tms.empty? or raise(ArgumentError, 'Op arguments is already set, use call()')
         
     | 
| 
      
 180 
     | 
    
         
            +
                    tms = @arguments
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
                  res = [*tms].flatten.map(&method(:perform))
         
     | 
| 
      
 183 
     | 
    
         
            +
                  tms.count == 1 && Util.timey?(tms.first) ? res.first : res
         
     | 
| 
      
 184 
     | 
    
         
            +
                end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                # Allows to use Op as a block:
         
     | 
| 
      
 187 
     | 
    
         
            +
                #
         
     | 
| 
      
 188 
     | 
    
         
            +
                # ```ruby
         
     | 
| 
      
 189 
     | 
    
         
            +
                # timestamps.map(&TimeMath().ceil(:week).advance(:day, 1))
         
     | 
| 
      
 190 
     | 
    
         
            +
                # ```
         
     | 
| 
      
 191 
     | 
    
         
            +
                # @return [Proc]
         
     | 
| 
      
 192 
     | 
    
         
            +
                def to_proc
         
     | 
| 
      
 193 
     | 
    
         
            +
                  method(:call).to_proc
         
     | 
| 
      
 194 
     | 
    
         
            +
                end
         
     | 
| 
      
 195 
     | 
    
         
            +
             
     | 
| 
      
 196 
     | 
    
         
            +
                private
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                def inspect_args
         
     | 
| 
      
 199 
     | 
    
         
            +
                  return ' ' if @arguments.empty?
         
     | 
| 
      
 200 
     | 
    
         
            +
                  '(' + [*@arguments].map(&:inspect).join(', ') + ').'
         
     | 
| 
      
 201 
     | 
    
         
            +
                end
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
                def perform(tm)
         
     | 
| 
      
 204 
     | 
    
         
            +
                  operations.inject(tm) { |memo, (op, unit, args)|
         
     | 
| 
      
 205 
     | 
    
         
            +
                    TimeMath::Units.get(unit).send(op, memo, *args)
         
     | 
| 
      
 206 
     | 
    
         
            +
                  }
         
     | 
| 
      
 207 
     | 
    
         
            +
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
              end
         
     | 
| 
      
 209 
     | 
    
         
            +
            end
         
     |