【问题标题】:Most recent previous business day in PythonPython 中最近的前一个工作日
【发布时间】:2011-01-14 13:09:52
【问题描述】:

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

我目前有一些代码需要始终在最近的工作日运行。因此,如果我们是周一到周五,那可能是今天,但如果是周六或周日,那么我需要将其设置回周末前的周五。我目前有一些漂亮的笨拙代码来执行此操作:

 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 在工作日而不是日历日工作吗?

【问题讨论】:

  • 假期呢?
  • 这里有一个来自 dzzone 的 sn-p,可能会对您有所帮助:snippets.dzone.com/posts/show/9173
  • 是的,我已经在处理这些问题了:只要假期是在工作日,我的数据库总是会回填假期。但我同意,一般假期也是一个问题。我的意思是我可以开始花哨并使用 skits.timeseries 但我真的想要更简单的东西。
  • 你好,我迟到了,对不起。 OP 可以做的一件更简单的事情是检查是否 datetime.date.weekday(lastBusDay) >=5 而不是分别检查周六和周日。但是是的..无论如何,下面还有其他更好的答案。
  • 星期六和星期日不够通用,例如在某些国家/地区,例如以色列,工作周是周日至周四。

标签: python datetime


【解决方案1】:

如果有人正在寻找关于假期的解决方案(没有像 pandas 这样的大型库),请尝试此功能:

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)

【讨论】:

    【解决方案2】:

    对于 pandas 用例,我发现以下内容非常有用且紧凑,尽管不完全可读:

    获取最近的前一个工作日:

    In [2]: datetime.datetime(2019, 11, 30) + BDay(1) - BDay(1)  # Saturday
    Out[2]: Timestamp('2019-11-29 00:00:00')
    
    
    In [3]: datetime.datetime(2019, 11, 29) + BDay(1) - BDay(1)  # Friday
    Out[3]: Timestamp('2019-11-29 00:00:00')
    

    在另一个方向,只需使用:

    In [4]: datetime.datetime(2019, 11, 30) + BDay(0)  # Saturday
    Out[4]: Timestamp('2019-12-02 00:00:00')
    
    In [5]: datetime.datetime(2019, 11, 29) + BDay(0)  # Friday
    Out[5]: Timestamp('2019-11-29 00:00:00')
    

    【讨论】:

      【解决方案3】:

      使用熊猫!

      import datetime
      # 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)
      

      【讨论】:

      • 不错。这是正确的答案,今天。问 Q 的时候,pandas 还是有点不完整。
      • 最新的 pandas 版本 (0.14.0) 也支持假日日历
      • 安装pandas会增加233M到vps。所以我重新发明了一个准系统轮子。
      【解决方案4】:

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

      如果你需要在一个表中找到正确的id,你可以使用这个sn-p。 Table 模型是一个 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 天之一。因此它处理可能具有不同日期的数据输入。

      【讨论】:

        【解决方案5】:

        另一个简化版本

        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
        

        【讨论】:

          【解决方案6】:

          如果您想跳过美国假期和周末,这对我有用(使用 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 日期对象,我这样做了:

          result.to_pydatetime().date()
          

          【讨论】:

            【解决方案7】:

            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)
            

            免责声明:我是 timeboard 的作者。

            【讨论】:

              【解决方案8】:

              这将给出一个工作日的生成器,当然没有假期,stop 是 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))
              

              【讨论】:

                【解决方案9】:

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

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

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

                希望这对其他人有用。

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

                【讨论】:

                • 非常感谢,您的库非常适合我。如果您想减去天数,也许您应该在文档中添加您的库支持负天数,并且它在 pip 上
                • 嘿,我知道这可能不是联系您的最佳方式,但我只是想报告我在使用您的 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/s。
                • 实际上,在进一步测试某些日期比较时,只是阻塞,没有明显的原因,似乎永远不会返回结果。在我上面的示例中,尝试将 datetime(2015, 1, 16) 与 datetime(2015, 1, 25) 进行比较会导致这样的块,无论是否明确设置任何假期。经过调查,当 date2 参数恰好是一个非工作日的日期时,就会发生这种情况。
                【解决方案10】:
                 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 
                

                【讨论】:

                  【解决方案11】:

                  你为什么不试试类似的东西:

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

                  【讨论】:

                    【解决方案12】:

                    也许这段代码会有所帮助:

                    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。

                    真的不知道这样在性能方面会不会更好。

                    【讨论】:

                      【解决方案13】:

                      如果您愿意安装额外的库,似乎有多种选择。

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

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

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

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

                      【讨论】:

                      • 不错的艾莉森。不幸的是,仍然不是很简单。不过我会走你的路。感谢您的帮助。
                      • 在所有文化中定义工作日不太可能简单到足以包含在标准库中。
                      • 一个有效的观点,因为我的申请确实是针对金融市场的,埃及和以色列周日开放。
                      • 有人甚至使用 BuinessHours 吗?在 1 分钟内,我发现第 87 行应该是 self.worktiming[1](缺少 self),第 51 行应该是 extradays(缺少 s)。源代码本身看起来很差,分号散落在各处。
                      猜你喜欢
                      • 1970-01-01
                      • 2019-02-12
                      • 2016-09-23
                      • 2018-10-26
                      • 2020-06-30
                      • 1970-01-01
                      • 2013-06-24
                      • 2012-08-16
                      • 2017-06-29
                      相关资源
                      最近更新 更多