2009-09-10 31 views
10

Cách tính ngày làm việc tiếp theo và trước đó trong Rails?Cách tính ngày tiếp theo, ngày làm việc trước đó trong Rails?

+0

đang ngày lễ coi là một ngày làm việc? Có phải ngày làm việc chỉ là M-F không? –

+0

chỉ M-F (ngày lễ được coi là ngày làm việc). nhưng nếu bạn có thể cho tôi để lọc ngày lễ sẽ tuyệt vời. –

+0

Bạn đã thử làm điều đó chưa? Bạn nên thêm các lần thử mã trong câu hỏi của mình. – rogeriopvl

Trả lời

15

Theo như tôi hiểu, đây là những gì bạn đang tìm kiếm? (Thử nghiệm nó)

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 

Bạn có thể kiểm tra nó như sau:

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

Nếu ngày của bạn được đề cập là ngày chủ nhật, các câu hỏi trên không hoạt động và nếu bạn thử tăng 7 ngày từ chủ nhật, ngày đó sẽ được lặp lại vô hạn. – DavidNorth

+0

@DavidNorth, nó hoạt động tốt. Ngày làm việc tiếp theo sau Chủ nhật có thể được coi là thứ hai hợp lý. Và có vẻ như bạn chỉ được phép vượt qua 1 hoặc -1 để skip_weekends, mà chỉ là một trợ giúp cho hai phương pháp khác. (Mặc dù nó sẽ tốt hơn nếu nó kiểm tra đối số không hợp lệ thay vì đi vào một vòng lặp vô hạn.) – antinome

+0

Tôi đã vượt qua đối tượng Thời gian làm đối số và bị bỏ lại tự hỏi làm thế nào nó có thể mất hơn 1 giây để có được kết quả. Tôi nghĩ sẽ tốt hơn nếu bạn làm 'date + = inc.day' –

0

Vâng, bạn có thể sử dụng một cái gì đó như ngày hôm qua = 1.days.ago để nhận ngày hôm qua. Sử dụng hôm qua.ngày ('% w') để lấy ngày trong tuần làm số nguyên (0 = Chủ Nhật, 6 = Thứ Bảy). Nếu hôm qua là 0 (Chủ nhật), thì ngày hôm trước của tuần sẽ là 3.days.ago ... bạn có ý tưởng.

Và bạn có thể sử dụng ngày mai = 1.days.since để nhận ngày của ngày mai.

3

Bạn có thể cần phải tính toán ngày làm việc trong tương lai bắt đầu từ fr om một thứ bảy hoặc chủ nhật. 1 ngày làm việc sau Thứ Hai là Thứ Ba, 1 ngày làm việc kể từ Chủ Nhật cũng phải là Thứ Ba - ngày cuối tuần bắt đầu sẽ bị bỏ qua. Sau đây đạt được điều này:

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

Điều này là tốt đẹp. Tôi sẽ đi với điều này. Cảm ơn. – juanpastas

8

Với holidays-gem bạn cũng có thể kiểm tra, nếu có một ngày nghỉ lễ. Nếu bạn làm như vậy, bạn phải xác định khu vực bạn cần. Ngày lễ-đá quý cũng cho phép sử dụng các tiểu vùng (ví dụ: us-va ...)

Mã ví dụ với ngày lễ Đức (de) và Mỹ-Mỹ (chúng tôi).

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 

Lưu ý: skip_weekends_and_holidays không tăng thêm ngày làm việc. Nếu bạn tăng 5 ngày từ Thứ Hai, bạn sẽ kết thúc vào Thứ Hai (trừ thứ Hai này không phải là ngày lễ). Nếu có một kỳ nghỉ trong 5 ngày, có tăng thêm.

Một số mã kiểm tra:

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

Một chiết xuất từ ​​kết quả (ngày trước giáng sinh):

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

Đức có hai ngày miễn phí (25 + 26,12):

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

Cập nhật: Tôi đã tạo một phiên bản khác để xác định số nhân e ngày làm việc:

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 

mã kiểm tra của tôi:

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  

Xem test_2012_12_24_us()date.next_business_days(7,... Bạn kết thúc vào năm 2013, mỗi kỳ nghỉ trong kỳ được tôn trọng.

0

Dưới đây là một phương pháp nhanh hơn mà sử dụng một phép tính đơn giản thay vì lặp lại trong những ngày.

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 

Phương pháp này là trên lớp Thời gian nhưng có thể được sử dụng trên lớp ngày là tốt.

1

Tôi nhận ra rằng đây là một chủ đề cũ, nhưng tôi đã phải làm việc này cho bản thân mình và tôi đang tìm kiếm một đoạn mã rất ngắn để sửa đổi nếu một doanh nghiệp có ngày mở cửa kỳ lạ (như " đóng cửa chủ nhật/thứ hai ").

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 

Tôi cho rằng nó có thể được rút ngắn một lần nữa để một cái gì đó như thế này, nhưng tôi nghĩ rằng nó trở nên ít rõ ràng

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

không cần trả lại dòng cuối cùng – piton4eg

+0

Cảm ơn @ piton4eg, rút ​​ngắn phiên bản ngắn hơn một chút. –

Các vấn đề liên quan