计算日期之间的天数,忽略周末

时间:2010-09-01 05:54:45

标签: python

如何计算忽略周末的两个日期之间的天数?

17 个答案:

答案 0 :(得分:61)

我认为最干净的解决方案是使用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 )

答案 1 :(得分:49)

>>> 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

fromdatetodate的{​​{3}} creates a generator using a generator expression

然后我们可以从生成器创建一个列表,使用will yield the list of days过滤掉周末,列表的大小给出了我们想要的天数。但是,为了节省将整个列表存储在内存中如果日期相隔很长时间可能会出现问题,我们使用另一个生成器表达式来过滤掉周末但返回1而不是每个日期。 the weekday() function,无需存储整个列表。

注意,如果fromdate == todate,则计算0而不是1。

答案 2 :(得分:10)

到目前为止给出的答案是可行的,但如果日期相隔很远(由于循环),效率非常低。

这应该有效:

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)

这将整整几周(有5个工作日),然后处理剩余的几天。

答案 3 :(得分:7)

懒惰的方法是pip install workdays获取完全相同的python包。

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

答案 4 :(得分:6)

首先导入numpynp。函数np.busday_count计算两个日期之间的有效天数,不包括结束日期的日期。

如果结束日期早于开始日期,则计数将为负数。有关np.busday_count的更多信息,请阅读文档here

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

请注意,函数接受字符串,在调用函数之前不必实例化datetime个对象。

答案 5 :(得分:4)

请注意,@ neil的(其他很棒的)代码将在星期日到星期四的时间间隔内失败。这是一个修复:

M/dd/yyyy

答案 6 :(得分:3)

修复周六到周日同一周末的运作。

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)

答案 7 :(得分:2)

我将Dave Webb的答案改编成了一个函数并添加了一些测试用例:

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))

答案 8 :(得分:2)

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

我觉得这样的事情应该有效。我现在没有工具来测试它。

答案 9 :(得分:1)

您可以使用以下简单功能来获取任意两个给定日期之间的工作天数:

import datetime

def working_days(start_dt,end_dt):
    num_days = (end_dt -start_dt).days +1
    num_weeks =(num_days)//7
    a=0
    #condition 1
    if end_dt.strftime('%a')=='Sat':
        if start_dt.strftime('%a') != 'Sun':
            a= 1
    #cindition 2
    if start_dt.strftime('%a')=='Sun':
        if end_dt.strftime('%a') !='Sat':
            a =1
    #condition 3
    if end_dt.strftime('%a')=='Sun':
        if start_dt.strftime('%a') not in ('Mon','Sun'):
            a =2
    #condition 4        
    if start_dt.weekday() not in (0,6):
        if (start_dt.weekday() -end_dt.weekday()) >=2:
            a =2
    working_days =num_days -(num_weeks*2)-a

    return working_days

用法示例:

start_dt = datetime.date(2019,6,5)
end_dt = datetime.date(2019,6,21)

working_days(start_dt,end_dt)

在此,包括开始日期和结束日期,但不包括所有周末。
希望有帮助!

答案 10 :(得分:1)

到目前为止,我发现所提供的解决方案都不令人满意。要么我不想要依赖的lib,要么循环算法效率低下,或者有些算法不能在所有情况下都起作用。不幸的是,@ neil提供的那个不能很好地工作。 @vekerdyb的回答已纠正了该问题,不幸的是,该回答不适用于所有情况(例如,选择同一周末的星期六或星期日)。
因此,我坐下并竭尽所能,提出了一个适用于所有输入日期的解决方案。它既小又高效。当然,也可以随时发现此错误。开始和结束是包括在内的(例如,一周中的星期一至星期二是2个工作日)。

SqlConnection con;
    SqlCommand cmd;
    public bool exsql(string query)
    {       
       try {
            con = null;
            con = new SqlConnection(ConnectionString.path);
            cmd = new SqlCommand(query, con);
            con.Open();
            var rowEffected = cmd.ExecuteNonQuery();
            con.Close();
            if(rowEffected>0)
            {
                return true;
            }
            else
            {
                return false;
            }
            }
       catch (Exception ex)
       {
           MessageBox.Show(ex.Message, "Exception occurred !", 
           MessageBoxButtons.OK, 
           MessageBoxIcon.Error);
       }
    }

答案 11 :(得分:0)

我尝试了前两个答案(戴夫韦伯和尼尔),由于某种原因,我得到了两个错误的答案。这可能是我的错误,但我选择了一个现有的库,因为它可能具有更多的功能并且更好地测试边缘情况:

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

答案 12 :(得分:0)

这是我实现的一项功能,用于衡量代码跨分支机构集成所需的工作日数。它不像其他解决方案那样需要在整个中间时间内进行迭代,但仅限于第一周。

这个问题可以分解为两个不同的问题:

  1. 计算间隔中的整数周数:对于整数周,周末天数始终为2.这是一个微不足道的整数除法:(todate - fromdate)/7 < / p>

  2. 计算剩余时间间隔内的周末天数:这可以通过计数方法轻松解决(map-reduce like):sum(map(is_weekend, rem_days))

  3. 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
    

    其工作样本:

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

答案 13 :(得分:0)

我的解决方案也在计算最后一天。因此,如果开始时间和结束时间设置为同一工作日,则增加数将为1(例如,两个10月17日)。 如果开始和结束是连续2个工作日,则答案将是2(例如10月17日和18日)。 它计算整个星期(每个星期都有2个周末),然后检查提醒日是否包含周末。

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 

用法示例:

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

答案 14 :(得分:0)

您可以使用https://pypi.org/project/python-networkdays/ 该软件包没有依赖项,没有NumPy或pandas可以计算日期。 ;)

In [3]: import datetime

In [4]: from networkdays import networkdays

In [5]: HOLIDAYS  = { datetime.date(2020, 12, 25),}

In [6]: days = networkdays.Networkdays(datetime.date(2020, 12, 1),datetime.date(2020, 12, 31), holidays=HOLIDAYS, weekdaysoff={6,7})

In [7]: days.networkdays()
Out[7]:
[datetime.date(2020, 12, 1),
 datetime.date(2020, 12, 2),
 datetime.date(2020, 12, 3),
 datetime.date(2020, 12, 4),
 datetime.date(2020, 12, 7),
 datetime.date(2020, 12, 8),
 datetime.date(2020, 12, 9),
 datetime.date(2020, 12, 10),
 datetime.date(2020, 12, 11),
 datetime.date(2020, 12, 14),
 datetime.date(2020, 12, 15),
 datetime.date(2020, 12, 16),
 datetime.date(2020, 12, 17),
 datetime.date(2020, 12, 18),
 datetime.date(2020, 12, 21),
 datetime.date(2020, 12, 22),
 datetime.date(2020, 12, 23),
 datetime.date(2020, 12, 24),
 datetime.date(2020, 12, 28),
 datetime.date(2020, 12, 29),
 datetime.date(2020, 12, 30),
 datetime.date(2020, 12, 31)]

答案 15 :(得分:0)

这是我用于我的管理脚本的东西,它考虑了假期,无论您在哪个国家/地区(使用网络服务获取特定国家/地区的假期数据)。需要一些效率重构,但除此之外,它有效。

from dateutil import rrule
from datetime import datetime
import pytz

timezone_manila = pytz.timezone('Asia/Manila')

class Holidays(object):
    
    def __init__(self, holidaydata):
        self.holidaydata = holidaydata
        
    def isHoliday(self,dateobj):
        for holiday in self.holidaydata:
            d = datetime(holiday['date']['year'], holiday['date']['month'], holiday['date']['day'], tzinfo=timezone_manila)            
            if d == dateobj:
                return True
        
        return False

def pullHolidays(start, end):
    import urllib.request, json
    urlstring = "https://kayaposoft.com/enrico/json/v2.0/?action=getHolidaysForDateRange&fromDate=%s&toDate=%s&country=phl&region=dc&holidayType=public_holiday" % (start.strftime("%d-%m-%Y"),end.strftime("%d-%m-%Y")) 
    
    with urllib.request.urlopen(urlstring) as url:
        holidaydata = json.loads(url.read().decode())
    
    return Holidays(holidaydata)


def countWorkDays(start, end):
    workdays=0
    holidayData=pullHolidays(start,end)
    for dt in rrule.rrule(rrule.DAILY, dtstart=start, until=end):
        if dt.weekday() < 5:
            if holidayData.isHoliday(dt) == False:
                workdays+=1
    
    return workdays

答案 16 :(得分:-1)

在PyPi中使用名为business-duration的包。

示例代码:

from business_duration import businessDuration

import pandas as pd

import datetime

start = pd.to_datetime("2010-1-1 00:00:00")

end = pd.to_datetime("2010-3-31 00:00:00")

businessDuration(startdate=start,enddate=end,unit='day')
  

出[6]:62.99927083333333