business_time 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,8 @@ require 'business_time/config'
7
7
  require 'business_time/business_hours'
8
8
  require 'business_time/business_days'
9
9
  require 'business_time/core_ext/date'
10
- require 'business_time/core_ext/time'
11
10
  require 'business_time/core_ext/fixnum'
12
11
 
12
+ require 'business_time/time_extensions'
13
+ require 'business_time/core_ext/time'
14
+ require 'business_time/core_ext/active_support/time_with_zone'
@@ -19,13 +19,10 @@ module BusinessTime
19
19
  @hours.times do
20
20
  after_time = after_time + 1.hour
21
21
 
22
- # Roll back a second if it is midnight so that we check end_of_workday for the right day
23
22
  if after_time.hour == 0 && after_time.min == 0 && after_time.sec == 0
24
- after_time = after_time - 1.second
25
- end
26
-
27
- # Ignore hours before opening and after closing
28
- if (after_time >= Time.end_of_workday(after_time))
23
+ after_time = Time.roll_forward(after_time)
24
+ elsif (after_time > Time.end_of_workday(after_time))
25
+ # Ignore hours before opening and after closing
29
26
  delta = after_time - Time.end_of_workday(after_time)
30
27
  after_time = Time.roll_forward(after_time) + delta
31
28
  end
@@ -40,14 +37,22 @@ module BusinessTime
40
37
  alias_method :since, :after
41
38
 
42
39
  def before(time)
43
- before_time = Time.roll_forward(time)
40
+ before_time = Time.roll_backward(time)
44
41
  # Step through the hours, skipping over non-business hours
45
42
  @hours.times do
46
43
  before_time = before_time - 1.hour
47
44
 
48
- # Ignore hours before opening and after closing
49
- if (before_time < Time.beginning_of_workday(before_time))
50
- before_time = before_time - off_hours
45
+ if before_time.hour == 0 && before_time.min == 0 && before_time.sec == 0
46
+ before_time = Time.roll_backward(before_time - 1.second)
47
+ elsif (before_time <= Time.beginning_of_workday(before_time))
48
+ # Ignore hours before opening and after closing
49
+ delta = Time.beginning_of_workday(before_time) - before_time
50
+
51
+ # Due to the 23:59:59 end-of-workday exception
52
+ time_roll_backward = Time.roll_backward(before_time)
53
+ time_roll_backward += 1.second if time_roll_backward.to_s =~ /23:59:59/
54
+
55
+ before_time = time_roll_backward - delta
51
56
  end
52
57
 
53
58
  # Ignore weekends and holidays
@@ -57,20 +62,5 @@ module BusinessTime
57
62
  end
58
63
  before_time
59
64
  end
60
-
61
- private
62
-
63
- def off_hours
64
- return @gap if @gap
65
- if Time.zone
66
- gap_end = Time.zone.parse(BusinessTime::Config.beginning_of_workday)
67
- gap_begin = (Time.zone.parse(BusinessTime::Config.end_of_workday)-1.day)
68
- else
69
- gap_end = Time.parse(BusinessTime::Config.beginning_of_workday)
70
- gap_begin = (Time.parse(BusinessTime::Config.end_of_workday) - 1.day)
71
- end
72
- @gap = gap_end - gap_begin
73
- end
74
65
  end
75
-
76
66
  end
@@ -12,6 +12,7 @@ module BusinessTime
12
12
  end_of_workday: '5:00 pm',
13
13
  work_week: %w(mon tue wed thu fri),
14
14
  work_hours: {},
15
+ work_hours_total: {},
15
16
  _weekdays: nil,
16
17
  }
17
18
 
@@ -66,6 +67,9 @@ module BusinessTime
66
67
  # {:mon => ["9:00","17:00"],:tue => ["9:00","17:00"].....}
67
68
  threadsafe_cattr_accessor :work_hours
68
69
 
70
+ # total work hours for a day. Never set, always calculated.
71
+ threadsafe_cattr_accessor :work_hours_total
72
+
69
73
  threadsafe_cattr_accessor :_weekdays # internal
70
74
 
71
75
  class << self
@@ -0,0 +1,3 @@
1
+ class ActiveSupport::TimeWithZone
2
+ include BusinessTime::TimeExtensions
3
+ end
@@ -1,140 +1,4 @@
1
1
  # Add workday and weekday concepts to the Time class
2
2
  class Time
3
- class << self
4
- # Gives the time at the end of the workday, assuming that this time falls on a
5
- # workday.
6
- # Note: It pretends that this day is a workday whether or not it really is a
7
- # workday.
8
- def end_of_workday(day)
9
- end_of_workday = Time.parse(BusinessTime::Config.end_of_workday(day))
10
- change_business_time(day,end_of_workday.hour,end_of_workday.min,end_of_workday.sec)
11
- end
12
-
13
- # Gives the time at the beginning of the workday, assuming that this time
14
- # falls on a workday.
15
- # Note: It pretends that this day is a workday whether or not it really is a
16
- # workday.
17
- def beginning_of_workday(day)
18
- beginning_of_workday = Time.parse(BusinessTime::Config.beginning_of_workday(day))
19
- change_business_time(day,beginning_of_workday.hour,beginning_of_workday.min,beginning_of_workday.sec)
20
- end
21
-
22
- # True if this time is on a workday (between 00:00:00 and 23:59:59), even if
23
- # this time falls outside of normal business hours.
24
- def workday?(day)
25
- Time.weekday?(day) &&
26
- !BusinessTime::Config.holidays.include?(day.to_date)
27
- end
28
-
29
- # True if this time falls on a weekday.
30
- def weekday?(day)
31
- BusinessTime::Config.weekdays.include? day.wday
32
- end
33
-
34
- def before_business_hours?(time)
35
- time.to_i < beginning_of_workday(time).to_i
36
- end
37
-
38
- def after_business_hours?(time)
39
- time.to_i > end_of_workday(time).to_i
40
- end
41
-
42
- # Rolls forward to the next beginning_of_workday
43
- # when the time is outside of business hours
44
- def roll_forward(time)
45
-
46
- if Time.before_business_hours?(time) || !Time.workday?(time)
47
- next_business_time = Time.beginning_of_workday(time)
48
- elsif Time.after_business_hours?(time) || Time.end_of_workday(time) == time
49
- next_business_time = Time.beginning_of_workday(time + 1.day)
50
- else
51
- next_business_time = time.clone
52
- end
53
-
54
- while !Time.workday?(next_business_time)
55
- next_business_time = Time.beginning_of_workday(next_business_time + 1.day)
56
- end
57
-
58
- next_business_time
59
- end
60
-
61
- # Rolls backwards to the previous end_of_workday when the time is outside
62
- # of business hours
63
- def roll_backward(time)
64
- prev_business_time = if (Time.before_business_hours?(time) || !Time.workday?(time))
65
- Time.end_of_workday(time) - 1.day
66
- elsif Time.after_business_hours?(time)
67
- Time.end_of_workday(time)
68
- else
69
- time.clone
70
- end
71
-
72
- while !Time.workday?(prev_business_time)
73
- prev_business_time -= 1.day
74
- end
75
-
76
- prev_business_time
77
- end
78
-
79
- private
80
-
81
- def change_business_time time, hour, min=0, sec=0
82
- if Time.zone
83
- time.in_time_zone(Time.zone).change(:hour => hour, :min => min, :sec => sec)
84
- else
85
- time.change(:hour => hour, :min => min, :sec => sec)
86
- end
87
- end
88
- end
89
-
90
- def business_time_until(to_time)
91
- # Make sure that we will calculate time from A to B "clockwise"
92
- if self < to_time
93
- time_a = self
94
- time_b = to_time
95
- direction = 1
96
- else
97
- time_a = to_time
98
- time_b = self
99
- direction = -1
100
- end
101
-
102
- # Align both times to the closest business hours
103
- time_a = Time::roll_forward(time_a)
104
- time_b = Time::roll_forward(time_b)
105
-
106
- # If same date, then calculate difference straight forward
107
- if time_a.to_date == time_b.to_date
108
- result = time_b - time_a
109
- return result * direction
110
- end
111
-
112
- # Both times are in different dates
113
- #result = Time.parse(time_a.strftime('%Y-%m-%d ') + BusinessTime::Config.end_of_workday) - time_a # First day
114
- #result += time_b - Time.parse(time_b.strftime('%Y-%m-%d ') + BusinessTime::Config.beginning_of_workday) # Last day
115
-
116
- result = 0
117
-
118
- # All days in between
119
- time_c = time_a
120
- while time_c.to_i < time_b.to_i do
121
- end_of_workday = Time.end_of_workday(time_c)
122
- if time_c.to_date == time_b.to_date
123
- if end_of_workday < time_b
124
- result += end_of_workday - time_c
125
- break
126
- else
127
- result += time_b - time_c
128
- break
129
- end
130
- else
131
- result += end_of_workday - time_c
132
- time_c = Time::roll_forward(end_of_workday)
133
- end
134
- result += 1 if end_of_workday.to_s =~ /23:59:59/
135
- end
136
-
137
- # Make sure that sign is correct
138
- result * direction
139
- end
3
+ include BusinessTime::TimeExtensions
140
4
  end
@@ -0,0 +1,145 @@
1
+ module BusinessTime
2
+ module TimeExtensions
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ # Gives the time at the end of the workday, assuming that this time falls on a
9
+ # workday.
10
+ # Note: It pretends that this day is a workday whether or not it really is a
11
+ # workday.
12
+ def end_of_workday(day)
13
+ end_of_workday = Time.parse(BusinessTime::Config.end_of_workday(day))
14
+ change_business_time(day,end_of_workday.hour,end_of_workday.min,end_of_workday.sec)
15
+ end
16
+
17
+ # Gives the time at the beginning of the workday, assuming that this time
18
+ # falls on a workday.
19
+ # Note: It pretends that this day is a workday whether or not it really is a
20
+ # workday.
21
+ def beginning_of_workday(day)
22
+ beginning_of_workday = Time.parse(BusinessTime::Config.beginning_of_workday(day))
23
+ change_business_time(day,beginning_of_workday.hour,beginning_of_workday.min,beginning_of_workday.sec)
24
+ end
25
+
26
+ # True if this time is on a workday (between 00:00:00 and 23:59:59), even if
27
+ # this time falls outside of normal business hours.
28
+ def workday?(day)
29
+ Time.weekday?(day) &&
30
+ !BusinessTime::Config.holidays.include?(day.to_date)
31
+ end
32
+
33
+ # True if this time falls on a weekday.
34
+ def weekday?(day)
35
+ BusinessTime::Config.weekdays.include? day.wday
36
+ end
37
+
38
+ def before_business_hours?(time)
39
+ time.to_i < beginning_of_workday(time).to_i
40
+ end
41
+
42
+ def after_business_hours?(time)
43
+ time.to_i > end_of_workday(time).to_i
44
+ end
45
+
46
+ # Rolls forward to the next beginning_of_workday
47
+ # when the time is outside of business hours
48
+ def roll_forward(time)
49
+
50
+ if Time.before_business_hours?(time) || !Time.workday?(time)
51
+ next_business_time = Time.beginning_of_workday(time)
52
+ elsif Time.after_business_hours?(time) || Time.end_of_workday(time) == time
53
+ next_business_time = Time.beginning_of_workday(time + 1.day)
54
+ else
55
+ next_business_time = time.clone
56
+ end
57
+
58
+ while !Time.workday?(next_business_time)
59
+ next_business_time = Time.beginning_of_workday(next_business_time + 1.day)
60
+ end
61
+
62
+ next_business_time
63
+ end
64
+
65
+ # Rolls backwards to the previous end_of_workday when the time is outside
66
+ # of business hours
67
+ def roll_backward(time)
68
+ prev_business_time = if (Time.before_business_hours?(time) || !Time.workday?(time))
69
+ Time.end_of_workday(time - 1.day)
70
+ elsif Time.after_business_hours?(time)
71
+ Time.end_of_workday(time)
72
+ else
73
+ time.clone
74
+ end
75
+
76
+ while !Time.workday?(prev_business_time)
77
+ prev_business_time = Time.end_of_workday(prev_business_time - 1.day)
78
+ end
79
+
80
+ prev_business_time
81
+ end
82
+
83
+ def work_hours_total(day)
84
+ return 0 unless Time.workday?(day)
85
+
86
+ day = day.strftime('%a').downcase.to_sym
87
+
88
+ if hours = BusinessTime::Config.work_hours[day]
89
+ BusinessTime::Config.work_hours_total[day] ||= begin
90
+ hours_last = hours.last
91
+ if hours_last == '00:00'
92
+ (Time.parse('23:59') - Time.parse(hours.first)) + 1.minute
93
+ else
94
+ Time.parse(hours_last) - Time.parse(hours.first)
95
+ end
96
+ end
97
+ else
98
+ BusinessTime::Config.work_hours_total[:default] ||= begin
99
+ Time.parse(BusinessTime::Config.end_of_workday) - Time.parse(BusinessTime::Config.beginning_of_workday)
100
+ end
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def change_business_time time, hour, min=0, sec=0
107
+ if Time.zone
108
+ time.in_time_zone(Time.zone).change(:hour => hour, :min => min, :sec => sec)
109
+ else
110
+ time.change(:hour => hour, :min => min, :sec => sec)
111
+ end
112
+ end
113
+ end
114
+
115
+ def business_time_until(to_time)
116
+ # Make sure that we will calculate time from A to B "clockwise"
117
+ if self < to_time
118
+ time_a = self
119
+ time_b = to_time
120
+ direction = 1
121
+ else
122
+ time_a = to_time
123
+ time_b = self
124
+ direction = -1
125
+ end
126
+
127
+ # Align both times to the closest business hours
128
+ time_a = Time::roll_forward(time_a)
129
+ time_b = Time::roll_forward(time_b)
130
+
131
+ if time_a.to_date == time_b.to_date
132
+ time_b - time_a
133
+ else
134
+ end_of_workday = Time.end_of_workday(time_a)
135
+ end_of_workday += 1 if end_of_workday.to_s =~ /23:59:59/
136
+
137
+ first_day = end_of_workday - time_a
138
+ days_in_between = ((time_a.to_date + 1)..(time_b.to_date - 1)).sum{ |day| Time::work_hours_total(day) }
139
+ last_day = time_b - Time.beginning_of_workday(time_b)
140
+
141
+ first_day + days_in_between + last_day
142
+ end * direction
143
+ end
144
+ end
145
+ end
@@ -1,3 +1,3 @@
1
1
  module BusinessTime
2
- VERSION = "0.7.1"
2
+ VERSION = "0.7.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: business_time
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-02-07 00:00:00.000000000 Z
12
+ date: 2014-03-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -120,9 +120,11 @@ files:
120
120
  - lib/business_time/business_days.rb
121
121
  - lib/business_time/business_hours.rb
122
122
  - lib/business_time/config.rb
123
+ - lib/business_time/core_ext/active_support/time_with_zone.rb
123
124
  - lib/business_time/core_ext/date.rb
124
125
  - lib/business_time/core_ext/fixnum.rb
125
126
  - lib/business_time/core_ext/time.rb
127
+ - lib/business_time/time_extensions.rb
126
128
  - lib/business_time/version.rb
127
129
  - lib/generators/business_time/config_generator.rb
128
130
  - rails_generators/business_time_config/business_time_config_generator.rb
@@ -143,7 +145,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
143
145
  version: '0'
144
146
  segments:
145
147
  - 0
146
- hash: -4140664876774747293
148
+ hash: 2725720830618277021
147
149
  required_rubygems_version: !ruby/object:Gem::Requirement
148
150
  none: false
149
151
  requirements:
@@ -152,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
154
  version: '0'
153
155
  segments:
154
156
  - 0
155
- hash: -4140664876774747293
157
+ hash: 2725720830618277021
156
158
  requirements: []
157
159
  rubyforge_project:
158
160
  rubygems_version: 1.8.23