business_time 0.1.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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 bokmann
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,92 @@
1
+ = business_time
2
+
3
+ ActiveSupport gives us some great helpers so we can do things like:
4
+
5
+ 5.days.ago
6
+
7
+ and
8
+
9
+ 8.hours.from_now
10
+
11
+ as well as helpers to do that from any provided date or time.
12
+
13
+ I needed this, but taking into account business hours/days and holidays.
14
+
15
+ == Usage
16
+ * install the gem
17
+
18
+ gem install business_time
19
+
20
+ or
21
+
22
+ sudo gem install business_time
23
+
24
+ if you require sudo to install gems
25
+
26
+ * open up your console
27
+
28
+ # if in irb, add these lines:
29
+
30
+ require 'rubygems'
31
+ require 'active_support'
32
+ require 'business_time'
33
+
34
+ # try these examples, using the current time:
35
+
36
+ 1.business_hour.from_now
37
+ 4.business_hours.from_now
38
+ 8.business_hours.from_now
39
+
40
+ 1.business_hour.ago
41
+ 4.business_hours.ago
42
+ 8.business_hours.ago
43
+
44
+ 1.business_day.from_now
45
+ 4.business_days.from_now
46
+ 8.business_days.from_now
47
+
48
+ 1.business_day.ago
49
+ 4.business_days.ago
50
+ 8.business_days.ago
51
+
52
+ # and we can do it from any Date or Time object.
53
+ my_birthday = Date.parse("August 4th, 1969")
54
+ 8.business_days.after(my_birthday)
55
+ 8.business_days.before(my_birthday)
56
+
57
+ my_birthday = Time.parse("August 4th, 1969, 8:32 am")
58
+ 8.business_days.after(my_birthday)
59
+ 8.business_days.before(my_birthday)
60
+
61
+
62
+ # We can adjust the start and end time of our business hours
63
+ BusinessTime::Config.beginning_of_workday = "8:30 am"
64
+ BusinessTime::Config.end_of_workday = "5:30 pm"
65
+
66
+ # and we can add holidays that don't count as business days
67
+ # July 5 in 2010 is a monday that the U.S. takes off because our independence day falls on that Sunday.
68
+ three_day_weekend = Date.parse("July 5th, 2010")
69
+ BusinessTime::Config.holidays << three_day_weekend
70
+ friday_afternoon = Time.parse("July 2nd, 2010, 4:50 pm")
71
+ tuesday_morning = 1.business_hour.after(friday_afternoon)
72
+
73
+
74
+
75
+ == Note on Patches/Pull Requests
76
+
77
+ * Fork the project.
78
+ * Make your feature addition or bug fix.
79
+ * Add tests for it. This is important so I don't break it in a
80
+ future version unintentionally.
81
+ * Commit, do not mess with rakefile, version, or history.
82
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
83
+ * Send me a pull request. Bonus points for topic branches.
84
+
85
+ == TODO
86
+ * I'd like to return ActiveSupport::TimeWithZone just like the equivalent ActiveSupport helpers do.
87
+ * if it doesn't pollute the logic too much, I'd like to vary the days counted as 'business days'. Bakers often don't work on Mondays, for instance.
88
+ * We don't care much about timezones. Dunno if thats an issue.
89
+
90
+ == Copyright
91
+
92
+ Copyright (c) 2010 bokmann. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "business_time"
8
+ gem.summary = %Q{Support for doing time math in business hours and days}
9
+ gem.description = %Q{Have you ever wanted to do things like "6.business_days.from_now" and have weekends and holidays taken into account? Now you can.}
10
+ gem.email = "dbock@codesherpas.com"
11
+ gem.homepage = "http://github.com/bokmann/business_time"
12
+ gem.authors = ["bokmann"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_dependency('activesupport','>= 2.0.0')
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/test_*.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/test_*.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :test => :check_dependencies
43
+
44
+ task :default => :test
45
+
46
+ require 'rake/rdoctask'
47
+ Rake::RDocTask.new do |rdoc|
48
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
49
+
50
+ rdoc.rdoc_dir = 'rdoc'
51
+ rdoc.title = "business_time #{version}"
52
+ rdoc.rdoc_files.include('README*')
53
+ rdoc.rdoc_files.include('lib/**/*.rb')
54
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,6 @@
1
+ require "business_time/config"
2
+ require "business_time/business_hours"
3
+ require "business_time/business_days"
4
+ require "extensions/date"
5
+ require "extensions/time"
6
+ require "extensions/fixnum"
@@ -0,0 +1,35 @@
1
+ module BusinessTime
2
+
3
+ class BusinessDays
4
+ def initialize(days)
5
+ @days = days
6
+ end
7
+
8
+ def ago
9
+ before(Time.now)
10
+ end
11
+
12
+ def from_now
13
+ after(Time.now)
14
+ end
15
+
16
+ def after(time)
17
+ @days.times do
18
+ begin
19
+ time = time + 1.day
20
+ end until time.workday?
21
+ end
22
+ time
23
+ end
24
+
25
+ def before(time)
26
+ @days.times do
27
+ begin
28
+ time = time - 1.day
29
+ end until time.workday?
30
+ end
31
+ time
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,54 @@
1
+ module BusinessTime
2
+
3
+ class BusinessHours
4
+ def initialize(hours)
5
+ @hours = hours
6
+ end
7
+
8
+ def ago
9
+ before(Time.now)
10
+ end
11
+
12
+ def from_now
13
+ after(Time.now)
14
+ end
15
+
16
+ def after(time)
17
+ @hours.times do
18
+ time = time + 1.hour #add an hour
19
+
20
+ if (time > time.end_of_workday)
21
+ time = time + off_hours # if that pushes us past business hours,
22
+ end # roll into the next day
23
+
24
+ while !time.workday?
25
+ time = time + 1.day # if that pushes us into a non-business day,
26
+ end # find the next business day
27
+ end
28
+ time
29
+ end
30
+
31
+ def before(time)
32
+ @hours.times do
33
+ time = time - 1.hour #add an hour
34
+
35
+ if (time < time.beginning_of_workday)
36
+ time = time - off_hours # if that pushes us before business hours,
37
+ end # roll into the previous day
38
+
39
+ while !time.workday?
40
+ time = time - 1.day # if that pushes us into a non-business day,
41
+ end # find the previous business day
42
+ end
43
+ time
44
+ end
45
+
46
+ private
47
+
48
+ def off_hours
49
+ @gap ||= Time.parse(BusinessTime::Config.beginning_of_workday) -
50
+ (Time.parse(BusinessTime::Config.end_of_workday) - 1.day)
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,17 @@
1
+ module BusinessTime
2
+
3
+ # controls the behavior of the code. You can change
4
+ # the beginning_of_workday, end_of_workday, and the list of holidays
5
+ class Config
6
+ class << self
7
+ attr_accessor :beginning_of_workday
8
+ attr_accessor :end_of_workday
9
+ attr_accessor :holidays
10
+
11
+ end
12
+ self.holidays = []
13
+ self.beginning_of_workday = "9:00 am"
14
+ self.end_of_workday = "5:00 pm"
15
+ end
16
+
17
+ end
@@ -0,0 +1,10 @@
1
+ # Add workday and weekday concepts to the Date class
2
+ class Date
3
+ def workday?
4
+ self.weekday? && !BusinessTime::Config.holidays.include?(self)
5
+ end
6
+
7
+ def weekday?
8
+ [1,2,3,4,5].include? self.wday
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ # hook into fixnum so we can say things like:
2
+ # 5.business_hours.from_now
3
+ # 7.business_days.ago
4
+ # 3.business_days.after(some_date)
5
+ # 4.business_hours.before(some_date_time)
6
+ class Fixnum
7
+ include BusinessTime
8
+
9
+ def business_hours
10
+ BusinessHours.new(self)
11
+ end
12
+ alias_method :business_hour, :business_hours
13
+
14
+ def business_days
15
+ BusinessDays.new(self)
16
+ end
17
+ alias_method :business_day, :business_days
18
+ end
@@ -0,0 +1,18 @@
1
+ # Add workday and weekday concepts to the Time class
2
+ class Time
3
+ def end_of_workday
4
+ Time.parse self.strftime("%B %d %Y #{BusinessTime::Config.end_of_workday}")
5
+ end
6
+
7
+ def beginning_of_workday
8
+ Time.parse self.strftime("%B %d %Y #{BusinessTime::Config.beginning_of_workday}")
9
+ end
10
+
11
+ def workday?
12
+ self.weekday? && !BusinessTime::Config.holidays.include?(self.to_date)
13
+ end
14
+
15
+ def weekday?
16
+ [1,2,3,4,5].include? self.wday
17
+ end
18
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'test/unit'
4
+ require 'shoulda'
5
+
6
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ require 'business_time'
9
+
10
+ class Test::Unit::TestCase
11
+ end
@@ -0,0 +1,26 @@
1
+ require 'helper'
2
+
3
+ class TestBusinessDays < Test::Unit::TestCase
4
+
5
+ should "move to tomorrow if we add a business day" do
6
+ first = Time.parse("April 13th, 2010, 11:00 am")
7
+ later = 1.business_day.after(first)
8
+ expected = Time.parse("April 14th, 2010, 11:00 am")
9
+ assert expected == later
10
+ end
11
+
12
+ should "move to yesterday is we subtract a business day"
13
+
14
+ should "take into account the weekend when adding a day"
15
+
16
+ should "move forward one week when adding 5 business days"
17
+
18
+ should "take into account a holiday when adding a day"
19
+
20
+ should "take into account a holiday on a weekend"
21
+
22
+ should "take into account a holiday on a Monday"
23
+
24
+ should "take into account a holiday on a Friday"
25
+
26
+ end
@@ -0,0 +1,22 @@
1
+ require 'helper'
2
+
3
+ class TestBusinessHours < Test::Unit::TestCase
4
+ should "move to tomorrow if we add 8 business hours" do
5
+ first = Time.parse("Aug 4 2010, 9:35 am")
6
+ later = 8.business_hours.after(first)
7
+ expected = Time.parse("Aug 5 2010, 9:35 am")
8
+ end
9
+
10
+ should "move to yesterday is we subtract 8 business hours"
11
+
12
+ should "take into account a weekend"
13
+
14
+ should "take into account a holiday"
15
+
16
+ should "add hours in the middle of the workday"
17
+
18
+ should "wrap to tomorrow if past the end of the workday"
19
+
20
+ should "wrap to yesterday if past the beginning of the workday"
21
+
22
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+
3
+ class TestConfig < Test::Unit::TestCase
4
+
5
+ should "keep track of the start of the day" do
6
+ assert BusinessTime::Config.beginning_of_workday == "9:00 am"
7
+ BusinessTime::Config.beginning_of_workday = "8:30 am"
8
+ assert BusinessTime::Config.beginning_of_workday == "8:30 am"
9
+ end
10
+
11
+ should "keep track of the end of the day" do
12
+ assert BusinessTime::Config.end_of_workday == "5:00 pm"
13
+ BusinessTime::Config.end_of_workday = "5:30 pm"
14
+ assert BusinessTime::Config.end_of_workday == "5:30 pm"
15
+ end
16
+
17
+ should "keep track of holidays" do
18
+ assert BusinessTime::Config.holidays.empty?
19
+ daves_birthday = Date.parse("August 4th, 1969")
20
+ BusinessTime::Config.holidays << daves_birthday
21
+ assert BusinessTime::Config.holidays.include?(daves_birthday)
22
+ end
23
+
24
+ end
@@ -0,0 +1,16 @@
1
+ require 'helper'
2
+
3
+ class TestDateExtensions < Test::Unit::TestCase
4
+
5
+ should "know what a weekend day is" do
6
+ assert(Time.parse("April 9, 2010").weekday?)
7
+ assert(!Time.parse("April 10, 2010").weekday?)
8
+ assert(!Time.parse("April 11, 2010").weekday?)
9
+ assert(Time.parse("April 12, 2010").weekday?)
10
+ end
11
+
12
+ should "know a weekend day is not a workday"
13
+
14
+ should "know a holiday is not a workday"
15
+
16
+ end
@@ -0,0 +1,17 @@
1
+ require 'helper'
2
+
3
+ class TestFixnumExtensions < Test::Unit::TestCase
4
+
5
+ should "respond to business_hours by returning an instance of BusinessHours" do
6
+ assert(1.respond_to?(:business_hour))
7
+ assert(1.respond_to?(:business_hours))
8
+ assert 1.business_hour.instance_of?(BusinessTime::BusinessHours)
9
+ end
10
+
11
+ should "respond to business_days by returning an instance of BusinessDays" do
12
+ assert(1.respond_to?(:business_day))
13
+ assert(1.respond_to?(:business_days))
14
+ assert 1.business_day.instance_of?(BusinessTime::BusinessDays)
15
+ end
16
+
17
+ end
@@ -0,0 +1,20 @@
1
+ require 'helper'
2
+
3
+ class TestTimeExtensions < Test::Unit::TestCase
4
+
5
+ should "know what a weekend day is" do
6
+ assert(Time.parse("April 9, 2010 10:30am").weekday?)
7
+ assert(!Time.parse("April 10, 2010 10:30am").weekday?)
8
+ assert(!Time.parse("April 11, 2010 10:30am").weekday?)
9
+ assert(Time.parse("April 12, 2010 10:30am").weekday?)
10
+ end
11
+
12
+ should "know a weekend day is not a workday"
13
+
14
+ should "know a holiday is not a workday"
15
+
16
+ should "know the beginning of the day for an instance"
17
+
18
+ should "know the end of the day for an instance"
19
+
20
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: business_time
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - bokmann
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-13 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thoughtbot-shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: activesupport
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ segments:
40
+ - 2
41
+ - 0
42
+ - 0
43
+ version: 2.0.0
44
+ type: :runtime
45
+ version_requirements: *id002
46
+ description: Have you ever wanted to do things like "6.business_days.from_now" and have weekends and holidays taken into account? Now you can.
47
+ email: dbock@codesherpas.com
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ extra_rdoc_files:
53
+ - LICENSE
54
+ - README.rdoc
55
+ files:
56
+ - .document
57
+ - .gitignore
58
+ - LICENSE
59
+ - README.rdoc
60
+ - Rakefile
61
+ - VERSION
62
+ - lib/business_time.rb
63
+ - lib/business_time/business_days.rb
64
+ - lib/business_time/business_hours.rb
65
+ - lib/business_time/config.rb
66
+ - lib/extensions/date.rb
67
+ - lib/extensions/fixnum.rb
68
+ - lib/extensions/time.rb
69
+ - test/helper.rb
70
+ - test/test_business_days.rb
71
+ - test/test_business_hours.rb
72
+ - test/test_config.rb
73
+ - test/test_date_extensions.rb
74
+ - test/test_fixnum_extensions.rb
75
+ - test/test_time_extensions.rb
76
+ has_rdoc: true
77
+ homepage: http://github.com/bokmann/business_time
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options:
82
+ - --charset=UTF-8
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ requirements: []
100
+
101
+ rubyforge_project:
102
+ rubygems_version: 1.3.6
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Support for doing time math in business hours and days
106
+ test_files:
107
+ - test/helper.rb
108
+ - test/test_business_days.rb
109
+ - test/test_business_hours.rb
110
+ - test/test_config.rb
111
+ - test/test_date_extensions.rb
112
+ - test/test_fixnum_extensions.rb
113
+ - test/test_time_extensions.rb