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
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.
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.
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
sunday, saturday = 0, 6
weekend = [sunday, saturday]
((start_date..end_date).collect(&:wday) - weekend).count
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.
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.
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.
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)
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
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
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With