Как рассчитать следующий, предыдущий рабочий день в Rails? - PullRequest
10 голосов
/ 10 сентября 2009

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

Ответы [ 11 ]

16 голосов
/ 11 сентября 2009

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

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 = 1)
  date += inc
  while date.wday == 0 || date.wday == 6
    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  
10 голосов
/ 11 июня 2012

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

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

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

Обратите внимание: 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/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

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

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 году, каждый праздник в период уважается.

5 голосов
/ 24 марта 2018
date = Date.today
date.next_weekday
date.prev_weekday
3 голосов
/ 12 сентября 2012

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

https://github.com/bokmann/business_time

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

3 голосов
/ 11 июня 2012

Возможно, вам потребуется рассчитать рабочие дни в будущем, начиная с субботы или воскресенья. 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
1 голос
/ 29 мая 2015

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

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
1 голос
/ 12 октября 2010

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

  1. http://codesnippets.joyent.com/posts/show/1209
  2. http://www.ruby -forum.com / тема / 93938 # 190402

Вот жемчужина Ruby для праздников:

0 голосов
/ 09 марта 2019

Это метод, который я использую в своем сценарии планирования производства:

require 'date'
def shift_business_days(date, incr)
  date = Date.parse(date.to_s)
  incr.abs.times do
    date += (incr < 0 ? -1 : 1)
    while date.saturday? || date.sunday? do
      date += (incr < 0 ? -1 : 1)
    end
  end
  date
end

Принимает дату и положительное или отрицательное целое число incr в качестве аргументов и увеличивает или уменьшает дату на incr дней, пропуская выходные. Это дает дополнительное преимущество - возможность обрабатывать объект Date / Time или любую строку даты, которую может обработать Date.parse. Например:

# (today is 2019-03-08)
shift_business_days(Time.now, 2)
##=> #<Date: 2019-03-12 ((2458555j,0s,0n),+0s,2299161j)>

shift_business_days('5/20', -10)
##=> #<Date: 2019-05-06 ((2458610j,0s,0n),+0s,2299161j)>
0 голосов
/ 30 апреля 2018

это реализация:

require 'business_time'

date = Time.now

next_workday(date)

private

def next_workday(date:)
  return date = date.next_weekday while date.workday?
end
0 голосов
/ 23 апреля 2014

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

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...