Class: Roxbury::BusinessCalendar
- Inherits:
-
Object
- Object
- Roxbury::BusinessCalendar
- Defined in:
- lib/roxbury/business_calendar.rb
Constant Summary collapse
- DAYS_OF_THE_WEEK =
%w[Mon Tue Wed Thu Fri Sat Sun]
Instance Attribute Summary collapse
-
#working_hours ⇒ Object
readonly
Returns the value of attribute working_hours.
Instance Method Summary collapse
-
#add_working_days(to, number_of_days) ⇒ Date, Time
The result of adding the number_of_days to the given date.
- #add_working_hours(to, number_of_hours) ⇒ Object
- #holiday?(date_or_time) ⇒ Boolean
-
#initialize(working_hours: {}, holidays: []) ⇒ BusinessCalendar
constructor
A new instance of BusinessCalendar.
-
#next_working_day(date) ⇒ Object
If a Date is given, returns then next business day.
-
#prev_working_day(date) ⇒ Object
If a Date is given, returns then prev business day.
-
#roll_backward(date) ⇒ Object
Snaps the date to the end of the previous business day, unless it is already within the working hours of a business day.
-
#roll_forward(date) ⇒ Object
Snaps the date to the beginning of the next business day, unless it is already within the working hours of a business day.
-
#working_days_between(from, to) ⇒ Float
The number of working days between the given dates.
-
#working_hours_between(from, to) ⇒ Float
The number of working hours between the given dates.
- #working_hours_percentage(date) ⇒ Object
Constructor Details
#initialize(working_hours: {}, holidays: []) ⇒ BusinessCalendar
Returns a new instance of BusinessCalendar.
7 8 9 10 11 12 13 14 15 |
# File 'lib/roxbury/business_calendar.rb', line 7 def initialize working_hours: {}, holidays: [] @working_hours = DAYS_OF_THE_WEEK.inject({}) do |wh, dow| wh.merge dow => WorkingHours.parse(working_hours[dow]) end if @working_hours.values.all?(&:non_working?) raise ArgumentError, 'You must specify at least one working day in working_hours.' end @holidays = Set.new(holidays) end |
Instance Attribute Details
#working_hours ⇒ Object (readonly)
Returns the value of attribute working_hours.
5 6 7 |
# File 'lib/roxbury/business_calendar.rb', line 5 def working_hours @working_hours end |
Instance Method Details
#add_working_days(to, number_of_days) ⇒ Date, Time
Returns The result of adding the number_of_days to the given date. If a Date is given returns a Date, otherwise if a Time is given returns a Time.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/roxbury/business_calendar.rb', line 60 def add_working_days to, number_of_days raise ArgumentError, 'number_of_days must not be negative' if number_of_days < 0 if to.is_a?(Time) # this implementation would work for Date instances as well, but is around 10 times slower than the other in my machine add_working_hours(to, number_of_days * max_working_hours_in_a_day) else remaining_days = number_of_days rolling_date = to loop do remaining_days -= working_hours_percentage(rolling_date) break if remaining_days < 0 rolling_date = roll_forward rolling_date.next end rolling_date end end |
#add_working_hours(to, number_of_hours) ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/roxbury/business_calendar.rb', line 33 def add_working_hours to, number_of_hours raise ArgumentError, 'number_of_hours must not be negative' if number_of_hours < 0 to = cast_time(to, :start) = roll_forward(to) remaining_hours = number_of_hours loop do bday = business_day() break if bday.include?( + remaining_hours.hours) remaining_hours -= bday.number_of_working_hours(from: ) break if remaining_hours < 0 = next_working_day() end + remaining_hours.hours end |
#holiday?(date_or_time) ⇒ Boolean
136 137 138 |
# File 'lib/roxbury/business_calendar.rb', line 136 def holiday? date_or_time @holidays.include?(date_or_time.to_date) end |
#next_working_day(date) ⇒ Object
If a Date is given, returns then next business day. Otherwise if a Time is given, snaps the date to the beginning of the next business day.
108 109 110 111 112 113 114 115 116 117 |
# File 'lib/roxbury/business_calendar.rb', line 108 def next_working_day date case date when Time roll_forward date.tomorrow.beginning_of_day when Date roll_forward date.tomorrow else raise ArgumentError, 'only Date or Time instances are allowed' end end |
#prev_working_day(date) ⇒ Object
If a Date is given, returns then prev business day. Otherwise if a Time is given, snaps the date to the beginning of the prev business day.
121 122 123 124 125 126 127 128 129 130 |
# File 'lib/roxbury/business_calendar.rb', line 121 def prev_working_day date case date when Time roll_backward date.yesterday.end_of_day when Date roll_backward date.yesterday else raise ArgumentError, 'only Date or Time instances are allowed' end end |
#roll_backward(date) ⇒ Object
Snaps the date to the end of the previous business day, unless it is already within the working hours of a business day.
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/roxbury/business_calendar.rb', line 95 def roll_backward date bday = business_day(date) if bday.include?(date) date elsif bday.ends_before?(date) bday.at_end else roll_backward(date.is_a?(Date) ? date.yesterday : date.yesterday.end_of_day) end end |
#roll_forward(date) ⇒ Object
Snaps the date to the beginning of the next business day, unless it is already within the working hours of a business day.
81 82 83 84 85 86 87 88 89 90 |
# File 'lib/roxbury/business_calendar.rb', line 81 def roll_forward date bday = business_day(date) if bday.include?(date) date elsif bday.starts_after?(date) bday.at_beginning else roll_forward(date.is_a?(Date) ? date.tomorrow : date.tomorrow.beginning_of_day) end end |
#working_days_between(from, to) ⇒ Float
Returns the number of working days between the given dates.
53 54 55 |
# File 'lib/roxbury/business_calendar.rb', line 53 def working_days_between from, to working_hours_between(from, to) / max_working_hours_in_a_day.to_f end |
#working_hours_between(from, to) ⇒ Float
Returns the number of working hours between the given dates.
20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/roxbury/business_calendar.rb', line 20 def working_hours_between from, to from, to, sign = invert_if_needed cast_time(from, :start), cast_time(to, :end) working_hours_per_day = (from.to_date..to.to_date).map do |date| filters = {} filters[:from] = from if date == from.to_date filters[:to] = to if date == to.to_date business_day(date).number_of_working_hours filters end working_hours_per_day.sum.round(2) * sign end |
#working_hours_percentage(date) ⇒ Object
132 133 134 |
# File 'lib/roxbury/business_calendar.rb', line 132 def working_hours_percentage date business_day(date).number_of_working_hours * 1.0 / max_working_hours_in_a_day end |