Ben*_*Ben 70
我认为最干净的解决方案是使用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 )
Run Code Online (Sandbox Code Playgroud)
Dav*_*ebb 50
>>> 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
Run Code Online (Sandbox Code Playgroud)
这将创建使用生成器表达式生成器,其将产生的天列表从获取fromdate
到todate
.
然后我们可以从生成器创建一个列表,使用该weekday()
函数过滤掉周末,列表的大小给出我们想要的天数.但是,为了节省将整个列表存储在内存中如果日期相隔很长时间可能会出现问题,我们使用另一个生成器表达式来过滤掉周末但返回1而不是每个日期. 然后我们可以将所有这些1组合在一起以获得长度而无需存储整个列表.
注意,如果fromdate == todate
这个计算0不是1.
nei*_*eil 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)
Run Code Online (Sandbox Code Playgroud)
这将它变成整整一周(有5个工作日),然后处理剩下的几天.
小智 9
首先导入numpy
为np
.该函数np.busday_count
计算两个日期之间的有效天数,不包括结束日期的日期.
如果结束日期早于开始日期,则计数将为负数.有关更多信息,请np.busday_count
阅读此处的文档.
import numpy as np
np.busday_count('2018-04-10', '2018-04-11')
Run Code Online (Sandbox Code Playgroud)
请注意,该函数接受字符串,datetime
在调用函数之前不必实例化对象.
到目前为止,我发现所提供的解决方案都不令人满意。要么依赖于我不想要的库,要么存在低效的循环算法,或者存在不适用于所有情况的算法。不幸的是@neil 提供的效果不够好。@vekerdyb 的答案纠正了这一点,不幸的是,该答案并不适用于所有情况(例如,选择同一个周末的星期六或星期日......)。
所以我坐下来,尽力想出一个适用于所有输入日期的解决方案。它体积小,效率高。当然,也请随意发现其中的错误。开始和结束都包含在内(例如,一周中的周一至周二就是 2 个工作日)。
def get_workdays(from_date: datetime, to_date: datetime):
# if the start date is on a weekend, forward the date to next Monday
if from_date.weekday() > 4:
from_date = from_date + timedelta(days=7 - from_date.weekday())
# if the end date is on a weekend, rewind the date to the previous Friday
if to_date.weekday() > 4:
to_date = to_date - timedelta(days=to_date.weekday() - 4)
if from_date > to_date:
return 0
# that makes the difference easy, no remainders etc
diff_days = (to_date - from_date).days + 1
weeks = int(diff_days / 7)
return weeks * 5 + (to_date.weekday() - from_date.weekday()) + 1
Run Code Online (Sandbox Code Playgroud)
非常感谢 @Renan 介绍了我现在在自己的项目中使用的这个令人惊叹的 API。这是他通过轻微清洁+测试得到的答案。
\nimport urllib.request\nimport json\nfrom typing import Dict\n\nfrom dateutil import rrule\nfrom datetime import date\n\n\nWEEKDAY_FRIDAY = 4 # date.weekday() starts with 0\n\n\nclass CountryCalendar(object):\n\n def __init__(self, special_dates: Dict[date, str]):\n self.special_dates = special_dates\n\n def is_working_day(self, dt: date):\n date_type = self.special_dates.get(dt)\n if date_type == "extra_working_day":\n return True\n if date_type == "public_holiday":\n return False\n return dt.weekday() <= WEEKDAY_FRIDAY\n\n\ndef load_calendar(\n country: str,\n region: str,\n start_date: date,\n end_date: date\n) -> CountryCalendar:\n """\n Access Enrico Service 2.0 JSON\n https://kayaposoft.com/enrico/\n\n Response format (for country=rus):\n\n [\n {\n holidayType: "public_holiday",\n date: {\n day: 2,\n month: 1,\n year: 2022,\n dayOfWeek: 7\n },\n name: [\n {lang: "ru", text: "\xd0\x9d\xd0\xbe\xd0\xb2\xd0\xbe\xd0\xb3\xd0\xbe\xd0\xb4\xd0\xbd\xd0\xb8\xd0\xb5 \xd0\xba\xd0\xb0\xd0\xbd\xd0\xb8\xd0\xba\xd1\x83\xd0\xbb\xd1\x8b"},\n {lang: "en", text: "New Year\xe2\x80\x99s Holiday"}\n ]\n },\n ...\n ]\n """\n urlstring = (\n f"https://kayaposoft.com/enrico/json/v2.0/"\n f"?action=getHolidaysForDateRange"\n f"&fromDate={start_date:%d-%m-%Y}"\n f"&toDate={end_date:%d-%m-%Y}"\n f"&country={country}"\n f"®ion={region}"\n f"&holidayType=all"\n )\n\n with urllib.request.urlopen(urlstring) as url:\n payload = json.loads(url.read().decode())\n\n return CountryCalendar({\n date(\n special_date["date"]["year"],\n special_date["date"]["month"],\n special_date["date"]["day"],\n ): special_date["holidayType"]\n for special_date in payload\n })\n\n\ndef count_work_days(\n start_date: date,\n end_date: date,\n country: str,\n region: str,\n):\n """\n Get working days specific for country\n using public holidays internet provider\n """\n if start_date > end_date:\n return 0\n\n try:\n country_calendar = load_calendar(country, region, start_date, end_date)\n except Exception as exc:\n print(f"Error accessing calendar of country: {country}. Exception: {exc}")\n raise\n\n workdays = 0\n for dt in rrule.rrule(rrule.DAILY, dtstart=start_date, until=end_date):\n if country_calendar.is_working_day(dt):\n workdays += 1\n\n return workdays\n\n\ndef test(test_name: str, start_date: date, end_date: date, expected: int):\n print(f"Running: {test_name}... ", end="")\n params = dict(\n start_date=start_date,\n end_date=end_date,\n country="rus",\n region=""\n )\n assert expected == count_work_days(**params), dict(\n expected=expected,\n actual=count_work_days(**params),\n **params\n )\n print("ok")\n\n\n# Start on Mon\ntest("Mon - Mon", date(2022, 4, 4), date(2022, 4, 4), 1)\ntest("Mon - Tue", date(2022, 4, 4), date(2022, 4, 5), 2)\ntest("Mon - Wed", date(2022, 4, 4), date(2022, 4, 6), 3)\ntest("Mon - Thu", date(2022, 4, 4), date(2022, 4, 7), 4)\ntest("Mon - Fri", date(2022, 4, 4), date(2022, 4, 8), 5)\ntest("Mon - Sut", date(2022, 4, 4), date(2022, 4, 9), 5)\ntest("Mon - Sun", date(2022, 4, 4), date(2022, 4, 10), 5)\ntest("Mon - next Mon", date(2022, 4, 4), date(2022, 4, 11), 6)\ntest("Mon - next Tue", date(2022, 4, 4), date(2022, 4, 12), 7)\n\n\n# Start on Fri\ntest("Fri - Sut", date(2022, 4, 1), date(2022, 4, 2), 1)\ntest("Fri - Sun", date(2022, 4, 1), date(2022, 4, 3), 1)\ntest("Fri - Mon", date(2022, 4, 1), date(2022, 4, 4), 2)\ntest("Fri - Tue", date(2022, 4, 1), date(2022, 4, 5), 3)\ntest("Fri - Wed", date(2022, 4, 1), date(2022, 4, 6), 4)\ntest("Fri - Thu", date(2022, 4, 1), date(2022, 4, 7), 5)\ntest("Fri - next Fri", date(2022, 4, 1), date(2022, 4, 8), 6)\ntest("Fri - next Sut", date(2022, 4, 1), date(2022, 4, 9), 6)\ntest("Fri - next Sun", date(2022, 4, 1), date(2022, 4, 10), 6)\ntest("Fri - next Mon", date(2022, 4, 1), date(2022, 4, 11), 7)\n\n\n# Some edge cases\ntest("start > end", date(2022, 4, 2), date(2022, 4, 1), 0)\ntest("Sut - Sun", date(2022, 4, 2), date(2022, 4, 3), 0)\ntest("Sut - Mon", date(2022, 4, 2), date(2022, 4, 4), 1)\ntest("Sut - Fri", date(2022, 4, 2), date(2022, 4, 8), 5)\ntest("Thu - Fri", date(2022, 3, 31), date(2022, 4, 8), 7)\n\n
Run Code Online (Sandbox Code Playgroud)\n即使@Sebastian的答案在很多情况下都不能应用,因为它没有考虑公共假期和额外的工作日,我仍然觉得它很棒,因为它可以完成工作,并决定修复一个错误(基本上只改变了他的最后一行)。
\nfrom datetime import date, timedelta\n\n\nWEEKDAY_FRIDAY = 4 # date.weekday() starts with 0\n\n\ndef count_work_days(start_date: date, end_date: date):\n """\n Math function to get workdays between 2 dates.\n Can be used only as fallback as it doesn\'t know\n about specific country holidays or extra working days.\n """\n # if the start date is on a weekend, forward the date to next Monday\n\n if start_date.weekday() > WEEKDAY_FRIDAY:\n start_date = start_date + timedelta(days=7 - start_date.weekday())\n\n # if the end date is on a weekend, rewind the date to the previous Friday\n if end_date.weekday() > WEEKDAY_FRIDAY:\n end_date = end_date - timedelta(days=end_date.weekday() - WEEKDAY_FRIDAY)\n\n if start_date > end_date:\n return 0\n # that makes the difference easy, no remainders etc\n diff_days = (end_date - start_date).days + 1\n weeks = int(diff_days / 7)\n\n remainder = end_date.weekday() - start_date.weekday() + 1\n\n if remainder != 0 and end_date.weekday() < start_date.weekday():\n remainder = 5 + remainder\n\n return weeks * 5 + remainder\n\n\ndef test(test_name: str, start_date: date, end_date: date, expected: int):\n print(f"Running: {test_name}... ", end="")\n params = dict(\n start_date=start_date,\n end_date=end_date,\n )\n assert expected == count_work_days(**params), dict(\n expected=expected,\n actual=count_work_days(**params),\n **params\n )\n print("ok")\n\n\n# Start on Mon\ntest("Mon - Mon", date(2022, 4, 4), date(2022, 4, 4), 1)\ntest("Mon - Tue", date(2022, 4, 4), date(2022, 4, 5), 2)\ntest("Mon - Wed", date(2022, 4, 4), date(2022, 4, 6), 3)\ntest("Mon - Thu", date(2022, 4, 4), date(2022, 4, 7), 4)\ntest("Mon - Fri", date(2022, 4, 4), date(2022, 4, 8), 5)\ntest("Mon - Sut", date(2022, 4, 4), date(2022, 4, 9), 5)\ntest("Mon - Sun", date(2022, 4, 4), date(2022, 4, 10), 5)\ntest("Mon - next Mon", date(2022, 4, 4), date(2022, 4, 11), 6)\ntest("Mon - next Tue", date(2022, 4, 4), date(2022, 4, 12), 7)\n\n\n# Start on Fri\ntest("Fri - Sut", date(2022, 4, 1), date(2022, 4, 2), 1)\ntest("Fri - Sun", date(2022, 4, 1), date(2022, 4, 3), 1)\ntest("Fri - Mon", date(2022, 4, 1), date(2022, 4, 4), 2)\ntest("Fri - Tue", date(2022, 4, 1), date(2022, 4, 5), 3)\ntest("Fri - Wed", date(2022, 4, 1), date(2022, 4, 6), 4)\ntest("Fri - Thu", date(2022, 4, 1), date(2022, 4, 7), 5)\ntest("Fri - next Fri", date(2022, 4, 1), date(2022, 4, 8), 6)\ntest("Fri - next Sut", date(2022, 4, 1), date(2022, 4, 9), 6)\ntest("Fri - next Sun", date(2022, 4, 1), date(2022, 4, 10), 6)\ntest("Fri - next Mon", date(2022, 4, 1), date(2022, 4, 11), 7)\n\n\n# Some edge cases\ntest("start > end", date(2022, 4, 2), date(2022, 4, 1), 0)\ntest("Sut - Sun", date(2022, 4, 2), date(2022, 4, 3), 0)\ntest("Sut - Mon", date(2022, 4, 2), date(2022, 4, 4), 1)\ntest("Sut - Fri", date(2022, 4, 2), date(2022, 4, 8), 5)\ntest("Thu - Fri", date(2022, 3, 31), date(2022, 4, 8), 7)\n
Run Code Online (Sandbox Code Playgroud)\n
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
Run Code Online (Sandbox Code Playgroud)
我认为类似的事情应该有效。我现在没有工具来测试它。
归档时间: |
|
查看次数: |
50540 次 |
最近记录: |