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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f865f5197acf9cc026d7d77c15626a7d03a5e8c1
4
- data.tar.gz: 5d150d70060e27513f373c3b32e96dbd0e4f68fc
3
+ metadata.gz: 33c5eb58e4e8d9ee6f5b42c071c99e8c8cf8ed5b
4
+ data.tar.gz: 05fdc0175b78e6147aaffcaea14d274955744a2d
5
5
  SHA512:
6
- metadata.gz: a3eb38f1f622c23d1d1524ad053ae07bd789277d0a3b5b5a8bb42a2aef7453d8a976eed7878e97b8207861b0e58860d6a6e5950bc5507468e44ea62ba82c6e3e
7
- data.tar.gz: 6871ae32bf137626682626e80f8f4ae115b162b1d7ac104ed1b615e0a3b06baa7979a3ea79ab5628d25c79b54d42910ddd415e28b2ce6e0bfc28de1a44b1a7fc
6
+ metadata.gz: c6556e0069a7f41c67a0cdf9d80f7886b622b2cc043ba6c65a1bb6201d65f6a39facc1a8073b5c4d612f0d2d152a05b72bdbd8d2183dfc40d4cfc98ff34c4fc1
7
+ data.tar.gz: b42dfdb7a0fbba2386b1fa13e45ced2314517ea8c35c9ec66873b5804c6ee7dea7b61f9c8ab03bbbf856428127a71e25ce9c9b7cc9eeb4718878536559e079c8
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
- # biz
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(&block)
17
- Thread.current[:biz_schedule] = Schedule.new(&block)
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 active?
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(periods, calculation_period)
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
- attr_reader :periods,
6
- :duration
5
+ UNITS = Set.new(%i[second seconds minute minutes hour hours day days])
7
6
 
8
- def initialize(periods, duration)
9
- unless duration.positive?
10
- fail ArgumentError, 'Duration adjustment must be positive.'
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
- @periods = periods
14
- @duration = duration
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
- def before(time)
18
- periods.before(time)
19
- .timeline.backward
20
- .for(duration).to_a
21
- .last.start_time
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
- def after(time)
25
- periods.after(time)
26
- .timeline.forward
27
- .for(duration).to_a
28
- .last.end_time
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
@@ -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
@@ -3,11 +3,7 @@ module Biz
3
3
  module Date
4
4
 
5
5
  def business_day?
6
- Biz.periods
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
@@ -2,7 +2,7 @@ module Biz
2
2
  module CoreExt
3
3
  module Fixnum
4
4
 
5
- %i[second seconds minute minutes hour hours].each do |unit|
5
+ Calculation::ForDuration::UNITS.each do |unit|
6
6
  define_method("business_#{unit}") { Biz.time(self, unit) }
7
7
  end
8
8
 
@@ -0,0 +1,14 @@
1
+ module Biz
2
+ class Dates < SimpleDelegator
3
+
4
+ def initialize(schedule)
5
+ super(
6
+ Clavius::Schedule.new do |c|
7
+ c.weekdays = schedule.weekdays
8
+ c.excluded = schedule.holidays.map(&:date)
9
+ end
10
+ )
11
+ end
12
+
13
+ end
14
+ end
@@ -8,34 +8,38 @@ module Biz
8
8
 
9
9
  extend Forwardable
10
10
 
11
- def self.from_hour(hour)
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
- def self.midnight
22
- MIDNIGHT
23
- end
13
+ def from_time(time)
14
+ new(time.hour * Time::MINUTES_IN_HOUR + time.min)
15
+ end
24
16
 
25
- def self.noon
26
- NOON
27
- end
17
+ def from_hour(hour)
18
+ new(hour * Time::MINUTES_IN_HOUR)
19
+ end
28
20
 
29
- def self.endnight
30
- ENDNIGHT
31
- end
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
- class << self
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
@@ -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
@@ -22,5 +22,7 @@ module Biz
22
22
  )
23
23
  end
24
24
 
25
+ alias_method :to_date, :date
26
+
25
27
  end
26
28
  end
@@ -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
 
@@ -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
@@ -2,6 +2,10 @@ module Biz
2
2
  class Periods
3
3
  class After < Abstract
4
4
 
5
+ def timeline
6
+ super.forward
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def weeks
@@ -2,6 +2,10 @@ module Biz
2
2
  class Periods
3
3
  class Before < Abstract
4
4
 
5
+ def timeline
6
+ super.backward
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def weeks
@@ -3,40 +3,45 @@ module Biz
3
3
 
4
4
  extend Forwardable
5
5
 
6
- def initialize(&block)
7
- @configuration = Configuration.new(&block)
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.new(
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).active?
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
@@ -1,8 +1,6 @@
1
1
  module Biz
2
2
  class Timeline
3
3
 
4
- attr_reader :periods
5
-
6
4
  def initialize(periods)
7
5
  @periods = periods
8
6
  end
@@ -15,6 +13,10 @@ module Biz
15
13
  Backward.new(periods)
16
14
  end
17
15
 
16
+ protected
17
+
18
+ attr_reader :periods
19
+
18
20
  end
19
21
  end
20
22
 
@@ -2,8 +2,6 @@ module Biz
2
2
  class Timeline
3
3
  class Abstract
4
4
 
5
- attr_reader :periods
6
-
7
5
  def initialize(periods)
8
6
  @periods = periods.lazy
9
7
  end
@@ -36,6 +34,10 @@ module Biz
36
34
  end
37
35
  end
38
36
 
37
+ protected
38
+
39
+ attr_reader :periods
40
+
39
41
  end
40
42
  end
41
43
  end
@@ -2,6 +2,10 @@ module Biz
2
2
  class Timeline
3
3
  class Backward < Abstract
4
4
 
5
+ def backward
6
+ self
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def occurred?(period, time)
@@ -2,6 +2,10 @@ module Biz
2
2
  class Timeline
3
3
  class Forward < Abstract
4
4
 
5
+ def forward
6
+ self
7
+ end
8
+
5
9
  private
6
10
 
7
11
  def occurred?(period, time)
@@ -1,5 +1,5 @@
1
1
  module Biz
2
2
 
3
- VERSION = '1.1.0'
3
+ VERSION = '1.2.0'
4
4
 
5
5
  end
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.1.0
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-02-26 00:00:00.000000000 Z
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