biz 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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