2009-09-10 61 views
10

如何計算Rails中的下一個和上一個工作日?如何計算Rails中的下一個工作日?

+0

被認爲是一個工作日的假期不太明顯?工作日只是M-F? – 2009-09-10 17:31:13

+0

僅限M-F(節假日視爲營業日)。 但如果你可以給我過濾假期,這將是非常棒的。 – 2009-09-10 17:36:00

+0

你試過嗎?您應該在您的問題中添加您的代碼嘗試。 – rogeriopvl 2009-09-10 17:39:47

回答

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 2012-06-11 11:31:46

+0

@DavidNorth,它工作正常。週日後的下一個工作日可合理地視爲週一。看起來你只應該傳遞1或-1給skip_weekends,這只是其他兩種方法的幫手。 (雖然它會更好,如果它檢查無效的參數,而不是進入無限循環。) – antinome 2014-12-24 21:56:57

+0

我傳遞了Time對象作爲參數,並想知道如何花費超過1秒才能得到結果。我認爲最好是通過'date + = inc.day'來明確表示' – 2017-01-09 12:50:16

0

那麼,你可以使用類似於昨天= 1.days.ago得到昨天的日期。使用yesterday.strftime('%w')以星期幾作爲整數(0 =星期日,6 =星期六)。如果昨天是0(星期天),那麼本週的前一天將是3.days.ago ...你明白了。

您可以使用明天= 1.days.since獲取明天的日期。

3

您可能需要在未來的計算工作日開始fr呃週六或週日。 星期一之後的一個工作日是星期二,星期日的一個工作日也應該是星期二 - 應該忽略開始的週末日。 下實現這一點:

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 2013-05-28 22:16:13

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天內有假期,還有額外的增量。

一些測試代碼:

====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 

[ 
    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]}" 
    } 

從結果(平安夜)的提取物

更新:我做了另一個版本來確定multipl商天娥:

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年結束,在此期間每個假期尊重。

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 2015-11-06 12:06:26

+0

謝謝@ piton4eg,縮短了短版本多一點。 – 2015-11-16 14:08:18