Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Counting days excluding weekends

I'm creating a library-style system in Ruby on Rails, and I'm trying to come up with a way to calculate the overdue days while excluding weekends when a borrowed item is returned. Right now I'm just calculating "dayslate" as the difference between the due date and the date the item was actually returned, but I want to exclude weekends, since items can only be returned on weekdays.

This is my first real experience with Ruby and Rails, so my apologies if I'm missing something obvious. Thanks for any help you all can provide.

Here's the code I have for the "return" function:

   def return
     @product = Product.find(params[:id])
     today = Date.today
     dayslate = today - @product.due_date
     if @product.due_date >= today
       @product.borrower = @product.check_out = @product.due_date = @product.extended_checkout = nil
       @product.save!
       flash[:notice] = "Okay, it's checked in!"
       redirect_to(products_url)
     else
       @product.borrower = @product.check_out = @product.due_date = @product.extended_checkout = nil
       @product.save!
       flash[:notice] = "Checked in, but it was #{dayslate} days late!"
       redirect_to(products_url)
     end
 end 
like image 427
Eric K Avatar asked Jan 04 '10 19:01

Eric K


People also ask

How do I calculate the number of days excluding weekends in Excel?

The Excel NETWORKDAYS function calculates the number of working days between two dates. NETWORKDAYS automatically excludes weekends (Saturday and Sunday) and can optionally exclude a list of holidays supplied as dates.

How do I calculate the number of days excluded from a Sunday in Excel?

Here I can introduce a formula for counting days excluding Sundays during a date range. Select a blank cell, here is C2, and type this formula =B2-A2-INT((B2-A2-WEEKDAY(B2)+1)/7) into it, and then press Enter key, a date displayed.


9 Answers

Here's a snippet of code to find the number of weekdays in a Range of Date objects

require 'date'
# Calculate the number of weekdays since 14 days ago
p ( (Date.today - 14)..(Date.today) ).select {|d| (1..5).include?(d.wday) }.size

This is how I would use it in your case.

class Product
  def days_late
    weekdays_in_date_range( self.due_date..(Date.today) )
  end

  protected
  def weekdays_in_date_range(range)
    # You could modify the select block to also check for holidays
    range.select { |d| (1..5).include?(d.wday) }.size
  end
end
like image 103
Ben Marini Avatar answered Oct 04 '22 08:10

Ben Marini


sunday, saturday = 0, 6
weekend = [sunday, saturday]
((start_date..end_date).collect(&:wday) - weekend).count
like image 20
11 revs, 10 users 40% Avatar answered Oct 04 '22 06:10

11 revs, 10 users 40%


If holidays matter to you (i.e. you don't want to count a day as a weekday if it is a holiday), you might want to look into http://rubyforge.org/projects/holidays/. If you combine the loop that @md5sum mentioned with a check to see if the weekday is a holiday, you may be golden.

like image 29
pkaeding Avatar answered Oct 04 '22 07:10

pkaeding


Um.. just one more thing which could be useful since it's your first rails experience - pay attention to that business logic in the controller. Your model, that's where it belongs.

like image 34
alex.zherdev Avatar answered Oct 04 '22 06:10

alex.zherdev


You might check out this page and see if you can add a counter into the loop and check against the current date through each iteration.

like image 29
Nathan Wheeler Avatar answered Oct 04 '22 06:10

Nathan Wheeler


Regarding @Marini's post it is possible to eliminate the overhead of generating a range of dates:

def weekdays(date1, date2)
  wday = date1.wday
  (0..(date2-date1)).count {|offset| ((wday + offset) % 7 % 6) > 0}
end

% 7 turns each offset into a weekday, and % 6 returns 0 for Saturday (6) or Sunday (0)

like image 31
noel Avatar answered Oct 04 '22 07:10

noel


calculate week days of the current_month

start_date = Date.today.beginning_of_month
end_date = Date.today.end_of_month

(start_date..end_date).select{|a| a.wday < 6 && a.wday > 0}.count
like image 22
spr Avatar answered Oct 04 '22 08:10

spr


I think this is simpler and more readable than the accepted answer:

require 'date'
to = Date.parse('2017-02-02')
from = to - 14
(from..to).count { |d| !d.sunday? && !d.saturday? }
#=> 11
like image 20
Junichi Ito Avatar answered Oct 04 '22 08:10

Junichi Ito


I found a faster way to counting weekend days, you can get days count excluding weekends by (start_date..end_date).size - weekend_count:

def weekend_count(start_date, end_date)
  size = (end_date - start_date).to_i
  count = 0
  if start_date.wday != 0
    size -= (7 - start_date.wday).to_i
    count += 1
  end

  left_over  = size % 7
  if left_over == 0
    count = (count / 7) * 2
  else
    size -=  left_over
    count += (size / 7) * 2 + 1
  end
  count
end

Compare with [0, 6].include? date.wday, see my benchmark result:

Distance 10
Optimized result:  4
Normal result:     4
                                                  user     system      total        real
Optimized                                     0.000000   0.000000   0.000000 (  0.000018)
Normal                                        0.000000   0.000000   0.000000 (  0.000032)
Distance 100
Optimized result:  29
Normal result:     29
                                                  user     system      total        real
Optimized                                     0.000000   0.000000   0.000000 (  0.000006)
Normal                                        0.000000   0.000000   0.000000 (  0.000094)
Distance 1000
Optimized result:  286
Normal result:     286
                                                  user     system      total        real
Optimized                                     0.000000   0.000000   0.000000 (  0.000010)
Normal                                        0.000000   0.000000   0.000000 (  0.000650)
Distance 10000
Optimized result:  2858
Normal result:     2858
                                                  user     system      total        real
Optimized                                     0.000000   0.000000   0.000000 (  0.000013)
Normal                                        0.000000   0.000000   0.000000 (  0.004995)
Distance 100000
Optimized result:  28572
Normal result:     28572
                                                  user     system      total        real
Optimized                                     0.000000   0.000000   0.000000 (  0.000011)
Normal                                        0.060000   0.000000   0.060000 (  0.064223)
like image 22
fangxing Avatar answered Oct 04 '22 08:10

fangxing