time_spanner 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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