time_spanner 1.0.0

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +13 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +60 -0
  8. data/Rakefile +11 -0
  9. data/lib/time_spanner.rb +8 -0
  10. data/lib/time_spanner/duration_chain.rb +55 -0
  11. data/lib/time_spanner/errors.rb +8 -0
  12. data/lib/time_spanner/time_span.rb +34 -0
  13. data/lib/time_spanner/time_unit_collector.rb +59 -0
  14. data/lib/time_spanner/time_units.rb +17 -0
  15. data/lib/time_spanner/time_units/base/calendar_unit.rb +30 -0
  16. data/lib/time_spanner/time_units/base/time_unit.rb +39 -0
  17. data/lib/time_spanner/time_units/base/unit.rb +31 -0
  18. data/lib/time_spanner/time_units/century.rb +30 -0
  19. data/lib/time_spanner/time_units/day.rb +25 -0
  20. data/lib/time_spanner/time_units/decade.rb +25 -0
  21. data/lib/time_spanner/time_units/hour.rb +15 -0
  22. data/lib/time_spanner/time_units/microsecond.rb +15 -0
  23. data/lib/time_spanner/time_units/millennium.rb +25 -0
  24. data/lib/time_spanner/time_units/millisecond.rb +15 -0
  25. data/lib/time_spanner/time_units/minute.rb +15 -0
  26. data/lib/time_spanner/time_units/month.rb +25 -0
  27. data/lib/time_spanner/time_units/nanosecond.rb +15 -0
  28. data/lib/time_spanner/time_units/second.rb +15 -0
  29. data/lib/time_spanner/time_units/week.rb +25 -0
  30. data/lib/time_spanner/time_units/year.rb +25 -0
  31. data/lib/time_spanner/version.rb +3 -0
  32. data/test/test_helper.rb +9 -0
  33. data/test/time_spanner/duration_chain_test.rb +267 -0
  34. data/test/time_spanner/time_spanner_test.rb +27 -0
  35. data/test/time_spanner/time_unit_collector_test.rb +66 -0
  36. data/test/time_spanner/time_units/base/calendar_unit_test.rb +17 -0
  37. data/test/time_spanner/time_units/base/time_unit_test.rb +18 -0
  38. data/test/time_spanner/time_units/base/unit_test.rb +37 -0
  39. data/test/time_spanner/time_units/century_test.rb +55 -0
  40. data/test/time_spanner/time_units/day_test.rb +84 -0
  41. data/test/time_spanner/time_units/decade_test.rb +67 -0
  42. data/test/time_spanner/time_units/hour_test.rb +56 -0
  43. data/test/time_spanner/time_units/microsecond_test.rb +56 -0
  44. data/test/time_spanner/time_units/millennium_test.rb +69 -0
  45. data/test/time_spanner/time_units/millisecond_test.rb +56 -0
  46. data/test/time_spanner/time_units/minute_test.rb +84 -0
  47. data/test/time_spanner/time_units/month_test.rb +68 -0
  48. data/test/time_spanner/time_units/nanosecond_test.rb +40 -0
  49. data/test/time_spanner/time_units/second_test.rb +56 -0
  50. data/test/time_spanner/time_units/week_test.rb +55 -0
  51. data/test/time_spanner/time_units/year_test.rb +91 -0
  52. data/time_spanner.gemspec +23 -0
  53. metadata +143 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6e2463079adde3dea23bf0f4ec07643d7f7b8287
4
+ data.tar.gz: 03b8c9c82c323a7c13a6da91e2fb08c6154ee798
5
+ SHA512:
6
+ metadata.gz: 5714eae36ccaa238f3b2b4fcde65fb0032e4b1dcb3fb0cc7ff2683ad76fd6a9bd9a84ac189905642b37b8e0c442e11da34b11a5552f8bef9a912391691e8b53d
7
+ data.tar.gz: 691f5e479c64690ba0e102d72b6569e68dce4ecacd60430b55533dc313d67312414e7f1a2234f8657e3ed4af2428661c1145774b5a2c766f37a4caf57797019b
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/
@@ -0,0 +1 @@
1
+ time_spanner
@@ -0,0 +1 @@
1
+ 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in time_spanner.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ platforms :mswin, :mingw do
8
+ gem 'win32console', '1.3.0'
9
+ end
10
+
11
+ gem 'minitest'
12
+ gem 'minitest-reporters'
13
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 TODO: Write your name
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,60 @@
1
+ time_spanner
2
+ ============
3
+
4
+ Returns a time span splitted in desired units given two timestamps.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'time_spanner'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install time_spanner
19
+
20
+ ## Usage
21
+
22
+ ```ruby
23
+ TimeSpan.new Time.parse('2012-04-05 07:05:05'), Time.parse('3024-11-14 12:06:49')
24
+ ```
25
+ returns:
26
+
27
+ ```ruby
28
+ {:millenniums=>1, :centuries=>0, :decades=>1, :years=>2, :months=>7, :weeks=>1, :days=>2, :hours=>6, :minutes=>1, :seconds=>44, :milliseconds=>0, :microseconds=>0, :nanoseconds=>0}
29
+ ```
30
+
31
+ ## Specifying units
32
+
33
+ Define which time units should be calculated.
34
+ Pass them within an Array as a third parameter.
35
+
36
+ Available units are:
37
+ <pre>:millenniums, :centuries, :decades, :years, :months, :weeks, :days, :hours, :minutes, :seconds, :milliseconds, :microseconds, :nanoseconds</pre>
38
+
39
+ If no units are supplied it defaults to all units.
40
+
41
+
42
+ ### Example
43
+
44
+ ```ruby
45
+ TimeSpan.new Time.parse('2012-04-05 07:05:05'), Time.at(Time.parse(2012-04-05 07:10:12', 1243.345), [:seconds, :milliseconds, :microseconds, :nanoseconds])
46
+ ```
47
+ returns:
48
+
49
+ ```ruby
50
+ {:seconds=>307, :milliseconds=>1, :microseconds=>243, :nanoseconds=>345}
51
+ ```
52
+
53
+
54
+ ## Contributing
55
+
56
+ 1. Fork it
57
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
58
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
59
+ 4. Push to the branch (`git push origin my-new-feature`)
60
+ 5. Create new Pull Request
@@ -0,0 +1,11 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ desc 'Run tests'
5
+ task :default => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << 'test'
9
+ t.pattern = 'test/**/*_test.rb'
10
+ t.verbose = true
11
+ end
@@ -0,0 +1,8 @@
1
+ require 'date'
2
+ require 'time'
3
+ require 'forwardable'
4
+ require 'time_spanner/errors'
5
+ require 'time_spanner/time_units'
6
+ require 'time_spanner/time_unit_collector'
7
+ require 'time_spanner/duration_chain'
8
+ require 'time_spanner/time_span'
@@ -0,0 +1,55 @@
1
+ module TimeSpanner
2
+
3
+ class DurationChain
4
+ extend Forwardable
5
+ include Enumerable
6
+ include TimeUnits
7
+
8
+ attr_accessor :remaining, :reverse, :units
9
+ attr_reader :to
10
+ alias :reverse? :reverse
11
+
12
+ def_delegator :units, :each
13
+
14
+ def initialize from, to, units
15
+ @from, @to = setup_times from, to
16
+ @remaining = to.to_r - from.to_r
17
+ @units = units.map &:new
18
+
19
+ calculate!
20
+ end
21
+
22
+ private
23
+
24
+ # Perform duration calculations for units in chain.
25
+ def calculate!
26
+ sort!
27
+
28
+ each do |unit|
29
+ calculate_unit unit
30
+ end
31
+ end
32
+
33
+ def calculate_unit unit
34
+ unit.calculate remaining, to
35
+ unit.reverse! if reverse?
36
+
37
+ @remaining = unit.rest
38
+ end
39
+
40
+ # Units must be sorted to perform a correct calculation chain.
41
+ def sort!
42
+ @units = units.sort
43
+ end
44
+
45
+ def setup_times from, to
46
+ @reverse = to < from
47
+ new_from = reverse? ? to : from
48
+ new_to = reverse? ? from : to
49
+
50
+ [ new_from, new_to ]
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,8 @@
1
+ module TimeSpanner
2
+ module Errors
3
+
4
+ class InvalidClassError < StandardError; end
5
+ class InvalidUnitError < StandardError; end
6
+
7
+ end
8
+ end
@@ -0,0 +1,34 @@
1
+ module TimeSpanner
2
+
3
+ class TimeSpan < Hash
4
+ include Errors
5
+
6
+ attr_reader :unit_chain
7
+
8
+ def initialize from, to, unit_names = []
9
+ validate! from, to
10
+
11
+ units = TimeUnitCollector.new( unit_names ).units
12
+ @unit_chain = DurationChain.new from.to_time, to.to_time, units
13
+
14
+ build!
15
+ end
16
+
17
+
18
+ private
19
+
20
+ def build!
21
+ unit_chain.each do |unit|
22
+ self[ unit.plural_name ] = unit.amount
23
+ end
24
+ end
25
+
26
+ def validate! from, to
27
+ unless [ from, to ].all? { |obj| obj.is_a?( Time ) || ( obj.respond_to?( :to_time ) && obj.to_time.is_a?( Time ) ) }
28
+ raise InvalidClassError, "Must convert to Time object!"
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,59 @@
1
+ module TimeSpanner
2
+
3
+ class TimeUnitCollector
4
+ include TimeUnits
5
+ include Errors
6
+
7
+ AVAILABLE_UNITS = [ :millenniums, :centuries, :decades, :years, :months, :weeks, :days, :hours, :minutes, :seconds, :milliseconds, :microseconds, :nanoseconds ]
8
+
9
+ attr_reader :unit_names
10
+ attr_accessor :units
11
+
12
+ def initialize unit_names = []
13
+ @unit_names = collect_unit_names unit_names
14
+ @units = []
15
+
16
+ validate_unit_names!
17
+ collect!
18
+ end
19
+
20
+
21
+ private
22
+
23
+ def collect!
24
+ unit_names.each do |name|
25
+ units << ( unit_by_name name )
26
+ end
27
+ end
28
+
29
+ def unit_by_name name
30
+ case name
31
+ when :millenniums then Millennium
32
+ when :centuries then Century
33
+ when :decades then Decade
34
+ when :years then Year
35
+ when :months then Month
36
+ when :weeks then Week
37
+ when :days then Day
38
+ when :hours then Hour
39
+ when :minutes then Minute
40
+ when :seconds then Second
41
+ when :milliseconds then Millisecond
42
+ when :microseconds then Microsecond
43
+ when :nanoseconds then Nanosecond
44
+ end
45
+ end
46
+
47
+ def collect_unit_names unit_names
48
+ !unit_names || unit_names.compact.empty? ? AVAILABLE_UNITS : unit_names
49
+ end
50
+
51
+ def validate_unit_names!
52
+ unit_names.each do |unit_name|
53
+ raise InvalidUnitError, "Unit '#{unit_name}' is not a valid time unit." unless AVAILABLE_UNITS.include? unit_name
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -0,0 +1,17 @@
1
+ require 'time_spanner/time_units/base/unit'
2
+ require 'time_spanner/time_units/base/calendar_unit'
3
+ require 'time_spanner/time_units/base/time_unit'
4
+
5
+ require 'time_spanner/time_units/nanosecond'
6
+ require 'time_spanner/time_units/microsecond'
7
+ require 'time_spanner/time_units/millisecond'
8
+ require 'time_spanner/time_units/second'
9
+ require 'time_spanner/time_units/minute'
10
+ require 'time_spanner/time_units/hour'
11
+ require 'time_spanner/time_units/day'
12
+ require 'time_spanner/time_units/week'
13
+ require 'time_spanner/time_units/month'
14
+ require 'time_spanner/time_units/year'
15
+ require 'time_spanner/time_units/decade'
16
+ require 'time_spanner/time_units/century'
17
+ require 'time_spanner/time_units/millennium'
@@ -0,0 +1,30 @@
1
+ module TimeSpanner
2
+ module TimeUnits
3
+
4
+ class CalendarUnit < Unit
5
+
6
+ attr_reader :from, :to
7
+
8
+ def initialize position
9
+ super position
10
+ end
11
+
12
+ def calculate duration, to
13
+ @to = to
14
+ @from = @to - duration.to_r
15
+
16
+ calculate_amount
17
+ calculate_rest at_amount( amount )
18
+ end
19
+
20
+
21
+ private
22
+
23
+ def calculate_rest from
24
+ @rest = to.to_r - from.to_r
25
+ end
26
+
27
+ end
28
+ end
29
+
30
+ end
@@ -0,0 +1,39 @@
1
+ module TimeSpanner
2
+ module TimeUnits
3
+
4
+ class TimeUnit < Unit
5
+
6
+ attr_reader :multiplier
7
+
8
+ def initialize position, multiplier
9
+ super position
10
+
11
+ @multiplier = multiplier
12
+ end
13
+
14
+ def calculate duration, to = nil
15
+ @duration = duration
16
+
17
+ calculate_amount
18
+ calculate_rest
19
+ end
20
+
21
+
22
+ private
23
+
24
+ def calculate_amount
25
+ @amount = ( ( duration * multiplier ).round 13 ).to_i
26
+ end
27
+
28
+ def calculate_rest
29
+ @rest = duration - amount_in_seconds
30
+ end
31
+
32
+ def amount_in_seconds
33
+ amount.to_r / multiplier
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,31 @@
1
+ module TimeSpanner
2
+ module TimeUnits
3
+
4
+ class Unit
5
+ include Comparable
6
+
7
+ attr_reader :position
8
+ attr_accessor :duration, :amount, :rest
9
+
10
+ def initialize position
11
+ @position = position
12
+ @amount = 0
13
+ @rest = 0
14
+ end
15
+
16
+ def reverse!
17
+ @amount = -amount
18
+ end
19
+
20
+ def <=> other
21
+ position <=> other.position
22
+ end
23
+
24
+ def plural_name
25
+ "#{self.class.name.split('::').last.downcase}s".to_sym
26
+ end
27
+
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,30 @@
1
+ module TimeSpanner
2
+ module TimeUnits
3
+
4
+ class Century < CalendarUnit
5
+
6
+ def initialize
7
+ super 2
8
+ end
9
+
10
+ # Override!
11
+ def plural_name
12
+ :centuries
13
+ end
14
+
15
+
16
+ private
17
+
18
+ def calculate_amount
19
+ amount = ( to.year - from.year ) / 100
20
+ @amount = at_amount( amount ) > to ? amount - 1 : amount
21
+ end
22
+
23
+ def at_amount amount
24
+ ( from.to_datetime >> amount * 1200 ).to_time
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end