2009-09-10 39 views
10

¿Cómo calcular días hábiles siguientes y anteriores en Rails?¿Cómo calcular el siguiente día hábil anterior en Rails?

+0

son días de fiesta considerado un día hábil? ¿Los días hábiles son solo M-F? –

+0

M-F solamente (días festivos considerados días hábiles). pero si me puede dar para filtrar las vacaciones que serán increíbles. –

+0

¿Has intentado hacerlo? Debe agregar sus intentos de código en su pregunta. – rogeriopvl

Respuesta

15

Por lo que yo entiendo, esto es lo que estás buscando? (Probado)

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 

puede comprobarlo de la siguiente manera:

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

Si su fecha en cuestión es un domingo, lo anterior no funciona, y si intenta aumentar 7 días desde el domingo, se mantendrá en un ciclo infinito. – DavidNorth

+0

@DavidNorth, funciona bien. El siguiente día hábil después del domingo se puede considerar razonablemente lunes. Y parece que solo debes pasar 1 o -1 para skip_weekends, que es solo una ayuda para los otros dos métodos. (Aunque sería mejor si buscara argumentos inválidos en lugar de ir a un bucle infinito.) – antinome

+0

Pasé el objeto Time como argumento y me quedé pensando cómo podría llevar más de 1 segundo obtener el resultado. Creo que sería mejor ser explícito haciendo 'date + = inc.day' –

0

Bueno, puedes usar algo como ayer = 1.days.ago para obtener la fecha de ayer. Use yesterday.strftime ('% w') para obtener el día de la semana como un número entero (0 = domingo, 6 = sábado). Si ayer es 0 (domingo), entonces el día anterior de la semana sería 3.days.ago ... ya entendiste la idea.

Y puede usar tomorrow = 1.days.since para obtener la fecha de mañana.

3

Es posible que necesite para calcular los días laborales en el futuro a partir de fr om un sábado o domingo. 1 día hábil después de que un lunes sea el martes, 1 día hábil a partir de un domingo también debería ser el martes: el día inicial del fin de semana debe ignorarse. El siguiente logra esto:

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

Este es bueno. Iré con esto. Gracias. – juanpastas

8

Con la holidays-gem también se puede comprobar, si hay un día festivo. Si lo hace, debe definir la región que necesita. La joya de vacaciones también permite el uso de subregiones (por ejemplo, us-va ...)

Un código de ejemplo con vacaciones en Alemania (de) y estadounidense (americano).

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 

Obtenga atención: skip_weekends_and_holidays no incrementa días hábiles. Si aumenta 5 días desde un lunes, termina un lunes (a menos que este lunes no sea feriado). Si hubo vacaciones durante los 5 días, hay un incremento adicional.

Algunos código de prueba:

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

Un extracto de resultado (Nochebuena):

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

Alemania tiene dos días libres (25 + 26,12):

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

Actualización: Hice otra versión para determinar multipl e días hábiles:

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 

Mi código de prueba:

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  

Ver test_2012_12_24_us() y date.next_business_days(7,... Se termina en 2013, se respeta cada día de fiesta en el período.

0

Aquí es un método más rápido que utiliza un cálculo simple en vez de iterar durante los días.

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 

El método está en el tiempo de la clase pero también se puede usar en la clase Date.

1

Me doy cuenta de que este es un hilo antiguo, pero tenía que trabajar con esto por mí mismo y estaba buscando un código muy breve que fuera trivial modificar si una empresa tenía días de apertura raros (como " cerrado domingo/lunes ").

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 

supongo que podría ser acortado de nuevo para algo como esto, pero creo que resulta menos obvia

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

no necesita devolución en la última línea – piton4eg

+0

Gracias @ piton4eg, acorta la versión corta un poco más. –

Cuestiones relacionadas