Подтвердить что ты не робот

Подсчет дней без выходных

Я создаю библиотечную систему в 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 
4b9b3361

Ответ 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)