# Calculating Business Hours Left

**URL:** <https://www.ruby-forum.com/t/calculating-business-hours-left/91282>\
**Category:** Ruby\
**Created:** [April 6, 2007, 7:55am UTC](https://www.ruby-forum.com/t/calculating-business-hours-left/91282 "2007-04-06T07:55:27Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![phrogz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/2acd7d/32.png) [@phrogz](https://www.ruby-forum.com/u/phrogz)\
**Post date:** [April 6, 2007, 7:55am UTC](https://www.ruby-forum.com/t/calculating-business-hours-left/91282/1 "2007-04-06T07:55:27Z")

</div>

current\_time = Time.new  
due\_time = # … some Time instance in the future  
work\_hours\_left\_before\_I\_am\_screwed = # … calculate

I’m writing a little task-tracking app to help me put out the most  
urgent fires. As above, given any two Time instances, I’d like to know  
how many hours are left to perform the task. This should skip weekends  
and hours outside of specified start- and end-hours for a work day.

Anyone got such code handy? Or feel like writing it for me? 🙂

Right now, my best idea involves looping over the range one day at a  
time and manually accumulating the available hours. It feels  
inelegant, but it’s all I have right now.

---

<div class="post-metadata">

**Author:** ![phrogz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/2acd7d/32.png) [@phrogz](https://www.ruby-forum.com/u/phrogz)\
**Post date:** [April 6, 2007, 4:14pm UTC](https://www.ruby-forum.com/t/calculating-business-hours-left/91282/2 "2007-04-06T16:14:49Z")

</div>

On 4/5/07, Phrogz [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> current\_time = Time.new  
> due\_time = # … some Time instance in the future  
> work\_hours\_left\_before\_I\_am\_screwed = # … calculate
> 
> I’m writing a little task-tracking app to help me put out the most  
> urgent fires. As above, given any two Time instances, I’d like to know  
> how many hours are left to perform the task. This should skip weekends  
> and hours outside of specified start- and end-hours for a work day.
> 
> Anyone got such code handy? Or feel like writing it for me? 🙂

Hmm… no idea if this works, but it might be a start:

#!/usr/bin/env ruby  
class WorkHours  
SECONDS\_IN\_HOUR = 3600  
HOURS\_I\_WORK = (7…16)  
DAYS\_I\_WORK = (1…5)  
def self.diff\_times(a, b)  
hours = []  
(a.to\_i…b.to\_i).step(SECONDS\_IN\_HOUR) {|t|  
time = Time.at(t)  
hours \<\< t if DAYS\_I\_WORK.include?(time.wday) &&  
HOURS\_I\_WORK.include?(time.hour)  
}  
return hours.length  
end  
end  
nil

now = Time.now  
=\> Fri Apr 06 07:12:08 PDT 2007  
twenty\_days\_from\_now = now + 1728000  
=\> Thu Apr 26 07:12:08 PDT 2007  
one\_day\_from\_now = now + 86400  
=\> Sat Apr 07 07:12:08 PDT 2007  
WorkHours.diff\_times(now, twenty\_days\_from\_now)  
=\> 141  
WorkHours.diff\_times(now, one\_day\_from\_now)  
=\> 10

HTH,  
Keith

---

<div class="post-metadata">

**Author:** ![phrogz](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/p/2acd7d/32.png) [@phrogz](https://www.ruby-forum.com/u/phrogz)\
**Post date:** [April 6, 2007, 4:25pm UTC](https://www.ruby-forum.com/t/calculating-business-hours-left/91282/3 "2007-04-06T16:25:52Z")

</div>

On Apr 6, 8:13 am, “Keith F.” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Hmm… no idea if this works, but it might be a start:

That feels like it might be good, thanks! I’ll have to investigate  
more. It certainly looks cleaner than what I came up with:

class Time  
def at( hrs, mins=0 )  
self.class.parse strftime(“%Y-%b-%d #{hrs}:#{mins}” )  
end  
end

# Fails for tasks one year or farther in the future

def work\_hours\_until( due\_time, work\_starts\_at=9, work\_ends\_at=17 )  
start\_time = [  
Time.now,  
Time.parse( “#{work\_starts\_at}:00” )  
].max  
end\_time = [  
due\_time,  
due\_time.at( work\_ends\_at )  
].min

weekdays = 1…5  
one\_hour = 3600.0  
one\_day = one\_hour \* 24

if start\_time.yday == end\_time.yday  
if weekdays.include?( start\_time.wday )  
( end\_time - start\_time ) / one\_hour  
else  
0  
end  
else  
business\_hours = work\_ends\_at - work\_starts\_at

```
# Handle partial hours on the first day
hours = ( start_time.at( work_ends_at ) - start_time ) / one_hour

t = start_time + one_day
until t.yday == due_time.yday
  hours += business_hours if weekdays.include?( t.wday )
  t += one_day
end

# Handle partial hours on the last day
if weekdays.include?( t.wday )
  hours += ( end_time - end_time.at( work_starts_at ) ) / one_hour
end

hours

```

end

end
