biz 0.0.1 → 1.0.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.
@@ -0,0 +1,37 @@
1
+ module Biz
2
+ class Schedule
3
+
4
+ extend Forwardable
5
+
6
+ def initialize(&block)
7
+ @configuration = Configuration.new(&block)
8
+ end
9
+
10
+ delegate %i[
11
+ intervals
12
+ holidays
13
+ time_zone
14
+ ] => :configuration
15
+
16
+ def periods
17
+ Periods.new(self)
18
+ end
19
+
20
+ def time(scalar, unit)
21
+ Calculation::ForDuration.new(self, Duration.with_unit(scalar, unit))
22
+ end
23
+
24
+ def within(origin, terminus)
25
+ Calculation::DurationWithin.new(self, TimeSegment.new(origin, terminus))
26
+ end
27
+
28
+ def business_hours?(time)
29
+ Calculation::Active.new(self, time).active?
30
+ end
31
+
32
+ protected
33
+
34
+ attr_reader :configuration
35
+
36
+ end
37
+ end
@@ -0,0 +1,49 @@
1
+ module Biz
2
+ class Time
3
+
4
+ MINUTE = 60
5
+ HOUR = 60 * MINUTE
6
+ DAY = 24 * HOUR
7
+ WEEK = 7 * DAY
8
+
9
+ MINUTES_IN_HOUR = 60
10
+ HOURS_IN_DAY = 24
11
+ DAYS_IN_WEEK = 7
12
+
13
+ MINUTES_IN_DAY = MINUTES_IN_HOUR * HOURS_IN_DAY
14
+ MINUTES_IN_WEEK = MINUTES_IN_DAY * DAYS_IN_WEEK
15
+
16
+ BIG_BANG = ::Time.new(-10**100)
17
+ HEAT_DEATH = ::Time.new(10**100)
18
+
19
+ attr_reader :time_zone
20
+
21
+ def initialize(time_zone)
22
+ @time_zone = time_zone
23
+ end
24
+
25
+ def local(time)
26
+ time_zone.utc_to_local(time.utc)
27
+ end
28
+
29
+ def on_date(date, day_time)
30
+ time_zone.local_to_utc(
31
+ ::Time.utc(
32
+ date.year,
33
+ date.month,
34
+ date.mday,
35
+ day_time.hour,
36
+ day_time.minute
37
+ ),
38
+ true
39
+ )
40
+ rescue TZInfo::PeriodNotFound
41
+ on_date(date, DayTime.new(day_time.day_minute + MINUTES_IN_HOUR))
42
+ end
43
+
44
+ def during_week(week, week_time)
45
+ on_date(week.start_date + week_time.wday, week_time)
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+ module Biz
2
+ class TimeSegment
3
+
4
+ include Equalizer.new(:start_time, :end_time)
5
+
6
+ def self.before(time)
7
+ new(Time::BIG_BANG, time)
8
+ end
9
+
10
+ def self.after(time)
11
+ new(time, Time::HEAT_DEATH)
12
+ end
13
+
14
+ attr_reader :start_time,
15
+ :end_time
16
+
17
+ def initialize(start_time, end_time)
18
+ @start_time = start_time
19
+ @end_time = end_time
20
+ end
21
+
22
+ def duration
23
+ Duration.new(end_time - start_time)
24
+ end
25
+
26
+ def endpoints
27
+ [start_time, end_time]
28
+ end
29
+
30
+ def empty?
31
+ start_time >= end_time
32
+ end
33
+
34
+ def contains?(time)
35
+ (start_time..end_time).cover?(time)
36
+ end
37
+
38
+ def &(other)
39
+ self.class.new(
40
+ lower_bound(other),
41
+ [lower_bound(other), upper_bound(other)].max
42
+ )
43
+ end
44
+
45
+ def /(other)
46
+ [
47
+ self.class.new(start_time, other.start_time),
48
+ self.class.new(other.end_time, end_time)
49
+ ].reject(&:empty?).map { |potential| self & potential }
50
+ end
51
+
52
+ private
53
+
54
+ def lower_bound(other)
55
+ [self, other].map(&:start_time).max
56
+ end
57
+
58
+ def upper_bound(other)
59
+ [self, other].map(&:end_time).min
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,24 @@
1
+ module Biz
2
+ class Timeline
3
+
4
+ attr_reader :periods
5
+
6
+ def initialize(periods)
7
+ @periods = periods
8
+ end
9
+
10
+ def forward
11
+ Forward.new(periods)
12
+ end
13
+
14
+ def backward
15
+ Backward.new(periods)
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+ require 'biz/timeline/abstract'
22
+
23
+ require 'biz/timeline/forward'
24
+ require 'biz/timeline/backward'
@@ -0,0 +1,41 @@
1
+ module Biz
2
+ class Timeline
3
+ class Abstract
4
+
5
+ attr_reader :periods
6
+
7
+ def initialize(periods)
8
+ @periods = periods.lazy
9
+ end
10
+
11
+ def until(terminus)
12
+ return enum_for(:until, terminus) unless block_given?
13
+
14
+ periods.map { |period|
15
+ period & comparison_period(period, terminus)
16
+ }.each do |period|
17
+ yield period unless period.empty?
18
+
19
+ break if occurred?(period, terminus)
20
+ end
21
+ end
22
+
23
+ def for(duration)
24
+ return enum_for(:for, duration) unless block_given?
25
+
26
+ remaining = duration
27
+
28
+ periods.map { |period|
29
+ period & duration_period(period, remaining)
30
+ }.each do |period|
31
+ yield period unless period.empty?
32
+
33
+ remaining -= period.duration
34
+
35
+ break unless remaining.positive?
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ module Biz
2
+ class Timeline
3
+ class Backward < Abstract
4
+
5
+ private
6
+
7
+ def occurred?(period, time)
8
+ period.start_time <= time
9
+ end
10
+
11
+ def comparison_period(period, terminus)
12
+ TimeSegment.new(terminus, period.end_time)
13
+ end
14
+
15
+ def duration_period(period, duration)
16
+ TimeSegment.new(
17
+ period.end_time - duration.in_seconds,
18
+ period.end_time
19
+ )
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module Biz
2
+ class Timeline
3
+ class Forward < Abstract
4
+
5
+ private
6
+
7
+ def occurred?(period, time)
8
+ period.end_time >= time
9
+ end
10
+
11
+ def comparison_period(period, terminus)
12
+ TimeSegment.new(period.start_time, terminus)
13
+ end
14
+
15
+ def duration_period(period, duration)
16
+ TimeSegment.new(
17
+ period.start_time,
18
+ period.start_time + duration.in_seconds
19
+ )
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,5 @@
1
1
  module Biz
2
- VERSION = "0.0.1"
2
+
3
+ VERSION = '1.0.0'
4
+
3
5
  end
@@ -0,0 +1,67 @@
1
+ module Biz
2
+ class Week
3
+
4
+ include Comparable
5
+
6
+ extend Forwardable
7
+
8
+ def self.from_date(date)
9
+ new(Day.from_date(date).to_i / Time::DAYS_IN_WEEK)
10
+ end
11
+
12
+ def self.from_time(time)
13
+ from_date(time.to_date)
14
+ end
15
+
16
+ class << self
17
+
18
+ alias_method :since_epoch, :from_time
19
+
20
+ end
21
+
22
+ attr_reader :week
23
+
24
+ delegate %i[
25
+ to_s
26
+ to_i
27
+ to_int
28
+ ] => :week
29
+
30
+ def initialize(week)
31
+ @week = Integer(week)
32
+ end
33
+
34
+ def start_date
35
+ Date.from_day(week * Time::DAYS_IN_WEEK)
36
+ end
37
+
38
+ def succ
39
+ self.class.new(week.succ)
40
+ end
41
+
42
+ def downto(final_week)
43
+ return enum_for(:downto, final_week) unless block_given?
44
+
45
+ week.downto(final_week.to_i).each do |raw_week|
46
+ yield self.class.new(raw_week)
47
+ end
48
+ end
49
+
50
+ def +(other)
51
+ self.class.new(week + other.week)
52
+ end
53
+
54
+ def coerce(other)
55
+ [self.class.new(other), self]
56
+ end
57
+
58
+ protected
59
+
60
+ def <=>(other)
61
+ return nil unless other.respond_to?(:to_i)
62
+
63
+ week <=> other.to_i
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ module Biz
2
+ module WeekTime
3
+ class << self
4
+
5
+ def from_time(time)
6
+ Start.from_time(time)
7
+ end
8
+
9
+ def start(week_minute)
10
+ Start.new(week_minute)
11
+ end
12
+
13
+ def end(week_minute)
14
+ End.new(week_minute)
15
+ end
16
+
17
+ alias_method :build, :start
18
+
19
+ end
20
+ end
21
+ end
22
+
23
+ require 'biz/week_time/abstract'
24
+
25
+ require 'biz/week_time/end'
26
+ require 'biz/week_time/start'
@@ -0,0 +1,75 @@
1
+ module Biz
2
+ module WeekTime
3
+ class Abstract
4
+
5
+ include AbstractType
6
+ include Comparable
7
+ include Memoizable
8
+
9
+ extend Forwardable
10
+
11
+ def self.from_time(time)
12
+ new(
13
+ time.wday * Time::MINUTES_IN_DAY +
14
+ time.hour * Time::MINUTES_IN_HOUR +
15
+ time.min
16
+ )
17
+ end
18
+
19
+ attr_reader :week_minute
20
+
21
+ def initialize(week_minute)
22
+ @week_minute = Integer(week_minute)
23
+ end
24
+
25
+ def wday_symbol
26
+ day_of_week.symbol
27
+ end
28
+
29
+ def coerce(other)
30
+ [self.class.new(other), self]
31
+ end
32
+
33
+ delegate wday: :day_of_week
34
+
35
+ delegate %i[
36
+ hour
37
+ minute
38
+ day_minute
39
+ timestamp
40
+ ] => :day_time
41
+
42
+ delegate strftime: :week_time
43
+
44
+ delegate %i[
45
+ to_s
46
+ to_i
47
+ to_int
48
+ ] => :week_minute
49
+
50
+ protected
51
+
52
+ def <=>(other)
53
+ return nil unless other.respond_to?(:to_i)
54
+
55
+ week_minute <=> other.to_i
56
+ end
57
+
58
+ private
59
+
60
+ def week_time
61
+ ::Time.new(
62
+ Date::EPOCH.year,
63
+ Date::EPOCH.month,
64
+ Date::EPOCH.mday + wday,
65
+ hour,
66
+ minute
67
+ )
68
+ end
69
+
70
+ abstract_method :day_of_week,
71
+ :day_time
72
+
73
+ end
74
+ end
75
+ end