business_time 0.8.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7dc74d9cffafd72acce648d07bdceed471344dfd
4
- data.tar.gz: 6394c590be260dddde6c2738090cf00c03060368
2
+ SHA256:
3
+ metadata.gz: cb1621dd19416fb58fba1a2dcaff32e65542e29e18d09fdea0a9d7dca621f132
4
+ data.tar.gz: ef8f7a0816e6e1df588a3e14424894b397df07e4217718594dd109c6c2bc4aaf
5
5
  SHA512:
6
- metadata.gz: c0321b6695f50318379b00423f54aca445eb53a5b1c2ee9b1ba1a164a442f4090c56bd6a541a00e3b527bc80ab4f0ba139e407aa6f12e84b445afe7958885692
7
- data.tar.gz: 633875c242cb89ae62ec2d9e2361ff16e7b2b91ad5216c7b2198f36d1631eaa4f33f502b86f4d13b05ba5fc77e34c76c398706d403c70024f84cef17c0940834
6
+ metadata.gz: fa4d68c497aa80d701692dc5182837603e8cd66cdef810298fb727f29b65d5c92b22da9b3ce5a6f1c5e4533551b3de1b4a7591931c1f68dc3c0e1dde8b62dd66
7
+ data.tar.gz: a62611ad7ac045fa795a98eaca9fed77a86725bc09a87e4941290ad2f7bda3a3851756fc22d609b802fadfc122440945f58f6bc240140bcd6bc19c0b21820ecf
data/README.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = business_time
2
2
 
3
- {<img src="https://secure.travis-ci.org/bokmann/business_time.png" />}[http://travis-ci.org/bokmann/business_time]
3
+ {<img src="https://github.com/bokmann/business_time/workflows/CI/badge.svg" />}[https://github.com/bokmann/business_time/actions?query=workflow%3ACI]
4
4
 
5
5
  ActiveSupport gives us some great helpers so we can do things like:
6
6
 
@@ -42,7 +42,7 @@ I needed this, but taking into account business hours/days and holidays.
42
42
  1.business_day.ago
43
43
  4.business_days.ago
44
44
  8.business_days.ago
45
-
45
+
46
46
  Date.today.workday?
47
47
  Date.parse("2015-12-09").workday?
48
48
  Date.parse("2015-12-12").workday?
@@ -164,22 +164,6 @@ Timezone relative date handling gets more and more complicated every time you lo
164
164
  # BusinessTime::Config.holidays << holiday[:date].next_week if !holiday[:date].weekday?
165
165
  end
166
166
 
167
- == Releases
168
-
169
- 0.7.6 - Fixed a defect where timezone was not preserved when dealing with beginning_or_workday and end_of_workday.
170
- Thanks bazzargh.
171
-
172
- 0.7.2 - 0.7.5 - many pull requests from contributors, and a changelog was not adequately kept. I regret that.
173
- git blame is your friend for diagnosing these dark days.
174
-
175
- 0.7.1 - fixing a multithreaded issue, upgrading some dependencies, loosening the dependency on TZInfo
176
-
177
- 0.7.0 - major maintenance upgrade on the process of constructing the gem, testing the gem, and updating dependencies.
178
- the api has not changed.
179
-
180
-
181
- 0.6.2 - rchady pointed out that issue #14 didn't appear to be released. This fixes that, as well as confirms that all tests run as expected on Ruby 2.0.0p195
182
-
183
167
  == Contributors
184
168
  * David Bock http://github.com/bokmann
185
169
  * Enrico Bianco http://github.com/enricob
@@ -220,6 +204,20 @@ Timezone relative date handling gets more and more complicated every time you lo
220
204
  need the baggage.
221
205
 
222
206
 
207
+ == A note on stability and change
208
+
209
+ Sometimes people ask me why this gem doesn't release more often. My opinions on that are best discussed in person in a friendly discussion, but I'll attempt some of that here.
210
+
211
+ First, a big part of the reason is that the projects I do use this gem on are happy with it's current functionality. It is 'suitable for the purpose' for which I released it, and as such, maintenance I do on this gem is a gift to the community.
212
+
213
+ Second, out of the ~1.3 million downloads (according to rubygems.org), the number of real 'issues' with this gem have been minimal. Most of the issues that are opened are really people with slightly different requirements than I have regarding whether 'off hours' work counts as the previous or the next business day, a disagreement on the semantics of days vs. hours, etc. I take care to try to explain these choices in the open issues, but to my mind, they aren't true issues if it's just a difference of opinion. Even so, I'll gladly accept pull requests that resolve this difference of opinion as a configuration option... just don't expect me to do your job for you. I've already given you 90% of what you need.
214
+
215
+ Third, a business time gem is, well, relevant to businesses. Many businesses don't move quickly. My government clients move even more slowly. Stability is favored in these environments.
216
+
217
+ Fourth, new features can wait. To the person that adds them they can be mission critical, but with modern packaging processes, they can use their version without waiting for their changes to be included in the upstream version. Their changes don't break your code.
218
+
219
+ I'm proud of the work in this gem; the stability is a big part of that. This gem has lived longer than many others that have attempted to do the same thing. I expect it to be here chugging away when Ruby has become the next COBOL.
220
+
223
221
  == Copyright
224
222
 
225
- Copyright (c) 2010-2016 bokmann. See LICENSE for details.
223
+ Copyright (c) 2010-2021 bokmann. See LICENSE for details.
data/lib/business_time.rb CHANGED
@@ -4,6 +4,7 @@ require 'active_support/time'
4
4
  require 'time'
5
5
  require 'yaml'
6
6
 
7
+ require 'business_time/parsed_time'
7
8
  require 'business_time/version'
8
9
  require 'business_time/config'
9
10
  require 'business_time/business_hours'
@@ -17,7 +17,29 @@ module BusinessTime
17
17
  end
18
18
 
19
19
  def after(time = Time.current)
20
- days = @days
20
+ non_negative_days? ? calculate_after(time, @days) : calculate_before(time, -@days)
21
+ end
22
+
23
+ alias_method :from_now, :after
24
+ alias_method :since, :after
25
+
26
+ def before(time = Time.current)
27
+ non_negative_days? ? calculate_before(time, @days) : calculate_after(time, -@days)
28
+ end
29
+
30
+ alias_method :ago, :before
31
+ alias_method :until, :before
32
+
33
+ private
34
+
35
+ def non_negative_days?
36
+ @days >= 0
37
+ end
38
+
39
+ def calculate_after(time, days)
40
+ if (time.is_a?(Time) || time.is_a?(DateTime)) && !time.workday?
41
+ time = Time.beginning_of_workday(time)
42
+ end
21
43
  while days > 0 || !time.workday?
22
44
  days -= 1 if time.workday?
23
45
  time += 1.day
@@ -30,11 +52,10 @@ module BusinessTime
30
52
  time
31
53
  end
32
54
 
33
- alias_method :from_now, :after
34
- alias_method :since, :after
35
-
36
- def before(time = Time.current)
37
- days = @days
55
+ def calculate_before(time, days)
56
+ if (time.is_a?(Time) || time.is_a?(DateTime)) && !time.workday?
57
+ time = Time.beginning_of_workday(time)
58
+ end
38
59
  while days > 0 || !time.workday?
39
60
  days -= 1 if time.workday?
40
61
  time -= 1.day
@@ -48,8 +69,5 @@ module BusinessTime
48
69
  end
49
70
  time
50
71
  end
51
-
52
- alias_method :ago, :before
53
- alias_method :until, :before
54
72
  end
55
73
  end
@@ -24,9 +24,24 @@ module BusinessTime
24
24
  end
25
25
 
26
26
  def after(time)
27
+ non_negative_hours? ? calculate_after(time, @hours) : calculate_before(time, -@hours)
28
+ end
29
+ alias_method :since, :after
30
+
31
+ def before(time)
32
+ non_negative_hours? ? calculate_before(time, @hours) : calculate_after(time, -@hours)
33
+ end
34
+
35
+ private
36
+
37
+ def non_negative_hours?
38
+ @hours >= 0
39
+ end
40
+
41
+ def calculate_after(time, hours)
27
42
  after_time = Time.roll_forward(time)
28
43
  # Step through the hours, skipping over non-business hours
29
- @hours.times do
44
+ hours.times do
30
45
  after_time = after_time + 1.hour
31
46
 
32
47
  if after_time.hour == 0 && after_time.min == 0 && after_time.sec == 0
@@ -44,12 +59,11 @@ module BusinessTime
44
59
  end
45
60
  after_time
46
61
  end
47
- alias_method :since, :after
48
62
 
49
- def before(time)
63
+ def calculate_before(time, hours)
50
64
  before_time = Time.roll_backward(time)
51
65
  # Step through the hours, skipping over non-business hours
52
- @hours.times do
66
+ hours.times do
53
67
  before_time = before_time - 1.hour
54
68
 
55
69
  if before_time.hour == 0 && before_time.min == 0 && before_time.sec == 0
@@ -7,16 +7,33 @@ module BusinessTime
7
7
  # manually, or with a yaml file and the load method.
8
8
  class Config
9
9
  DEFAULT_CONFIG = {
10
- holidays: [],
11
- beginning_of_workday: '9:00 am',
12
- end_of_workday: '5:00 pm',
10
+ holidays: SortedSet.new,
11
+ beginning_of_workday: ParsedTime.parse('9:00 am'),
12
+ end_of_workday: ParsedTime.parse('5:00 pm'),
13
13
  work_week: %w(mon tue wed thu fri),
14
14
  work_hours: {},
15
15
  work_hours_total: {},
16
16
  _weekdays: nil,
17
+ fiscal_month_offset: 10,
17
18
  }
18
19
 
19
20
  class << self
21
+ def beginning_of_workday=(time)
22
+ config[:beginning_of_workday] = ParsedTime.parse(time)
23
+ end
24
+
25
+ def end_of_workday=(time)
26
+ config[:end_of_workday] = ParsedTime.parse(time)
27
+ end
28
+
29
+ def work_hours=(work_hours)
30
+ work_hours.each_with_object(config[:work_hours] = {}) do |(day, hours), c|
31
+ c[day] = hours.map do |time|
32
+ ParsedTime.parse(time)
33
+ end
34
+ end
35
+ end
36
+
20
37
  private
21
38
 
22
39
  def config
@@ -30,44 +47,40 @@ module BusinessTime
30
47
  end
31
48
 
32
49
  def local_config
33
- Thread.current[:business_time_local_config]
50
+ local_config_stack.last
34
51
  end
35
52
 
36
53
  def local_config=(config)
37
- Thread.current[:business_time_local_config] = config
54
+ local_config_stack.last.replace(config)
55
+ end
56
+
57
+ def local_config_stack
58
+ Thread.current[:business_time_local_config] ||= []
38
59
  end
39
60
 
40
61
  def local_config?
41
- !local_config.nil?
62
+ !local_config_stack.empty?
42
63
  end
43
64
 
44
65
  def threadsafe_cattr_accessor(name)
66
+ threadsafe_cattr_reader(name)
67
+ threadsafe_cattr_setter(name)
68
+ end
69
+
70
+ def threadsafe_cattr_reader(name)
45
71
  define_singleton_method name do
46
72
  config[name]
47
73
  end
74
+ end
75
+
76
+ def threadsafe_cattr_setter(name)
48
77
  define_singleton_method "#{name}=" do |value|
49
78
  config[name] = value
50
79
  end
51
80
  end
52
81
  end
53
82
 
54
- # You can set this yourself, either by the load method below, or
55
- # by saying
56
- # BusinessTime::Config.beginning_of_workday = "8:30 am"
57
- # someplace in the initializers of your application.
58
- threadsafe_cattr_accessor :beginning_of_workday
59
-
60
- # You can set this yourself, either by the load method below, or
61
- # by saying
62
- # BusinessTime::Config.end_of_workday = "5:30 pm"
63
- # someplace in the initializers of your application.
64
- threadsafe_cattr_accessor :end_of_workday
65
-
66
- # You can set this yourself, either by the load method below, or
67
- # by saying
68
- # BusinessTime::Config.work_week = [:sun, :mon, :tue, :wed, :thu]
69
- # someplace in the initializers of your application.
70
- threadsafe_cattr_accessor :work_week
83
+ threadsafe_cattr_reader :work_week
71
84
 
72
85
  # You can set this yourself, either by the load method below, or
73
86
  # by saying
@@ -79,23 +92,33 @@ module BusinessTime
79
92
  # and end_of_workday. Keys will be added ad weekdays.
80
93
  # Example:
81
94
  # {:mon => ["9:00","17:00"],:tue => ["9:00","17:00"].....}
82
- threadsafe_cattr_accessor :work_hours
95
+ threadsafe_cattr_reader :work_hours
83
96
 
84
97
  # total work hours for a day. Never set, always calculated.
85
98
  threadsafe_cattr_accessor :work_hours_total
86
99
 
87
100
  threadsafe_cattr_accessor :_weekdays # internal
88
101
 
102
+ threadsafe_cattr_accessor :fiscal_month_offset
103
+
89
104
  class << self
105
+ # You can set this yourself, either by the load method below, or
106
+ # by saying
107
+ # BusinessTime::Config.end_of_workday = "5:30 pm"
108
+ # someplace in the initializers of your application.
90
109
  def end_of_workday(day=nil)
91
110
  if day
92
111
  wday = work_hours[int_to_wday(day.wday)]
93
- wday ? (wday.last =~ /^0{1,2}\:0{1,2}$/ ? "23:59:59" : wday.last) : config[:end_of_workday]
112
+ wday ? (wday.last == ParsedTime.new(0, 0) ? ParsedTime.new(23, 59, 59) : wday.last) : config[:end_of_workday]
94
113
  else
95
114
  config[:end_of_workday]
96
115
  end
97
116
  end
98
117
 
118
+ # You can set this yourself, either by the load method below, or
119
+ # by saying
120
+ # BusinessTime::Config.beginning_of_workday = "8:30 am"
121
+ # someplace in the initializers of your application.
99
122
  def beginning_of_workday(day=nil)
100
123
  if day
101
124
  wday = work_hours[int_to_wday(day.wday)]
@@ -105,6 +128,10 @@ module BusinessTime
105
128
  end
106
129
  end
107
130
 
131
+ # You can set this yourself, either by the load method below, or
132
+ # by saying
133
+ # BusinessTime::Config.work_week = [:sun, :mon, :tue, :wed, :thu]
134
+ # someplace in the initializers of your application.
108
135
  def work_week=(days)
109
136
  config[:work_week] = days
110
137
  self._weekdays = nil
@@ -113,10 +140,11 @@ module BusinessTime
113
140
  def weekdays
114
141
  return _weekdays unless _weekdays.nil?
115
142
 
116
- self._weekdays = (!work_hours.empty? ? work_hours.keys : work_week).each_with_object([]) do |day_name, days|
117
- day_num = wday_to_int(day_name)
118
- days << day_num unless day_num.nil?
119
- end
143
+ days = (!work_hours.empty? ? work_hours.keys : work_week).map do |day_name|
144
+ wday_to_int(day_name)
145
+ end.compact
146
+
147
+ self._weekdays = SortedSet.new(days)
120
148
  end
121
149
 
122
150
  # loads the config data from a yaml file written as:
@@ -145,11 +173,11 @@ module BusinessTime
145
173
  end
146
174
 
147
175
  def with(config)
148
- self.local_config = config().dup
176
+ local_config_stack.push(config().dup)
149
177
  config.each { |k,v| send("#{k}=", v) } # calculations are done on setting
150
178
  yield
151
179
  ensure
152
- self.local_config = nil
180
+ local_config_stack.pop
153
181
  end
154
182
 
155
183
  def default_config
@@ -168,8 +196,8 @@ module BusinessTime
168
196
  end
169
197
 
170
198
  def reset
171
- self.config = default_config
172
- self.local_config = nil
199
+ local_config_stack.clear
200
+ self.config = default_config
173
201
  end
174
202
 
175
203
  def deep_dup(object)
@@ -13,4 +13,49 @@ class Date
13
13
  (self...to_date).select(&:workday?)
14
14
  end
15
15
  end
16
+
17
+ # Adapted from:
18
+ # https://github.com/activewarehouse/activewarehouse/blob/master/lib/active_warehouse/core_ext/time/calculations.rb
19
+
20
+ def week
21
+ cyw = ((yday - 1) / 7) + 1
22
+ cyw = 52 if cyw == 53
23
+ cyw
24
+ end
25
+
26
+ def quarter
27
+ ((month - 1) / 3) + 1
28
+ end
29
+
30
+ def fiscal_month_offset
31
+ BusinessTime::Config.fiscal_month_offset
32
+ end
33
+
34
+ def fiscal_year_week
35
+ fyw = ((fiscal_year_yday - 1) / 7) + 1
36
+ fyw = 52 if fyw == 53
37
+ fyw
38
+ end
39
+
40
+ def fiscal_year_month
41
+ shifted_month = month - (fiscal_month_offset - 1)
42
+ shifted_month += 12 if shifted_month <= 0
43
+ shifted_month
44
+ end
45
+
46
+ def fiscal_year_quarter
47
+ ((fiscal_year_month - 1) / 3) + 1
48
+ end
49
+
50
+ def fiscal_year
51
+ month >= fiscal_month_offset ? year + 1 : year
52
+ end
53
+
54
+ def fiscal_year_yday
55
+ offset_days = 0
56
+ 1.upto(fiscal_month_offset - 1) { |m| offset_days += ::Time.days_in_month(m, year) }
57
+ shifted_year_day = yday - offset_days
58
+ shifted_year_day += 365 if shifted_year_day <= 0
59
+ shifted_year_day
60
+ end
16
61
  end
@@ -0,0 +1,34 @@
1
+ module BusinessTime
2
+ class ParsedTime
3
+ include Comparable
4
+
5
+ attr_reader :hour, :min, :sec
6
+
7
+ def initialize(hour, min = 0, sec = 0)
8
+ @hour = hour
9
+ @min = min
10
+ @sec = sec
11
+ end
12
+
13
+ def self.parse(time_or_string)
14
+ if time_or_string.is_a?(String)
15
+ time = Time.parse(time_or_string)
16
+ else
17
+ time = time_or_string
18
+ end
19
+ new(time.hour, time.min, time.sec)
20
+ end
21
+
22
+ def to_s
23
+ "#{hour}:#{min}:#{sec}"
24
+ end
25
+
26
+ def -(other)
27
+ (hour - other.hour) * 3600 + (min - other.min) * 60 + sec - other.sec
28
+ end
29
+
30
+ def <=>(other)
31
+ [hour, min, sec] <=> [other.hour, other.min, other.sec]
32
+ end
33
+ end
34
+ end
@@ -17,7 +17,7 @@ module BusinessTime
17
17
  # Note: It pretends that this day is a workday whether or not it really is a
18
18
  # workday.
19
19
  def end_of_workday(day)
20
- end_of_workday = Time.parse(BusinessTime::Config.end_of_workday(day))
20
+ end_of_workday = BusinessTime::Config.end_of_workday(day)
21
21
  change_business_time(day,end_of_workday.hour,end_of_workday.min,end_of_workday.sec)
22
22
  end
23
23
 
@@ -26,7 +26,7 @@ module BusinessTime
26
26
  # Note: It pretends that this day is a workday whether or not it really is a
27
27
  # workday.
28
28
  def beginning_of_workday(day)
29
- beginning_of_workday = Time.parse(BusinessTime::Config.beginning_of_workday(day))
29
+ beginning_of_workday = BusinessTime::Config.beginning_of_workday(day)
30
30
  change_business_time(day,beginning_of_workday.hour,beginning_of_workday.min,beginning_of_workday.sec)
31
31
  end
32
32
 
@@ -116,15 +116,15 @@ module BusinessTime
116
116
  if hours = BusinessTime::Config.work_hours[day]
117
117
  BusinessTime::Config.work_hours_total[day] ||= begin
118
118
  hours_last = hours.last
119
- if hours_last == '00:00'
120
- (Time.parse('23:59') - Time.parse(hours.first)) + 1.minute
119
+ if hours_last == ParsedTime.new(0, 0)
120
+ (ParsedTime.new(23, 59) - hours.first) + 1.minute
121
121
  else
122
- Time.parse(hours_last) - Time.parse(hours.first)
122
+ hours_last - hours.first
123
123
  end
124
124
  end
125
125
  else
126
126
  BusinessTime::Config.work_hours_total[:default] ||= begin
127
- Time.parse(BusinessTime::Config.end_of_workday) - Time.parse(BusinessTime::Config.beginning_of_workday)
127
+ BusinessTime::Config.end_of_workday - BusinessTime::Config.beginning_of_workday
128
128
  end
129
129
  end
130
130
  end
@@ -169,5 +169,30 @@ module BusinessTime
169
169
  def during_business_hours?
170
170
  self.workday? && self.to_i.between?(Time.beginning_of_workday(self).to_i, Time.end_of_workday(self).to_i)
171
171
  end
172
+
173
+ def consecutive_workdays
174
+ workday? ? consecutive_days { |date| date.workday? } : []
175
+ end
176
+
177
+ def consecutive_non_working_days
178
+ !workday? ? consecutive_days { |date| !date.workday? } : []
179
+ end
180
+
181
+ private
182
+
183
+ def consecutive_days
184
+ days = []
185
+ date = self + 1.day
186
+ while yield(date)
187
+ days << date
188
+ date += 1.day
189
+ end
190
+ date = self - 1.day
191
+ while yield(date)
192
+ days << date
193
+ date -= 1.day
194
+ end
195
+ (days << self).sort
196
+ end
172
197
  end
173
198
  end
@@ -1,3 +1,3 @@
1
1
  module BusinessTime
2
- VERSION = "0.8.0"
2
+ VERSION = "0.10.0"
3
3
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: business_time
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bokmann
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-01 00:00:00.000000000 Z
11
+ date: 2021-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 4.2.8
19
+ version: 3.2.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 4.2.8
26
+ version: 3.2.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: tzinfo
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sorted_set
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -111,6 +125,7 @@ files:
111
125
  - lib/business_time/core_ext/date.rb
112
126
  - lib/business_time/core_ext/integer.rb
113
127
  - lib/business_time/core_ext/time.rb
128
+ - lib/business_time/parsed_time.rb
114
129
  - lib/business_time/time_extensions.rb
115
130
  - lib/business_time/version.rb
116
131
  - lib/generators/business_time/config_generator.rb
@@ -121,7 +136,7 @@ homepage: https://github.com/bokmann/business_time
121
136
  licenses:
122
137
  - MIT
123
138
  metadata: {}
124
- post_install_message:
139
+ post_install_message:
125
140
  rdoc_options: []
126
141
  require_paths:
127
142
  - lib
@@ -136,9 +151,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
151
  - !ruby/object:Gem::Version
137
152
  version: '0'
138
153
  requirements: []
139
- rubyforge_project:
140
- rubygems_version: 2.6.11
141
- signing_key:
154
+ rubygems_version: 3.2.11
155
+ signing_key:
142
156
  specification_version: 4
143
157
  summary: Support for doing time math in business hours and days
144
158
  test_files: []