ti-enxame.com

Conte o número de dias entre as datas, ignorando os finais de semana

Como posso calcular o número de dias entre duas datas ignorando os finais de semana?

42
Mohammad Nadeem

Eu acho que a solução mais limpa é usar a função numpy busday_count

import numpy as np
import datetime as dt

start = dt.date( 2014, 1, 1 )
end = dt.date( 2014, 1, 16 )

days = np.busday_count( start, end )
57
Ben
>>> from datetime import date,timedelta
>>> fromdate = date(2010,1,1)
>>> todate = date(2010,3,31)
>>> daygenerator = (fromdate + timedelta(x + 1) for x in xrange((todate - fromdate).days))
>>> sum(1 for day in daygenerator if day.weekday() < 5)
63

Este cria um gerador usando uma expressão de gerador que produzirá a lista de dias para ir de fromdate para todate

Poderíamos então criar uma lista a partir do gerador, filtrando os finais de semana usando a função weekday() , e o tamanho da lista fornece o número de dias que desejamos. No entanto, para salvar a lista inteira na memória, o que pode ser um problema se as datas forem muito distantes, usamos outra expressão geradora que filtra os finais de semana, mas retorna 1 em vez de cada data. Podemos então adicionar todos esses 1s juntos para obter o comprimento sem ter que armazenar a lista inteira.

Note, se fromdate == todate isto calcular 0 não 1.

49
Dave Webb

As respostas dadas até agora funcionarão, mas serão altamente ineficientes se as datas estiverem distantes uma da outra (devido ao loop).

Isso deve funcionar:

import datetime

start = datetime.date(2010,1,1)
end = datetime.date(2010,3,31)

daydiff = end.weekday() - start.weekday()

days = ((end-start).days - daydiff) / 7 * 5 + min(daydiff,5) - (max(end.weekday() - 4, 0) % 5)

Isso o transforma em semanas inteiras (que têm 5 dias úteis) e depois lida com os dias restantes.

10
neil

A maneira preguiçosa é pip install workdays para obter o pacote python que faz exatamente isso.

https://pypi.python.org/pypi/workdays/

7
Matthew Scouten

Primeiro importe numpy como np. A função np.busday_count conta o número de dias válidos entre duas datas, excluindo o dia da data final.

Se a data final for anterior à data de início, a contagem será negativa. Para mais sobre np.busday_count leia a documentação aqui .

import numpy as np
np.busday_count('2018-04-10', '2018-04-11')

Note que a função aceita strings, não é necessário instanciar objetos datetime antes de chamar a função.

5
Yinan

Observe que o código do @ neil (caso contrário, ótimo) falhará nos intervalos de domingo a quinta-feira. Aqui está uma correção:

def working_days_in_range(from_date, to_date):
    from_weekday = from_date.weekday()
    to_weekday = to_date.weekday()
    # If start date is after Friday, modify it to Monday
    if from_weekday > 4:
        from_weekday = 0
    day_diff = to_weekday - from_weekday
    whole_weeks = ((to_date - from_date).days - day_diff) / 7
    workdays_in_whole_weeks = whole_weeks * 5
    beginning_end_correction = min(day_diff, 5) - (max(to_weekday - 4, 0) % 5)
    working_days = workdays_in_whole_weeks + beginning_end_correction
    # Final sanity check (i.e. if the entire range is weekends)
    return max(0, working_days)
4
vekerdyb

Corrigido sábado a domingo mesmo fim de semana para funcionar.

from __future__ import print_function
from datetime import date, timedelta

def workdaycount(startdate,enddate):
    if startdate.year != enddate.year:
        raise ValueError("Dates to workdaycount must be during same year")
    if startdate == enddate:
        return int(startdate.weekday() < 5)
    Elif (enddate - startdate).days == 1 and enddate.weekday() == 6: # Saturday and Sunday same weekend
        return 0
    first_week_workdays = min(startdate.weekday(), 4) + 1
    last_week_workdays = min(enddate.weekday(), 4) + 1
    workweeks = int(enddate.strftime('%W')) - int(startdate.strftime('%W'))
    return (5 * workweeks)  + last_week_workdays - first_week_workdays + 1

for comment, start,end in (
     ("Two dates same weekend:", date(2010,9,18), date(2010,9,19)),
     ("Same dates during weekend:", date(2010,9,19), date(2010,9,19)),
     ("Same dates during week", date(2010,9,16), date(2010,9,16)),
     ("Dates during same week", date(2010,9,13), date(2010,9,16)),
     ("Dates during following weeks", date(2010,9,7), date(2010,9,16)),
     ("Dates after two weeks", date(2010,9,7), date(2010,9,24)),
     ("Dates from other solution", date(2010,1, 1), date(2010, 3,31))):

    daydiff = end.weekday() - start.weekday()
    days = ((end-start).days - daydiff) / 7 * 5 + min(daydiff,5)
    daygenerator = (start + timedelta(x + 1) for x in xrange((end - start).days))
    gendays = sum(day.weekday() < 5 for day in daygenerator)

    print(comment,start,end,workdaycount(start,end))
    print('Other formula:', days, '. Generator formula: ', gendays)
3
Tony Veijalainen

Eu adaptei a resposta de Dave Webb em uma função e adicionei alguns casos de teste:

import datetime

def weekdays_between(start, end):
    return sum([1 for daydelta in xrange(1, (end - start).days + 1)
                if (start + datetime.timedelta(daydelta)).weekday() < 5])

assert 7 == weekdays_between(
    datetime.date(2014,2,19),
    datetime.date(2014,3,1))

assert 1 == weekdays_between(
    datetime.date(2014,2,19),
    datetime.date(2014,2,20))

assert 2 == weekdays_between(
    datetime.date(2014,2,19),
    datetime.date(2014,2,22))

assert 2 == weekdays_between(
    datetime.date(2014,2,19),
    datetime.date(2014,2,23))

assert 3 == weekdays_between(
    datetime.date(2014,2,19),
    datetime.date(2014,2,24))

assert 1 == weekdays_between(
    datetime.date(2014,2,21),
    datetime.date(2014,2,24))

assert 1 == weekdays_between(
    datetime.date(2014,2,22),
    datetime.date(2014,2,24))

assert 2 == weekdays_between(
    datetime.date(2014,2,23),
    datetime.date(2014,2,25))
2
jterrace
import datetime

# some givens
dateB = datetime.date(2010, 8, 31)
dateA = datetime.date(2010, 7, 8)
delta = datetime.timedelta(1)

# number of days
days = 0

while dateB != dateA:
    #subtract a day
    dateB -= delta

    # if not saturday or sunday, add to count
    if dateB.isoweekday() not in (6, 7):
        days += 1

Eu acho que algo assim deveria funcionar. Eu não tenho as ferramentas para testá-lo agora.

2
Coding District

Minha solução também está contando o último dia. Então, se início e fim são definidos para o mesmo dia da semana, então o número será 1 (por exemplo, 17 de outubro ambos). Se início e fim são 2 dias úteis consecutivos, a resposta será 2 (por exemplo, para 17 e 18 de outubro). Ele conta as semanas inteiras (em cada um teremos dois dias de fim de semana) e depois verifica os dias lembrados se eles contiverem dias de fim de semana. 

import datetime

def getWeekdaysNumber(start,end):
    numberOfDays = (end-start).days+1
    numberOfWeeks = numberOfDays // 7
    reminderDays = numberOfDays % 7
    numberOfDays -= numberOfWeeks *2
    if reminderDays:
        #this line is creating a set of weekdays for remainder days where 7 and 0 will be Saturday, 6 and -1 will be Sunday
        weekdays = set(range(end.isoweekday(), end.isoweekday() - reminderDays, -1))
        numberOfDays -= len(weekdays.intersection([7,6,0,-1])
    return numberOfDays 

exemplo de uso:

start = date(2018,10,10)
end = date (2018,10,17)
result = getWeekdaysNumber(start,end)`
0
varciasz

Esta é uma função que implementei para medir quantos dias úteis são necessários para que o código seja integrado entre as filiais. Não precisa de iterações em todos os dias intermediários, como outras soluções, mas apenas na primeira semana.

Esse problema pode ser dividido em dois problemas diferentes:

  1. Calculando o número de semanas inteiras no intervalo : para uma semana integral, o número de dias de fim de semana é sempre 2. Esta é uma divisão inteira trivial: (todate - fromdate)/7

  2. Calculando o número de dias de fim de semana no intervalo restante : isso pode ser facilmente resolvido com a abordagem de contagem (map-reduce like): sum(map(is_weekend, rem_days)).

def count_working_days(fromdate, todate):
    from datetime import timedelta as td
    def is_weekend(d): return d.weekday() > 4

    # 1st problem
    num_weeks = (todate - fromdate).days/7

    # 2nd problem
    rem_days = (todate - fromdate).days%7
    rem_weekend_days = sum(is_weekend(fromdate + td(days=i+1)) for i in range(rem_days))

    return (todate - fromdate).days - 2*num_weeks - rem_weekend_days

E uma amostra do seu trabalho:

>>> for i in range(10): latency(datetime.now(), datetime.now() + timedelta(days=i))
...
0    1    1    1    2    3    4    5    6    6
0
Ernesto Puerta

Eu tentei as duas melhores respostas (Dave Webb's e Neil's) e por alguma razão eu estava recebendo respostas incorretas de ambos. Pode ter sido um erro da minha parte, mas eu fui com uma biblioteca existente na base de que provavelmente tinha mais funcionalidade e foi melhor testado para casos de borda: 

https://bitbucket.org/shelldweller/python-bizdatetime

0
Andy Baker