最近的Python工作日


79

我需要从当前日期减去工作日

我目前有一些代码,需要始终在最近的工作日运行。如果今天是星期一至星期五,则可能是今天,但是如果是星期六或星期日,则需要将其设置回周末之前的星期五。我目前有一些笨拙的代码可以做到这一点:

 lastBusDay = datetime.datetime.today()
 if datetime.date.weekday(lastBusDay) == 5:      #if it's Saturday
     lastBusDay = lastBusDay - datetime.timedelta(days = 1) #then make it Friday
 elif datetime.date.weekday(lastBusDay) == 6:      #if it's Sunday
     lastBusDay = lastBusDay - datetime.timedelta(days = 2); #then make it Friday

有没有更好的办法?

我可以告诉timedelta在工作日而不是日历日工作吗?


7
假期呢?
SLaks 2010年

以下是dzzone的摘要,可能会对您有所帮助:snippets.dzone.com/posts/show/9173
David Underhill 2010年

是的,我已经在照顾这些:只要假日是工作日,我的数据库就总是回填假日。但我同意,假期通常也是一个问题。我的意思是我可能会开始喜欢上并使用sckits.timeseries,但实际上我想要更简单的东西。
托马斯·布朗

1
你好,我迟到了,抱歉。OP可以做的一件简单的事情是检查datetime.date.weekday(lastBusDay)> = 5,而不是分别检查Saturday和Sunday。但是是的..无论如何,下面还有其他更好的答案。
塔戈马

Answers:


139

使用熊猫!

import datetime
import pandas as pd
# BDay is business day, not birthday...
from pandas.tseries.offsets import BDay

today = datetime.datetime.today()
print(today - BDay(4))

由于今天是9月26日,星期四,因此您将获得以下输出:

datetime.datetime(2013, 9, 20, 14, 8, 4, 89761)

2
真好 今天,这是正确的答案。当我问问Q时,熊猫还有些残缺。
托马斯·布朗

6
最新的熊猫版本(0.14.0)也支持假期日历
惊人的2014年

1
安装熊猫会将233M添加到vps。所以我重新发明了准系统轮子。
Stuart Gathman '19

@EBpandas.pydata.org/ pandas
docs/

13

如果您愿意安装其他库,似乎有几种选择。

这篇文章描述了一种使用dateutil定义工作日的方法

http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-09/3758.html

通过BusinessHours,您可以自定义定义假期等的列表,以定义工作时间(以及延长工作日)的时间。

http://pypi.python.org/pypi/BusinessHours/


好人的艾莉森。不幸的是仍然不是很简单。我要走你的路线。谢谢您的帮助。
Thomas Browne 2010年

2
在所有文化中定义工作日似乎不太容易,因此很难纳入标准库。
艾莉森·R.2010年

2
一个正确的观点是,正如我的申请确实是针对金融市场的,埃及和以色列在周日开放。
Thomas Browne 2010年

2
有人甚至使用BuinessHours吗?在1分钟内,我发现第87行应读取self.worktiming[1](缺少self),第51行应读取extradays(缺少s)。源代码本身看起来很差,各处都有分号。
Pakman 2014年

11

也许这段代码可以帮助您:

lastBusDay = datetime.datetime.today()
shift = datetime.timedelta(max(1,(lastBusDay.weekday() + 6) % 7 - 3))
lastBusDay = lastBusDay - shift

这个想法是,星期一必须回溯3天,星期日2回溯,其他任何一天回溯1天。

该语句(lastBusDay.weekday() + 6) % 7只是将星期一从0改为6。

真的不知道这样做是否会更好。


11

如果您想跳过美国假期以及周末,这对我有用(使用pandas 0.23.3):

import pandas as pd
from pandas.tseries.holiday import USFederalHolidayCalendar
from pandas.tseries.offsets import CustomBusinessDay
US_BUSINESS_DAY = CustomBusinessDay(calendar=USFederalHolidayCalendar())
july_5 = pd.datetime(2018, 7, 5)
result = july_5 - 2 * US_BUSINESS_DAY # 2018-7-2

要转换为python date对象,我这样做:

result.to_pydatetime().date()

10

免责声明:我是作者...

我写了一个包来做这个,业务日期计算。您可以使用自定义星期规格和假期。

在处理财务数据时,我遇到了这个确切的问题,并且发现任何可用的解决方案都不容易,所以我写了一个。

希望这对其他人有用。

https://pypi.python.org/pypi/business_calendar/


1
非常感谢,您的图书馆对我来说很完美。也许您应该在文档中添加以下内容:如果要减去天数,则库支持负数天,并且
正值

嘿,我知道这可能不是联系您的最佳方法,但我只想报告我与business_calendar模块有关的问题。我设置了一个美国联邦假日日历:['2015-01-01','2015-01-19','2015-02-16','2015-05-25','2015-07-03' ,“ 2015-09-07”,“ 2015-10-12”,“ 2015-11-11”,“ 2015-11-26”,“ 2015-12-25”],然后尝试计算datetime之间的时差( 2015、1、16、15、28、40)和datetime(2015、1、23、11、58、0),但始终返回-1。从在4.日期时间的结果(正确地)汽提H / M /秒
达伦林格氏

1
实际上,在进一步测试某些日期比较时,只是没有明显的理由而阻塞,似乎从未返回结果。在我上面的示例中,尝试将datetime(2015,1,16)与datetime(2015,1,25)进行比较会导致这种情况,无论是否显式设置任何假日。经调查,当date2参数恰巧是非工作日的日期时,会发生这种情况。
达伦·林格

6

timeboard 包这样做。

假设您的日期是2017年9月4日。尽管是星期一,但在美国(劳动节)还是个假期。因此,最近的工作日是9月1日,星期五。

>>> import timeboard.calendars.US as US
>>> clnd = US.Weekly8x5()
>>> clnd('04 Sep 2017').rollback().to_timestamp().date()
datetime.date(2017, 9, 1)

在英国,2017年9月4日是常规工作日,因此最近的工作日本身就是该日。

>>> import timeboard.calendars.UK as UK
>>> clnd = UK.Weekly8x5()
>>> clnd('04 Sep 2017').rollback().to_timestamp().date()
datetime.date(2017, 9, 4)

免责声明:我是时间表的作者。


2

这将产生一个工作日的生成器,当然没有假期,停止是datetime.datetime对象。如果您需要假期,只需在假期列表中添加其他参数,然后使用“ IFology”检查;-)

def workingdays(stop, start=datetime.date.today()):
    while start != stop:
        if start.weekday() < 5:
            yield start
        start += datetime.timedelta(1)

以后你可以指望他们像

workdays = workingdays(datetime.datetime(2015, 8, 8))
len(list(workdays))

2

如果有人在寻找与假期相关的解决方案(没有像熊猫这样的大型图书馆),请尝试以下功能:

import holidays
import datetime


def previous_working_day(check_day_, holidays=holidays.US()):
    offset = max(1, (check_day_.weekday() + 6) % 7 - 3)
    most_recent = check_day_ - datetime.timedelta(offset)
    if most_recent not in holidays:
        return most_recent
    else:
        return previous_working_day(most_recent, holidays)

check_day = datetime.date(2020, 12, 28)
previous_working_day(check_day)

产生:

datetime.date(2020, 12, 24)

0

为什么不尝试以下方法:

lastBusDay = datetime.datetime.today()
if datetime.date.weekday(lastBusDay) not in range(0,5):
    lastBusDay = 5

0
 def getNthBusinessDay(startDate, businessDaysInBetween):
    currentDate = startDate
    daysToAdd = businessDaysInBetween
    while daysToAdd > 0:
        currentDate += relativedelta(days=1)
        day = currentDate.weekday()
        if day < 5:
            daysToAdd -= 1

    return currentDate 

0

另一个简化版本

lastBusDay = datetime.datetime.today()
wk_day = datetime.date.weekday(lastBusDay)
if wk_day > 4:      #if it's Saturday or Sunday
    lastBusDay = lastBusDay - datetime.timedelta(days = wk_day-4) #then make it Friday

0

解决方案,无论不同的司法管辖区有不同的假期:

如果您需要在表格中找到正确的ID,则可以使用此代码段。表模型是sqlalchemy模型,要搜索的日期在现场日期中。

def last_relevant_date(db: Session, given_date: date) -> int:
    available_days = (db.query(Table.id, Table.day)
                      .order_by(desc(Table.day))
                      .limit(100).all())
    close_dates = pd.DataFrame(available_days)
    close_dates['delta'] = close_dates['day'] - given_date
    past_dates = (close_dates
                  .loc[close_dates['delta'] < pd.Timedelta(0, unit='d')])
    table_id = int(past_dates.loc[past_dates['delta'].idxmax()]['id'])
    return table_id

当您必须批量转换时,这不是我建议的解决方案。由于您不使用联接,因此它相当通用且昂贵。此外,它假设您有一个相关的日子,该日子是模型表中最近100天之一。因此,它可以处理日期可能不同的数据输入。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.