Calculating Business Hours Left

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? :slight_smile:

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.

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

···

On 4/5/07, Phrogz <gavin@refinery.com> 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? :slight_smile:

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

···

On Apr 6, 8:13 am, "Keith Fahlgren" <k...@audiobeta.com> wrote:

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