business_time 0.7.1 → 0.7.2

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.
@@ -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