biz 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![biz](http://d26a57ydsghvgx.cloudfront.net/www/public/assets/images/bizlogo.png)
|
2
|
+
|
2
3
|
[![Gem Version](https://badge.fury.io/rb/biz.svg)](http://badge.fury.io/rb/biz)
|
3
4
|
[![Build Status](https://travis-ci.org/zendesk/biz.svg?branch=master)](https://travis-ci.org/zendesk/biz)
|
4
5
|
[![Code Climate](https://codeclimate.com/github/zendesk/biz/badges/gpa.svg)](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
|