How to calculate next, previous business day in Rails?

前端 未结 12 2355
粉色の甜心
粉色の甜心 2021-02-08 08:43

How to calculate next and previous business days in Rails?

12条回答
  •  囚心锁ツ
    2021-02-08 09:32

    With the holidays-gem you can also check, if there is a public holiday. If you do so, you must define the region you need. The holidays-gem allows also to use subregions (e.g. us-va...)

    An example code with German (de) and US-american (us) holidays.

    require 'holidays'
    require 'holidays/us'
    require 'holidays/de'
    require 'holidays/core_extensions/date'
    class Date
      include Holidays::CoreExtensions::Date #provide Date#holiday?
    
      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
    

    Get attention: skip_weekends_and_holidays does not increment business days. If you increment 5 days from a Monday, you end on a Monday (unless this Monday is no holiday). If there was a holiday during the 5 days, there is additional increment.

    Some test code:

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

    An extract from result (christmas eve):

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

    Germany has two free days (25+26.12):

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

    Update: I made another version to determine multiple business days:

    require 'holidays'
    require 'holidays/us'
    require 'holidays/core_extensions/date'
    #~ require 'holidays/de'
    class Date
      include Holidays::CoreExtensions::Date #provide Date#holiday?
      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
    

    My test code:

    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    
    

    See test_2012_12_24_us() and date.next_business_days(7,... You end in 2013, each holiday in the period is respected.

提交回复
热议问题