Подсчет дней без выходных
Я создаю библиотечную систему в Ruby on Rails, и я пытаюсь найти способ рассчитать просроченные дни, исключая выходные, когда возвращается заимствованный элемент. Прямо сейчас я просто вычисляю "dayslate" как разницу между установленной датой и датой, когда элемент был фактически возвращен, но я хочу исключить выходные, так как элементы могут быть возвращены только в будние дни.
Это мой первый реальный опыт работы с Ruby и Rails, поэтому я извиняюсь, если мне не хватает чего-то очевидного. Спасибо за любую помощь, которую вы можете предоставить.
Здесь код, который у меня есть для функции "return":
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 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
Ответы
Ответ 1
Здесь приведен фрагмент кода для поиска числа будних дней в объектах Range of Date
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
Вот как я буду использовать его в вашем случае.
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
Ответ 2
Если для вас важны праздники (т.е. вы не хотите считать день в будние дни, если это праздник), вы можете посмотреть в http://rubyforge.org/projects/holidays/. Если вы комбинируете цикл, который @md5sum упоминается с чек, чтобы увидеть, является ли будний день праздником, вы можете быть золотым.
Ответ 3
Ум... Еще одна вещь, которая может быть полезна, поскольку это ваши первые рельсы - обратите внимание на эту бизнес-логику в контроллере. Ваша модель, где она принадлежит.
Ответ 4
sunday, saturday = 0, 6
weekend = [sunday, saturday]
((start_date..end_date).collect(&:wday) - weekend).count
Ответ 5
Вы можете проверить эту страницу и посмотреть, можно ли добавить счетчик в цикл и проверить текущую дату на каждой итерации.
Ответ 6
Что касается сообщения @Marini, можно устранить накладные расходы на создание диапазона дат:
def weekdays(date1, date2)
wday = date1.wday
(0..(date2-date1)).count {|offset| ((wday + offset) % 7 % 6) > 0}
end
% 7
превращает каждое смещение в будний день, а % 6
возвращает 0
в субботу (6) или воскресенье (0)
Ответ 7
Я думаю, что это проще и читабельнее, чем принятый ответ:
require 'date'
to = Date.parse('2017-02-02')
from = to - 14
(from..to).count { |d| !d.sunday? && !d.saturday? }
#=> 11
Ответ 8
neutrino совершенно прав о перемещении всей логики, которая не участвует в отображении/рендеринге/перенаправлении на модели.
например.
class ProductLoan
def due_date_passed?
days_late > 0
end
def days_late
Date.today - due_date
end
def complete!
self.borrower = self.check_out = self.due_date = self.extended_checkout = nil
save
end
end
class ReturnsController
def create
@product = ProductLoan.find(params[:id])
if @product.due_date_passed?
flash[:notice] = "Okay, it checked in!"
else
flash[:notice] = "Checked in, but it was #{@product.days_late} days late!"
end
@product.complete!
redirect_to products_url
end
end
Я просто набрал это без проверки, но что-то по этим строкам могло подсчитать прошедшие дни, исключая выходные:
class DateOffsetCalculator
DAYS_IN_A_WEEK = 7
DAYS_IN_A_WEEKEND = 2
def initialize(date)
@date = date
end
def compute
days_passed - weekend_days_passed
end
def days_passed
(Date.today - @date).to_i
end
def weekend_days_passed
weekends_passed * DAYS_IN_A_WEEKEND
end
def weekends_passed
rounded_up_week_days / DAYS_IN_A_WEEK
end
def rounded_up_week_days
days_passed + commercial_working_day_correction
end
def commercial_working_day_correction
@date.cwday - 1
end
end
Ответ 9
рассчитать недельные дни текущего_месяца
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
Ответ 10
Самое лучшее решение прекрасно, и я знаю, что я не должен беспокоиться о производительности, но есть часть меня, которая беспокоилась о подсчете числа будних дней, создавая коллекцию и итерируя ее, потому что время для вычисления - это линейная функция от разницы между двумя датами.
Ответ может быть математически рассчитан в фиксированное время, если вы это продумаете. Вы можете подумать об этом в двух частях:
ЧАСТЬ 1 - КОЛИЧЕСТВО НЕДЕЛИ МЕЖДУ
Если разница в днях больше недели, вы можете подсчитать каждую 7-дневную неделю как имеющую 5 рабочих дней и "удалить" полные недели с рассмотрения.
ЧАСТЬ 2 - ДНИ В ЧАСТНОЙ НЕДЕЛЕ
Вычитая полные недели, вы можете рассмотреть упрощенную проблему того, сколько будних дней лежит между двумя днями в течение 7 дней. Если вы построите таблицу, вы увидите там довольно простое правило или можете повторить логику, используемую в выигрышном решении, но только через интервал не более 7 дней.
Ответ 11
Я нашел более быстрый способ подсчета дней выходных дней, вы можете получить количество дней, исключая выходные дни, на (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
Сравните с [0, 6].include? date.wday
, см. мой результат теста:
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)