time_clock 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 16d6a4d558f242cca80a3c248bcb66a3a47ab024
4
+ data.tar.gz: 5c71d2e18dc9b22873b464eb823f4207a1053ea9
5
+ SHA512:
6
+ metadata.gz: 14622660e5e94c0fe9dd90c3384ef646c8fdea527ea848259691bc7e0d4a2e4c64af0a0920feaa28ae0794288c5eff58daab6c262ae3e88af144977e4bd96759
7
+ data.tar.gz: cf7839ecd504452949a56e8310d262982f6fd4a63a6407fd1ba5d3b5d648a8dfe754de1f0641a86dc7fb11d5ccef80049894d13d0204fdf66c83c2007f514456
data/.gitignore ADDED
@@ -0,0 +1,17 @@
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
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in time_clock.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 barelyknown
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.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # TimeClock
2
+
3
+ gem 'time_clock'
4
+
5
+ Calculate the amount of business time between two times based on any arbitrary work calendar.
6
+
7
+ Time.now.business_seconds_until(Time.now + 1.day)
8
+ => 43200
9
+ Time.now.business_minutes_until(Time.now + 1.day)
10
+ => 720
11
+ Time.now.business_hours_until(Time.now + 1.day)
12
+ => 12
13
+
14
+ Things you may like about this library:
15
+
16
+ - No dependencies.
17
+ - Extremely simple codebase. Only 80 lines of code.
18
+ - Works with any kind of `Time`, including mixed types.
19
+ - Easy to customize with totally custom business calendars.
20
+
21
+ ## Configuration
22
+
23
+ Set a `default_calendar` for all comparisons to use for their calculation.
24
+
25
+ For example, in a Rails initializer:
26
+
27
+ # Time zones aren't required. Used here to make it easier to read.
28
+ time_zone = ActiveSupport::TimeZone.new("America/Chicago")
29
+
30
+ # The TimeClock::Calendar object stores the shifts for the business calendar.
31
+ calendar = TimeClock::Calendar.new
32
+
33
+ # Add all the work days (or individual shifts) to the calendar
34
+ # This example adds non week days between 6am and 6pm.
35
+ (Date.new(2011,1,1)..Date.new(2015,1,1)).each do |date|
36
+ next unless (1..5).cover?(date.wday)
37
+ calendar.add_shift(
38
+ TimeClock::Shift.new(
39
+ time_zone.local(date.year,date.month,date.day,6),
40
+ time_zone.local(date.year,date.month,date.day,18),
41
+ )
42
+ )
43
+ end
44
+
45
+ # Set the default calendar
46
+ TimeClock.default_calendar = calendar
47
+
48
+ ## Calendars
49
+
50
+ If you want to use a separate calendar for an individual calculation, use a `TimeClock::Comparison` instance. Refer back to the configuration example to understand how to build a calendar.
51
+
52
+ TimeClock::Comparison.new(Time.now, Time.now + 1.day, custom_calendar).seconds
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
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ module TimeClock
2
+ class Calendar
3
+ class OverlappingShiftError < ArgumentError; end
4
+
5
+ attr_reader :shifts
6
+
7
+ def initialize
8
+ @shifts = []
9
+ end
10
+
11
+ def add_shift(new_shift)
12
+ starting_shift_count = @shifts.size
13
+ @shifts.each_with_index do |shift, i|
14
+ raise OverlappingShiftError if new_shift.overlaps?(shift)
15
+ break @shifts.insert(i, new_shift) if shift.start_time > new_shift.end_time
16
+ end
17
+ @shifts << new_shift unless @shifts.size > starting_shift_count
18
+ @shifts
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ module TimeClock
2
+ class Comparison
3
+
4
+ attr_reader :start_time, :end_time, :calendar
5
+
6
+ def initialize(start_time, end_time, calendar=TimeClock.default_calendar)
7
+ @start_time, @end_time, @calendar = start_time, end_time, calendar
8
+ end
9
+
10
+ def period
11
+ @period ||= Shift.new(start_time, end_time)
12
+ end
13
+
14
+ def seconds
15
+ calendar.shifts.inject(0) do |total_seconds, shift|
16
+ total_seconds + period.overlapping_seconds(shift)
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ class Time
2
+
3
+ def business_seconds_until(time)
4
+ TimeClock::Comparison.new(self, time).seconds.to_i
5
+ end
6
+
7
+ def business_minutes_until(time)
8
+ (business_seconds_until(time).to_f / 60).ceil
9
+ end
10
+
11
+ def business_hours_until(time)
12
+ (business_seconds_until(time).to_f / 3600).ceil
13
+ end
14
+
15
+ end
@@ -0,0 +1,25 @@
1
+ module TimeClock
2
+ class Shift
3
+ class EndTimeAfterStartTimeError < ArgumentError; end
4
+
5
+ attr_reader :start_time, :end_time
6
+
7
+ def initialize(start_time, end_time)
8
+ raise EndTimeAfterStartTimeError unless end_time > start_time
9
+ @start_time, @end_time = start_time, end_time
10
+ end
11
+
12
+ def overlaps?(shift)
13
+ start_time <= shift.end_time && end_time >= shift.start_time
14
+ end
15
+
16
+ def overlapping_seconds(shift)
17
+ [[end_time,shift.end_time].min - [start_time,shift.start_time].max,0].max
18
+ end
19
+
20
+ def ==(value)
21
+ start_time == value.start_time && end_time == value.end_time
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module TimeClock
2
+ VERSION = "0.0.1"
3
+ end
data/lib/time_clock.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "time_clock/version"
2
+
3
+ require "time_clock/core_ext/time"
4
+
5
+ require "time_clock/shift"
6
+ require "time_clock/calendar"
7
+ require "time_clock/comparison"
8
+
9
+ module TimeClock
10
+
11
+ def self.default_calendar=(value)
12
+ @default_calendar = value
13
+ end
14
+
15
+ def self.default_calendar
16
+ @default_calendar
17
+ end
18
+
19
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ module TimeClock
4
+ describe Calendar do
5
+
6
+ let(:base_shift) { Shift.new(Time.new(2013,8,19,6,0),Time.new(2013,8,19,18,0)) }
7
+
8
+ it "should have an array of shifts" do
9
+ expect(subject.shifts).to be_a_kind_of Array
10
+ end
11
+
12
+ context "with an empty array of shifts" do
13
+ it "should add a shift to the array" do
14
+ subject.add_shift(base_shift)
15
+ expect(subject.shifts).to include base_shift
16
+ end
17
+ end
18
+
19
+ context "after the base shift is added" do
20
+ before do
21
+ subject.add_shift(base_shift)
22
+ end
23
+
24
+ context "when adding a shift that should come before" do
25
+ let(:new_shift) { Shift.new(Time.new(2013,8,18,6,0),Time.new(2013,8,18,18)) }
26
+ it "adds it to the beginning" do
27
+ subject.add_shift(new_shift)
28
+ expect(subject.shifts.first).to eq new_shift
29
+ end
30
+ end
31
+
32
+ context "when adding a shift that should come after" do
33
+ let(:new_shift) { Shift.new(Time.new(2013,8,20,6,0),Time.new(2013,8,20,18,0))}
34
+ it "adds it to the end" do
35
+ subject.add_shift(new_shift)
36
+ expect(subject.shifts.last).to eq new_shift
37
+ end
38
+ end
39
+
40
+ context "when adding a shift that overlaps" do
41
+ let(:new_shift) { Shift.new(Time.new(2013,8,19,8),Time.new(2013,8,19,15)) }
42
+ it "should raise an error" do
43
+ expect{subject.add_shift(new_shift)}.to raise_error Calendar::OverlappingShiftError
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ module TimeClock
4
+ describe Comparison do
5
+
6
+ let(:start_time) { Time.now }
7
+ let(:end_time) { Time.now + 3600 }
8
+ let(:calendar) { Calendar.new }
9
+
10
+ subject do
11
+ described_class.new(start_time, end_time, calendar)
12
+ end
13
+
14
+ it "should set the start time" do
15
+ expect(subject.start_time).to eq start_time
16
+ end
17
+
18
+ it "should set the end time" do
19
+ expect(subject.end_time).to eq end_time
20
+ end
21
+
22
+ it "should set the calendar" do
23
+ expect(subject.calendar).to eq calendar
24
+ end
25
+
26
+ it "should create a period" do
27
+ expect(subject.period).to eq Shift.new(start_time, end_time)
28
+ end
29
+
30
+ context "when calculating seconds between" do
31
+ let(:calendar) do
32
+ Calendar.new.tap do |c|
33
+ c.add_shift(Shift.new(Time.new(2013,8,19,6),Time.new(2013,8,19,18)))
34
+ c.add_shift(Shift.new(Time.new(2013,8,20,6),Time.new(2013,8,20,18)))
35
+ c.add_shift(Shift.new(Time.new(2013,8,21,6),Time.new(2013,8,21,18)))
36
+ c.add_shift(Shift.new(Time.new(2013,8,23,6),Time.new(2013,8,23,18)))
37
+ end
38
+ end
39
+ context "when the times are within one shift" do
40
+ let(:start_time) { Time.new(2013,8,20,10) }
41
+ let(:end_time) { Time.new(2013,8,20,14) }
42
+ subject do
43
+ described_class.new(start_time, end_time, calendar)
44
+ end
45
+ it "should calculate seconds between" do
46
+ expect(subject.seconds).to eq 60 * 60 * 4
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Time do
4
+ subject { Time.new(2013,8,13,14,15) }
5
+
6
+ it "should respond to #seconds_until" do
7
+ expect(subject).to respond_to :business_seconds_until
8
+ end
9
+
10
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ module TimeClock
4
+ describe Shift do
5
+
6
+ let(:start_time) { Time.new(2013,8,19,6,0) }
7
+ let(:end_time) { Time.new(2013,8,19,18,0) }
8
+
9
+ subject do
10
+ described_class.new(start_time, end_time)
11
+ end
12
+
13
+ it "should set the start time" do
14
+ expect(subject.start_time).to eq start_time
15
+ end
16
+
17
+ it "should set the end time" do
18
+ expect(subject.end_time).to eq end_time
19
+ end
20
+
21
+ it "should raise the right exception if the end time is before the start time" do
22
+ expect{described_class.new(end_time, start_time)}.to raise_error Shift::EndTimeAfterStartTimeError
23
+ end
24
+
25
+ it "should be eq if the times are equal" do
26
+ expect(subject).to eq subject.dup
27
+ end
28
+
29
+ context "when evaluating overlaps" do
30
+ let(:overlapping_shift) { Shift.new(Time.new(2013,8,19,5), Time.new(2013,8,19,10)) }
31
+ let(:non_overlapping_shift) { Shift.new(Time.new(2013,8,18,5), Time.new(2013,8,18,10)) }
32
+ it "should know when it does" do
33
+ expect(overlapping_shift.overlaps?(subject)).to be_true
34
+ end
35
+ it "should know when it doesn't" do
36
+ expect(non_overlapping_shift.overlaps?(subject)).to_not be_true
37
+ end
38
+ it "should calculate the overlapping seconds when there is an overlap" do
39
+ expect(overlapping_shift.overlapping_seconds(subject)).to eq 4 * 60 * 60
40
+ end
41
+ it "should calculate the overlapping seconds when there isn't an overlap" do
42
+ expect(non_overlapping_shift.overlapping_seconds(subject)).to eq 0
43
+ end
44
+ end
45
+
46
+
47
+ end
48
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe TimeClock do
4
+ let(:default_calendar) { TimeClock::Calendar.new }
5
+ it "should set a default calendar" do
6
+ described_class.default_calendar = default_calendar
7
+ expect(described_class.default_calendar).to eq default_calendar
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'time_clock'
5
+
6
+ RSpec.configure do |config|
7
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
+ config.run_all_when_everything_filtered = true
9
+ config.filter_run :focus
10
+
11
+ config.order = 'random'
12
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'time_clock/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "time_clock"
8
+ spec.version = TimeClock::VERSION
9
+ spec.authors = ["barelyknown"]
10
+ spec.email = ["sean@buytruckload.com"]
11
+ spec.description = %q{Calculate the business time for a period of time.}
12
+ spec.summary = %q{Calculate the business time for a period of time.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "guard-rspec"
25
+ spec.add_development_dependency "metric_fu"
26
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: time_clock
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - barelyknown
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: metric_fu
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Calculate the business time for a period of time.
84
+ email:
85
+ - sean@buytruckload.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .rspec
92
+ - Gemfile
93
+ - Guardfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - lib/time_clock.rb
98
+ - lib/time_clock/calendar.rb
99
+ - lib/time_clock/comparison.rb
100
+ - lib/time_clock/core_ext/time.rb
101
+ - lib/time_clock/shift.rb
102
+ - lib/time_clock/version.rb
103
+ - spec/lib/time_clock/calendar_spec.rb
104
+ - spec/lib/time_clock/comparison_spec.rb
105
+ - spec/lib/time_clock/core_ext/time_spec.rb
106
+ - spec/lib/time_clock/shift_spec.rb
107
+ - spec/lib/time_clock_spec.rb
108
+ - spec/spec_helper.rb
109
+ - time_clock.gemspec
110
+ homepage: ''
111
+ licenses:
112
+ - MIT
113
+ metadata: {}
114
+ post_install_message:
115
+ rdoc_options: []
116
+ require_paths:
117
+ - lib
118
+ required_ruby_version: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - '>='
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - '>='
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ requirements: []
129
+ rubyforge_project:
130
+ rubygems_version: 2.0.2
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Calculate the business time for a period of time.
134
+ test_files:
135
+ - spec/lib/time_clock/calendar_spec.rb
136
+ - spec/lib/time_clock/comparison_spec.rb
137
+ - spec/lib/time_clock/core_ext/time_spec.rb
138
+ - spec/lib/time_clock/shift_spec.rb
139
+ - spec/lib/time_clock_spec.rb
140
+ - spec/spec_helper.rb