2009-09-10 2 views
10

Как рассчитать следующий и предыдущие рабочие дни в Rails?Как рассчитать следующий, предыдущий рабочий день в Rails?

+0

праздники считается рабочий день? Являются ли рабочие дни только M-F? –

+0

Только M-F (праздники считаются рабочими днями). , но если вы можете дать мне отфильтровать праздники, которые будут потрясающими. –

+0

Вы пытались это сделать? Вы должны добавить свои попытки кода в свой вопрос. – rogeriopvl

ответ

15

Насколько я понимаю, это то, что вы ищете? (Тестировал)

require 'date' 
def next_business_day(date) 
    skip_weekends(date, 1) 
end  

def previous_business_day(date) 
    skip_weekends(date, -1) 
end 

def skip_weekends(date, inc) 
    date += inc 
    while (date.wday % 7 == 0) or (date.wday % 7 == 6) do 
    date += inc 
    end 
    date 
end 

Вы можете проверить это следующим образом:

begin 
    t = Date.new(2009,9,11) #Friday, today 
    puts "Today: #{Date::DAYNAMES[t.wday]} #{Date::MONTHNAMES[t.mon]} #{t.day}" 
    nextday = next_business_day(t) 
    puts "Next B-day: #{Date::MONTHNAMES[nextday.mon]} #{nextday.day}" 
    previousday = previous_business_day(nextday) 
    puts "back to previous: #{Date::MONTHNAMES[previousday.mon]} #{previousday.day}" 
    yesterday = previous_business_day(previousday) 
    puts "yesterday: #{Date::MONTHNAMES[yesterday.mon]} #{yesterday.day}" 
end 
+3

Если ваша дата в воскресенье, указанное выше не работает, и если вы попытаетесь увеличить 7 дней с воскресенья, она будет придерживаться бесконечного цикла. – DavidNorth

+0

@DavidNorth, он отлично работает. На следующий рабочий день после воскресенья можно разумно считать понедельник. И кажется, что вы должны только передать 1 или -1 в skip_weekends, что является лишь помощником для двух других методов. (Хотя было бы лучше, если бы он проверял недействительные аргументы вместо того, чтобы переходить в бесконечный цикл.) – antinome

+0

Я передал объект Time в качестве аргумента, и мне было интересно, как это может занять более 1 секунды, чтобы получить результат. Я думаю, что было бы лучше быть явным, делая «date + = inc.day» –

0

Ну, вы можете использовать что-то наподобие вчера = 1.days.ago, чтобы получить вчерашнюю дату. Используйте вчера. Strftime ('% w'), чтобы получить день недели как целое число (0 = воскресенье, 6 = суббота). Если вчера 0 (воскресенье), то в предыдущий день недели будет 3.days.ago ... вы получите эту идею.

И вы можете использовать завтра = 1.days.since, чтобы получить дату завтрашнего дня.

1

Вот два примера для числа рабочих дней между двумя датами:

  1. http://codesnippets.joyent.com/posts/show/1209
  2. http://www.ruby-forum.com/topic/93938#190402

Вот Рубин праздники перл:

3

Вам может понадобиться для расчета рабочих дней в будущем начиная с fr om в субботу или воскресенье. 1 рабочий день после понедельника вторник, 1 рабочий день с воскресенья также должен быть во вторник - начало выходного дня должно быть проигнорировано. Ниже это достигается:

class Date 

    def business_days_future(inc) 
    date = skip_weekend 
    inc.times do 
     date = date + 1 
     date = date.skip_weekend 
    end 
    date 
    end 

    # If date is a saturday or sunday, advance to the following monday 
    def skip_weekend 
    if wday == 0 
     self + 1 
    elsif wday == 6 
     self + 2 
    else 
     self 
    end 
    end 

end 
+0

Это хорошо. Я пойду с этим. Благодарю. – juanpastas

8

С holidays-gem вы также можете проверить, если есть государственный праздник. Если вы это сделаете, вы должны определить регион, в котором вы нуждаетесь. Праздник-драгоценный камень позволяет также использовать субрегионы (например, us-va ...)

Пример кода с немецкими (де) и американо-американскими (нами) праздниками.

require 'holidays' 
require 'holidays/us' 
require 'holidays/de' 
class Date 
    def next_business_day(region=:any) 
    skip_weekends_and_holidays(1,region) 
    end  

    def previous_business_day(region=:any) 
    skip_weekends_and_holidays(-1,region) 
    end 

    def skip_weekends_and_holidays(inc, region = :any) 
    date = self + inc 
    while (date.wday == 6 or date.holiday?(region)) do 
     date += inc 
    end 
    date 
    end 
end 

Привлеките внимание: skip_weekends_and_holidays не увеличивает рабочие дни. Если вы увеличиваете 5 дней с понедельника, вы заканчиваете понедельник (если этот понедельник не будет праздником). Если в течение 5 дней был праздник, есть дополнительный прирост.

Некоторые тест-код:

[ 
    Date.new(2012,6,8), #Friday 
    Date.new(2012,6,10), #Monday 
    Date.new(2012,6,9), #Sunday 
    Date.new(2012,12,24), #Christmas eve 
    Date.new(2012,12,26), #After Christmas 
].each{|t| 
    %w{us de}.each{|region| 
    puts "====#{region}======" 
    puts "Today: #{Date::DAYNAMES[t.wday]} #{Date::MONTHNAMES[t.mon]} #{t.day}" 
    nextday = t.next_business_day(region) 
    puts "Next B-day: #{Date::MONTHNAMES[nextday.mon]} #{nextday.day} - #{Date::DAYNAMES[nextday.wday]}" 
    previousday = t.previous_business_day(region) 
    puts "Previous B-day: #{Date::MONTHNAMES[previousday.mon]} #{previousday.day} - #{Date::DAYNAMES[previousday.wday]}" 
    } 

Выписка из результата (Сочельник):

====us====== 
Today: Monday December 24 
Next B-day: December 26 - Wednesday 
Previous B-day: December 23 - Sunday 

Германия имеет два свободных дня (25 + 26,12):

====de====== 
Today: Monday December 24 
Next B-day: December 27 - Thursday 
Previous B-day: December 23 - Sunday 

Обновление: я сделал еще одну версию, чтобы определить множитель е рабочих дней:

require 'holidays' 
require 'holidays/us' 
#~ require 'holidays/de' 
class Date 
    def next_business_day(region=:any) 
    next_business_days(1,region) 
    end  

    def next_business_days(inc, region=:any) 
    date = self 
    inc.times{ 
     date = date.next 
     while (date.wday == 6 or date.holiday?(region)) do 
     date = date.next 
     end 
    } 
    date 
    end  

    def previous_business_day(region=:any) 
    previous_business_days(1,region) 
    end 

    def previous_business_days(inc, region=:any) 
    date = self 
    inc.times{ 
     date = date.prev_day 
     while (date.wday == 6 or date.holiday?(region)) do 
     date = date.prev_day 
     end 
    } 
    date 
    end  


end 

Мой тестовый код:

require 'test/unit' 
class BDay_Test < Test::Unit::TestCase 
    def test_2012_06_08_us() 
    date = Date.new(2012, 6, 8) 
    assert_equal(Date.new(2012, 06, 10), date.next_business_day('us')) 
    assert_equal(Date.new(2012, 06, 7), date.previous_business_day('us')) 

    assert_equal(Date.new(2012, 06, 17), date.next_business_days(7, 'us')) 
    assert_equal(Date.new(2012, 05, 31), date.previous_business_day(7, 'us')) 
    end 
    def test_2012_06_08_de() 
    date = Date.new(2012, 6, 8) 
    assert_equal(Date.new(2012, 06, 10), date.next_business_day('de')) 
    assert_equal(Date.new(2012, 06, 7), date.previous_business_day('de')) 

    assert_equal(Date.new(2012, 06, 17), date.next_business_days(7, 'de')) 
    assert_equal(Date.new(2012, 05, 31), date.previous_business_day(7, 'de')) 
    end 
    def test_2012_06_10_us() 
    date = Date.new(2012, 6, 10) 
    assert_equal(Date.new(2012, 06, 11), date.next_business_day('us')) 
    assert_equal(Date.new(2012, 06, 8), date.previous_business_day('us')) 

    assert_equal(Date.new(2012, 06, 18), date.next_business_days(7, 'us')) 
    assert_equal(Date.new(2012, 06, 1), date.previous_business_day(7, 'us')) 
    end 
    def test_2012_06_10_de() 
    date = Date.new(2012, 6, 10) 
    assert_equal(Date.new(2012, 06, 11), date.next_business_day('de')) 
    assert_equal(Date.new(2012, 06, 8), date.previous_business_day('de')) 

    assert_equal(Date.new(2012, 06, 18), date.next_business_days(7, 'de')) 
    assert_equal(Date.new(2012, 06, 1), date.previous_business_day(7, 'de')) 
    end 
    def test_2012_06_09_us() 
    date = Date.new(2012, 6, 9) 
    assert_equal(Date.new(2012, 06, 10), date.next_business_day('us')) 
    assert_equal(Date.new(2012, 06, 8), date.previous_business_day('us')) 

    assert_equal(Date.new(2012, 06, 17), date.next_business_days(7, 'us')) 
    assert_equal(Date.new(2012, 06, 1), date.previous_business_day(7, 'us')) 
    end 
    def test_2012_06_09_de() 
    date = Date.new(2012, 6, 9) 
    assert_equal(Date.new(2012, 06, 10), date.next_business_day('de')) 
    assert_equal(Date.new(2012, 06, 8), date.previous_business_day('de')) 

    assert_equal(Date.new(2012, 06, 17), date.next_business_days(7, 'de')) 
    assert_equal(Date.new(2012, 06, 1), date.previous_business_day(7, 'de')) 
    end 
    def test_2012_12_24_us() 
    date = Date.new(2012, 12, 24) 
    assert_equal(Date.new(2012, 12, 26), date.next_business_day('us')) 
    assert_equal(Date.new(2012, 12, 23), date.previous_business_day('us')) 

    assert_equal(Date.new(2013, 01, 3), date.next_business_days(7, 'us')) 
    assert_equal(Date.new(2012, 12, 16), date.previous_business_day(7, 'us')) 
    end 
    def test_2012_12_24_de() 
    date = Date.new(2012, 12, 24) 
    assert_equal(Date.new(2012, 12, 27), date.next_business_day('de')) 
    assert_equal(Date.new(2012, 12, 23), date.previous_business_day('de')) 

    assert_equal(Date.new(2013, 01, 4), date.next_business_days(7, 'de')) 
    assert_equal(Date.new(2012, 12, 16), date.previous_business_day(7, 'de')) 
    end 
    def test_2012_12_26_us() 
    date = Date.new(2012, 12, 26) 
    assert_equal(Date.new(2012, 12, 27), date.next_business_day('us')) 
    assert_equal(Date.new(2012, 12, 24), date.previous_business_day('us')) 

    assert_equal(Date.new(2013, 01, 4), date.next_business_days(7, 'us')) 
    assert_equal(Date.new(2012, 12, 17), date.previous_business_day(7, 'us')) 
    end 
    def test_2012_12_26_de() 
    date = Date.new(2012, 12, 26) 
    assert_equal(Date.new(2012, 12, 27), date.next_business_day('de')) 
    assert_equal(Date.new(2012, 12, 24), date.previous_business_day('de')) 

    assert_equal(Date.new(2013, 01, 4), date.next_business_days(7, 'de')) 
    assert_equal(Date.new(2012, 12, 17), date.previous_business_day(7, 'de')) 
    end 

end  

test_2012_12_24_us() Смотрите и date.next_business_days(7,... Вы заканчиваете в 2013 году, каждый праздник в период соблюдается.

2

может быть, этот драгоценный камень может быть полезен для вас вопрос

https://github.com/bokmann/business_time

Это позволит вам рассчитать часы и дни бизнеса с даты, указанной

0

Вот быстрый метод, который использует простой расчет вместо итерации в течение дней.

class Time 

    def shift_weekdays(num_weekdays) 
    base = self 

    # corner case: self falls on a Sat or Sun then treat like its the next Monday 
    case self.wday 
     when 0 
     base = self + 1.day 
     when 6 
     base = self + 2.day 
    end 
    day_of_week = base.wday - 1 # Monday is 0 

    weekends = (day_of_week + num_weekdays)/5 

    base + (weekends*2).days + num_weekdays.days 
    end 

end 

Метод находится в классе Time, но может использоваться и для класса Date.

1

Я понимаю, что это старая тема, но мне просто нужно было это сделать для себя, и я искал очень короткую часть кода, которая была тривиальной для изменения, если у бизнеса были странные дни открытия (например, закрыто воскресенье/понедельник ").

def next_business_day(from_day) 
    workdays = [1,2,3,4,5,6] 
    test_day = from_day + 1.day 
    return workdays.include?(test_day.wday) ? test_day : next_business_day(test_day) 
end 

Я предполагаю, что это может быть снова укорачивается к чему-то вроде этого, но я думаю, что это становится менее заметным

def next_business_day(from_day) 
    test_day = from_day + 1.day 
    [1,2,3,4,5,6].include?(test_day.wday) ? test_day : next_business_day(test_day) 
end 
+0

не нужно возвращать в последней строке – piton4eg

+0

Спасибо @ piton4eg, укоротил короткую версию немного больше. –

Смежные вопросы