biz 1.1.0 → 1.2.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.
- checksums.yaml +4 -4
- data/README.md +7 -6
- data/lib/biz.rb +7 -4
- data/lib/biz/calculation/active.rb +6 -4
- data/lib/biz/calculation/duration_within.rb +2 -3
- data/lib/biz/calculation/for_duration.rb +91 -17
- data/lib/biz/configuration.rb +7 -2
- data/lib/biz/core_ext/date.rb +1 -5
- data/lib/biz/core_ext/fixnum.rb +1 -1
- data/lib/biz/dates.rb +14 -0
- data/lib/biz/day_time.rb +23 -19
- data/lib/biz/duration.rb +0 -20
- data/lib/biz/holiday.rb +2 -0
- data/lib/biz/periods.rb +4 -2
- data/lib/biz/periods/abstract.rb +5 -3
- data/lib/biz/periods/after.rb +4 -0
- data/lib/biz/periods/before.rb +4 -0
- data/lib/biz/schedule.rb +16 -11
- data/lib/biz/timeline.rb +4 -2
- data/lib/biz/timeline/abstract.rb +4 -2
- data/lib/biz/timeline/backward.rb +4 -0
- data/lib/biz/timeline/forward.rb +4 -0
- data/lib/biz/version.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33c5eb58e4e8d9ee6f5b42c071c99e8c8cf8ed5b
|
4
|
+
data.tar.gz: 05fdc0175b78e6147aaffcaea14d274955744a2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6556e0069a7f41c67a0cdf9d80f7886b622b2cc043ba6c65a1bb6201d65f6a39facc1a8073b5c4d612f0d2d152a05b72bdbd8d2183dfc40d4cfc98ff34c4fc1
|
7
|
+
data.tar.gz: b42dfdb7a0fbba2386b1fa13e45ced2314517ea8c35c9ec66873b5804c6ee7dea7b61f9c8ab03bbbf856428127a71e25ce9c9b7cc9eeb4718878536559e079c8
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+

|
2
|
+
|
2
3
|
[](http://badge.fury.io/rb/biz)
|
3
4
|
[](https://travis-ci.org/zendesk/biz)
|
4
5
|
[](https://codeclimate.com/github/zendesk/biz)
|
@@ -75,6 +76,9 @@ Biz.time(30, :minutes).before(Time.utc(2015, 1, 1, 11, 45))
|
|
75
76
|
# Find the time an amount of business time *after* a specified starting time
|
76
77
|
Biz.time(2, :hours).after(Time.utc(2015, 12, 25, 9, 30))
|
77
78
|
|
79
|
+
# Calculations can be performed in seconds, minutes, hours, or days
|
80
|
+
Biz.time(1, :day).after(Time.utc(2015, 1, 8, 10))
|
81
|
+
|
78
82
|
# Find the amount of business time between two times
|
79
83
|
Biz.within(Time.utc(2015, 3, 7), Time.utc(2015, 3, 14)).in_seconds
|
80
84
|
|
@@ -89,9 +93,7 @@ which you can use to do your own custom calculations or just get a better idea
|
|
89
93
|
of what's happening under the hood:
|
90
94
|
|
91
95
|
```ruby
|
92
|
-
Biz.periods
|
93
|
-
.after(Time.utc(2015, 1, 10, 10))
|
94
|
-
.timeline.forward
|
96
|
+
Biz.periods.after(Time.utc(2015, 1, 10, 10)).timeline
|
95
97
|
.until(Time.utc(2015, 1, 17, 10)).to_a
|
96
98
|
|
97
99
|
#=> [#<Biz::TimeSegment start_time=2015-01-10 18:00:00 UTC end_time=2015-01-10 22:00:00 UTC>,
|
@@ -102,8 +104,7 @@ Biz.periods
|
|
102
104
|
# #<Biz::TimeSegment start_time=2015-01-15 21:00:00 UTC end_time=2015-01-16 01:00:00 UTC>]
|
103
105
|
|
104
106
|
Biz.periods
|
105
|
-
.before(Time.utc(2015, 5, 5, 12, 34, 57))
|
106
|
-
.timeline.backward
|
107
|
+
.before(Time.utc(2015, 5, 5, 12, 34, 57)).timeline
|
107
108
|
.for(Biz::Duration.minutes(3_598)).to_a
|
108
109
|
|
109
110
|
#=> [#<Biz::TimeSegment start_time=2015-05-05 07:00:00 UTC end_time=2015-05-05 12:34:57 UTC>,
|
data/lib/biz.rb
CHANGED
@@ -4,6 +4,7 @@ require 'forwardable'
|
|
4
4
|
require 'set'
|
5
5
|
|
6
6
|
require 'abstract_type'
|
7
|
+
require 'clavius'
|
7
8
|
require 'equalizer'
|
8
9
|
require 'memoizable'
|
9
10
|
require 'tzinfo'
|
@@ -13,8 +14,8 @@ module Biz
|
|
13
14
|
|
14
15
|
extend Forwardable
|
15
16
|
|
16
|
-
def configure(&
|
17
|
-
Thread.current[:biz_schedule] = Schedule.new(&
|
17
|
+
def configure(&config)
|
18
|
+
Thread.current[:biz_schedule] = Schedule.new(&config)
|
18
19
|
end
|
19
20
|
|
20
21
|
delegate %i[
|
@@ -22,6 +23,8 @@ module Biz
|
|
22
23
|
holidays
|
23
24
|
time_zone
|
24
25
|
periods
|
26
|
+
date
|
27
|
+
dates
|
25
28
|
time
|
26
29
|
within
|
27
30
|
in_hours?
|
@@ -37,13 +40,12 @@ module Biz
|
|
37
40
|
end
|
38
41
|
end
|
39
42
|
|
40
|
-
require 'biz/version'
|
41
|
-
|
42
43
|
require 'biz/date'
|
43
44
|
require 'biz/time'
|
44
45
|
|
45
46
|
require 'biz/calculation'
|
46
47
|
require 'biz/configuration'
|
48
|
+
require 'biz/dates'
|
47
49
|
require 'biz/day'
|
48
50
|
require 'biz/day_of_week'
|
49
51
|
require 'biz/day_time'
|
@@ -56,3 +58,4 @@ require 'biz/timeline'
|
|
56
58
|
require 'biz/time_segment'
|
57
59
|
require 'biz/week'
|
58
60
|
require 'biz/week_time'
|
61
|
+
require 'biz/version'
|
@@ -2,19 +2,21 @@ module Biz
|
|
2
2
|
module Calculation
|
3
3
|
class Active
|
4
4
|
|
5
|
-
attr_reader :schedule,
|
6
|
-
:time
|
7
|
-
|
8
5
|
def initialize(schedule, time)
|
9
6
|
@schedule = schedule
|
10
7
|
@time = time
|
11
8
|
end
|
12
9
|
|
13
|
-
def
|
10
|
+
def result
|
14
11
|
schedule.intervals.any? { |interval| interval.contains?(time) } &&
|
15
12
|
schedule.holidays.none? { |holiday| holiday.contains?(time) }
|
16
13
|
end
|
17
14
|
|
15
|
+
protected
|
16
|
+
|
17
|
+
attr_reader :schedule,
|
18
|
+
:time
|
19
|
+
|
18
20
|
end
|
19
21
|
end
|
20
22
|
end
|
@@ -2,10 +2,9 @@ module Biz
|
|
2
2
|
module Calculation
|
3
3
|
class DurationWithin < SimpleDelegator
|
4
4
|
|
5
|
-
def initialize(
|
5
|
+
def initialize(schedule, calculation_period)
|
6
6
|
super(
|
7
|
-
periods.after(calculation_period.start_time)
|
8
|
-
.timeline.forward
|
7
|
+
schedule.periods.after(calculation_period.start_time).timeline
|
9
8
|
.until(calculation_period.end_time)
|
10
9
|
.map(&:duration)
|
11
10
|
.reduce(Duration.new(0), :+)
|
@@ -2,30 +2,104 @@ module Biz
|
|
2
2
|
module Calculation
|
3
3
|
class ForDuration
|
4
4
|
|
5
|
-
|
6
|
-
:duration
|
5
|
+
UNITS = Set.new(%i[second seconds minute minutes hour hours day days])
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
include AbstractType
|
8
|
+
|
9
|
+
def self.with_unit(schedule, scalar, unit)
|
10
|
+
unless UNITS.include?(unit)
|
11
|
+
fail ArgumentError, 'The unit is not supported.'
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
send(unit, schedule, scalar)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.unit
|
18
|
+
name.split('::').last.downcase.to_sym
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(schedule, scalar)
|
22
|
+
@schedule = schedule
|
23
|
+
@scalar = scalar
|
15
24
|
end
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
26
|
+
abstract_method :before,
|
27
|
+
:after
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
attr_reader :schedule,
|
32
|
+
:scalar
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def unit
|
37
|
+
self.class.unit
|
22
38
|
end
|
23
39
|
|
24
|
-
|
25
|
-
|
26
|
-
.
|
27
|
-
|
28
|
-
|
40
|
+
[
|
41
|
+
*%i[second seconds minute minutes hour hours].map { |unit|
|
42
|
+
const_set(unit.to_s.capitalize,
|
43
|
+
Class.new(self) do
|
44
|
+
def before(time)
|
45
|
+
timeline(:before, time).last.start_time
|
46
|
+
end
|
47
|
+
|
48
|
+
def after(time)
|
49
|
+
timeline(:after, time).last.end_time
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def timeline(direction, time)
|
55
|
+
schedule.periods.send(direction, time).timeline
|
56
|
+
.for(duration).to_a
|
57
|
+
end
|
58
|
+
|
59
|
+
def duration
|
60
|
+
Duration.send(unit, scalar)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
)
|
64
|
+
},
|
65
|
+
*%i[day days].map { |unit|
|
66
|
+
const_set(unit.to_s.capitalize,
|
67
|
+
Class.new(self) do
|
68
|
+
def before(time)
|
69
|
+
periods(:before, time).first.end_time
|
70
|
+
end
|
71
|
+
|
72
|
+
def after(time)
|
73
|
+
periods(:after, time).first.start_time
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def periods(direction, time)
|
79
|
+
schedule.periods.send(direction, advanced_date(direction, time))
|
80
|
+
end
|
81
|
+
|
82
|
+
def advanced_date(direction, time)
|
83
|
+
schedule.in_zone.on_date(
|
84
|
+
schedule.dates.days(scalar).send(direction, local(time)),
|
85
|
+
day_time(time)
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def day_time(time)
|
90
|
+
DayTime.from_time(local(time))
|
91
|
+
end
|
92
|
+
|
93
|
+
def local(time)
|
94
|
+
schedule.in_zone.local(time)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
)
|
98
|
+
}
|
99
|
+
].each do |unit_class|
|
100
|
+
define_singleton_method(unit_class.unit) { |schedule, scalar|
|
101
|
+
unit_class.new(schedule, scalar)
|
102
|
+
}
|
29
103
|
end
|
30
104
|
|
31
105
|
end
|
data/lib/biz/configuration.rb
CHANGED
@@ -4,7 +4,7 @@ module Biz
|
|
4
4
|
include Memoizable
|
5
5
|
|
6
6
|
def initialize
|
7
|
-
@raw = Raw.new.tap do |raw| yield raw end
|
7
|
+
@raw = Raw.new.tap do |raw| yield raw if block_given? end
|
8
8
|
end
|
9
9
|
|
10
10
|
def intervals
|
@@ -21,6 +21,10 @@ module Biz
|
|
21
21
|
TZInfo::TimezoneProxy.new(raw.time_zone)
|
22
22
|
end
|
23
23
|
|
24
|
+
def weekdays
|
25
|
+
raw.hours.keys.to_set
|
26
|
+
end
|
27
|
+
|
24
28
|
protected
|
25
29
|
|
26
30
|
attr_reader :raw
|
@@ -44,7 +48,8 @@ module Biz
|
|
44
48
|
end
|
45
49
|
|
46
50
|
memoize :intervals,
|
47
|
-
:holidays
|
51
|
+
:holidays,
|
52
|
+
:weekdays
|
48
53
|
|
49
54
|
Raw = Struct.new(:hours, :holidays, :time_zone) do
|
50
55
|
module Default
|
data/lib/biz/core_ext/date.rb
CHANGED
@@ -3,11 +3,7 @@ module Biz
|
|
3
3
|
module Date
|
4
4
|
|
5
5
|
def business_day?
|
6
|
-
Biz.
|
7
|
-
.after(Biz::Time.new(Biz.time_zone).on_date(self, DayTime.midnight))
|
8
|
-
.timeline.forward
|
9
|
-
.until(Biz::Time.new(Biz.time_zone).on_date(self, DayTime.endnight))
|
10
|
-
.any?
|
6
|
+
Biz.dates.active?(self)
|
11
7
|
end
|
12
8
|
|
13
9
|
end
|
data/lib/biz/core_ext/fixnum.rb
CHANGED
data/lib/biz/dates.rb
ADDED
data/lib/biz/day_time.rb
CHANGED
@@ -8,34 +8,38 @@ module Biz
|
|
8
8
|
|
9
9
|
extend Forwardable
|
10
10
|
|
11
|
-
|
12
|
-
new(hour * Time::MINUTES_IN_HOUR)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.from_timestamp(timestamp)
|
16
|
-
timestamp.match(TIMESTAMP_PATTERN) { |match|
|
17
|
-
new(match[:hour].to_i * Time::MINUTES_IN_HOUR + match[:minute].to_i)
|
18
|
-
}
|
19
|
-
end
|
11
|
+
class << self
|
20
12
|
|
21
|
-
|
22
|
-
|
23
|
-
|
13
|
+
def from_time(time)
|
14
|
+
new(time.hour * Time::MINUTES_IN_HOUR + time.min)
|
15
|
+
end
|
24
16
|
|
25
|
-
|
26
|
-
|
27
|
-
|
17
|
+
def from_hour(hour)
|
18
|
+
new(hour * Time::MINUTES_IN_HOUR)
|
19
|
+
end
|
28
20
|
|
29
|
-
|
30
|
-
|
31
|
-
|
21
|
+
def from_timestamp(timestamp)
|
22
|
+
timestamp.match(TIMESTAMP_PATTERN) { |match|
|
23
|
+
new(match[:hour].to_i * Time::MINUTES_IN_HOUR + match[:minute].to_i)
|
24
|
+
}
|
25
|
+
end
|
32
26
|
|
33
|
-
|
27
|
+
def midnight
|
28
|
+
MIDNIGHT
|
29
|
+
end
|
34
30
|
|
35
31
|
alias_method :am, :midnight
|
36
32
|
|
33
|
+
def noon
|
34
|
+
NOON
|
35
|
+
end
|
36
|
+
|
37
37
|
alias_method :pm, :noon
|
38
38
|
|
39
|
+
def endnight
|
40
|
+
ENDNIGHT
|
41
|
+
end
|
42
|
+
|
39
43
|
end
|
40
44
|
|
41
45
|
attr_reader :day_minute
|
data/lib/biz/duration.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module Biz
|
2
2
|
class Duration
|
3
3
|
|
4
|
-
UNITS = Set.new(%i[second seconds minute minutes hour hours day days])
|
5
|
-
|
6
4
|
include Equalizer.new(:seconds)
|
7
5
|
include Comparable
|
8
6
|
|
@@ -10,14 +8,6 @@ module Biz
|
|
10
8
|
|
11
9
|
class << self
|
12
10
|
|
13
|
-
def with_unit(scalar, unit)
|
14
|
-
unless UNITS.include?(unit)
|
15
|
-
fail ArgumentError, 'The unit is not supported.'
|
16
|
-
end
|
17
|
-
|
18
|
-
public_send(unit, scalar)
|
19
|
-
end
|
20
|
-
|
21
11
|
def seconds(seconds)
|
22
12
|
new(seconds)
|
23
13
|
end
|
@@ -36,12 +26,6 @@ module Biz
|
|
36
26
|
|
37
27
|
alias_method :hour, :hours
|
38
28
|
|
39
|
-
def days(days)
|
40
|
-
new(days * Time::DAY)
|
41
|
-
end
|
42
|
-
|
43
|
-
alias_method :day, :days
|
44
|
-
|
45
29
|
end
|
46
30
|
|
47
31
|
attr_reader :seconds
|
@@ -64,10 +48,6 @@ module Biz
|
|
64
48
|
seconds / Time::HOUR
|
65
49
|
end
|
66
50
|
|
67
|
-
def in_days
|
68
|
-
seconds / Time::DAY
|
69
|
-
end
|
70
|
-
|
71
51
|
def +(other)
|
72
52
|
self.class.new(seconds + other.seconds)
|
73
53
|
end
|
data/lib/biz/holiday.rb
CHANGED
data/lib/biz/periods.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
module Biz
|
2
2
|
class Periods
|
3
3
|
|
4
|
-
attr_reader :schedule
|
5
|
-
|
6
4
|
def initialize(schedule)
|
7
5
|
@schedule = schedule
|
8
6
|
end
|
@@ -15,6 +13,10 @@ module Biz
|
|
15
13
|
Before.new(schedule, origin)
|
16
14
|
end
|
17
15
|
|
16
|
+
protected
|
17
|
+
|
18
|
+
attr_reader :schedule
|
19
|
+
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
data/lib/biz/periods/abstract.rb
CHANGED
@@ -4,9 +4,6 @@ module Biz
|
|
4
4
|
|
5
5
|
extend Forwardable
|
6
6
|
|
7
|
-
attr_reader :schedule,
|
8
|
-
:origin
|
9
|
-
|
10
7
|
def initialize(schedule, origin)
|
11
8
|
@schedule = schedule
|
12
9
|
@origin = origin
|
@@ -20,6 +17,11 @@ module Biz
|
|
20
17
|
Timeline.new(self)
|
21
18
|
end
|
22
19
|
|
20
|
+
protected
|
21
|
+
|
22
|
+
attr_reader :schedule,
|
23
|
+
:origin
|
24
|
+
|
23
25
|
private
|
24
26
|
|
25
27
|
def periods
|
data/lib/biz/periods/after.rb
CHANGED
data/lib/biz/periods/before.rb
CHANGED
data/lib/biz/schedule.rb
CHANGED
@@ -3,40 +3,45 @@ module Biz
|
|
3
3
|
|
4
4
|
extend Forwardable
|
5
5
|
|
6
|
-
def initialize(&
|
7
|
-
@configuration = Configuration.new(&
|
6
|
+
def initialize(&config)
|
7
|
+
@configuration = Configuration.new(&config)
|
8
8
|
end
|
9
9
|
|
10
10
|
delegate %i[
|
11
11
|
intervals
|
12
12
|
holidays
|
13
13
|
time_zone
|
14
|
+
weekdays
|
14
15
|
] => :configuration
|
15
16
|
|
16
17
|
def periods
|
17
18
|
Periods.new(self)
|
18
19
|
end
|
19
20
|
|
21
|
+
def dates
|
22
|
+
Dates.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method :date, :dates
|
26
|
+
|
20
27
|
def time(scalar, unit)
|
21
|
-
Calculation::ForDuration.
|
22
|
-
periods,
|
23
|
-
Duration.with_unit(scalar, unit)
|
24
|
-
)
|
28
|
+
Calculation::ForDuration.with_unit(self, scalar, unit)
|
25
29
|
end
|
26
30
|
|
27
31
|
def within(origin, terminus)
|
28
|
-
Calculation::DurationWithin.new(
|
29
|
-
periods,
|
30
|
-
TimeSegment.new(origin, terminus)
|
31
|
-
)
|
32
|
+
Calculation::DurationWithin.new(self, TimeSegment.new(origin, terminus))
|
32
33
|
end
|
33
34
|
|
34
35
|
def in_hours?(time)
|
35
|
-
Calculation::Active.new(self, time).
|
36
|
+
Calculation::Active.new(self, time).result
|
36
37
|
end
|
37
38
|
|
38
39
|
alias_method :business_hours?, :in_hours?
|
39
40
|
|
41
|
+
def in_zone
|
42
|
+
Time.new(time_zone)
|
43
|
+
end
|
44
|
+
|
40
45
|
protected
|
41
46
|
|
42
47
|
attr_reader :configuration
|
data/lib/biz/timeline.rb
CHANGED
data/lib/biz/timeline/forward.rb
CHANGED
data/lib/biz/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: biz
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Craig Little
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-03-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: abstract_type
|
@@ -25,6 +25,20 @@ dependencies:
|
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: 0.0.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: clavius
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '1.0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.0'
|
28
42
|
- !ruby/object:Gem::Dependency
|
29
43
|
name: equalizer
|
30
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,6 +129,7 @@ files:
|
|
115
129
|
- lib/biz/core_ext/fixnum.rb
|
116
130
|
- lib/biz/core_ext/time.rb
|
117
131
|
- lib/biz/date.rb
|
132
|
+
- lib/biz/dates.rb
|
118
133
|
- lib/biz/day.rb
|
119
134
|
- lib/biz/day_of_week.rb
|
120
135
|
- lib/biz/day_time.rb
|