9

我正在尝试计算给定日期的第 n 个工作日。例如,我应该能够计算给定日期该月的第三个星期三。

我已经编写了两个版本的函数,应该这样做:

from datetime import datetime, timedelta

### version 1
def nth_weekday(the_date, nth_week, week_day):
    temp = the_date.replace(day=1)
    adj = (nth_week-1)*7 + temp.weekday()-week_day
    return temp + timedelta(days=adj)

### version 2
def nth_weekday(the_date, nth_week, week_day):
    temp = the_date.replace(day=1)
    adj = temp.weekday()-week_day
    temp += timedelta(days=adj)
    temp += timedelta(weeks=nth_week)
    return temp

控制台输出

# Calculate the 3rd Friday for the date 2011-08-09
x=nth_weekday(datetime(year=2011,month=8,day=9),3,4)
print 'output:',x.strftime('%d%b%y') 

# output: 11Aug11 (Expected: '19Aug11')

这两个函数的逻辑显然是错误的,但我似乎无法找到错误 - 任何人都可以发现代码有什么问题 - 我如何修复它以返回正确的值?

4

8 回答 8

10

你的问题在这里:

adj = temp.weekday()-week_day

首先,您以错误的方式减去事物:您需要从所需的日期中减去实际的日期,而不是相反。

其次,您需要确保减法的结果不是负数 - 它应该使用 . 放在 0-6 范围内% 7

结果:

adj = (week_day - temp.weekday()) % 7

此外,在您的第二个版本中,您需要nth_week-1像在第一个版本中一样添加几周。

完整示例:

def nth_weekday(the_date, nth_week, week_day):
    temp = the_date.replace(day=1)
    adj = (week_day - temp.weekday()) % 7
    temp += timedelta(days=adj)
    temp += timedelta(weeks=nth_week-1)
    return temp

>>> nth_weekday(datetime(2011,8,9), 3, 4)
datetime.datetime(2011, 8, 19, 0, 0)
于 2012-08-09T12:30:01.840 回答
9

单线

您可以使用标准库中的日历找到第 n 个工作日。

import calendar
calendar.Calendar(x).monthdatescalendar(year, month)[n][0]

在哪里:

  • x : 代表你的工作日的整数(0 是星期一)

  • n :您问题的“第 n”部分

  • 年,月:整数年和月

这将返回一个 datetime.date 对象。

坏了

可以这样分解:

calendar.Calendar(x)

创建一个日历对象,其工作日从您所需的工作日开始。

.monthdatescalendar(year, month)

返回该月的所有日历日。

[n][0]

返回第 n 周(该周的第一天,从第 x 天开始)的 0 索引值。

为什么它有效

在您要求的工作日开始一周的原因是,默认情况下,0(星期一)将用作一周的第一天,如果该月从星期三开始,日历将考虑从第一次出现的第一周开始星期一(即第 2 周),你会落后一周。

例子

如果您需要 2013 年 9 月的第三个星期六(该月的美国股票期权到期日),您将使用以下命令:

calendar.Calendar(5).monthdatescalendar(2013,9)[3][0]
于 2013-09-21T04:00:16.680 回答
2

得票最多的单线的问题是它不起作用。

但是,它可以用作细化的基础:

你看到这是你得到的:

c = calendar.Calendar(calendar.SUNDAY).monthdatescalendar(2018, 7)
for c2 in c:
    print(c2[0])

2018-07-01
2018-07-08
2018-07-15
2018-07-22
2018-07-29

c = calendar.Calendar(calendar.SUNDAY).monthdatescalendar(2018, 8)
for c2 in c:
    print(c2[0])

2018-07-29
2018-08-05
2018-08-12
2018-08-19
2018-08-26

如果您考虑一下,它会尝试将日历组织成嵌套列表,以便一次打印一周的日期。因此,其他月份的落后者开始发挥作用。通过使用该月有效日期的新列表 - 这可以解决问题。


用附加列表回答

import calendar
import datetime
def get_nth_DOW_for_YY_MM(dow, yy, mm, nth) -> datetime.date:
    #dow - Python Cal - 6 Sun 0 Mon ...  5 Sat
    #nth is 1 based... -1. is ok for last.
    i = -1 if nth == -1 or nth == 5 else nth -1
    valid_days = []
    for d in calendar.Calendar(dow).monthdatescalendar(yy, mm):
        if d[0].month == mm:
            valid_days.append(d[0])
    return valid_days[i]

所以这里是如何调用它的:

firstSundayInJuly2018 = get_nth_DOW_for_YY_MM(calendar.SUNDAY, 2018, 7, 1)
firstSundayInAugust2018 = get_nth_DOW_for_YY_MM(calendar.SUNDAY, 2018, 8, 1)
print(firstSundayInJuly2018)
print(firstSundayInAugust2018)

这是输出:

2018-07-01 
2018-08-05

get_nth_DOW_for_YY_MM()可以使用 lambda 表达式重构,如下所示:

用 lambda 表达式重构回答

import calendar
import datetime
def get_nth_DOW_for_YY_MM(dow, yy, mm, nth) -> datetime.date:
    #dow - Python Cal - 6 Sun 0 Mon ...  5 Sat
    #nth is 1 based... -1. is ok for last.
    i = -1 if nth == -1 or nth == 5 else nth -1
    return list(filter(lambda x: x.month == mm, \
          list(map(lambda x: x[0], \ 
            calendar.Calendar(dow).monthdatescalendar(yy, mm) \
          )) \
        ))[i]

于 2018-07-06T22:33:09.003 回答
1

如果目标日期在本月的第一天,单行答案似乎不起作用。例如,如果您想要每个月的第二个星期五,那么单线方法

calendar.Calendar(4).monthdatescalendar(year, month)[2][0]

2013 年 3 月将返回 2013 年 3 月 15 日,它应该是 2013 年 3 月 8 日。也许添加一个像这样的支票

if date(year, month, 1).weekday() == x:
    delivery_date.append(calendar.Calendar(x).monthdatescalendar(year, month)[n-1][0])
else:
    delivery_date.append(calendar.Calendar(x).monthdatescalendar(year, month)[n][0])
于 2016-10-03T09:07:15.840 回答
0

稍加调整即可使单线正常工作:

import calendar
calendar.Calendar((weekday+1)%7).monthdatescalendar(year, month)[n_th][-1]

这里n_th 应该解释为 c 风格,例如 0 是第一个索引。

示例:要查找 2018 年 7 月的第一个星期日,可以键入:

>>> calendar.Calendar(0).monthdatescalendar(2018, 7)[0][-1]
datetime.date(2018, 7, 1)
于 2021-01-21T12:30:08.767 回答
0

这里的人似乎喜欢单线,我将在下面提出。

import calendar
[cal[0] for cal in calendar.Calendar(x).monthdatescalendar(year, month) if cal[0].month == month][n]
于 2021-01-22T03:04:19.587 回答
0

或者,这将适用于 Python 2,返回该月中工作日的出现,即如果输入 2018 年 6 月 16 日,则返回 2018 年 6 月 16 日当天的出现

您可以将月/年/日期整数替换为您可能想要的任何内容 - 现在它正在通过以下方式从系统获取输入/日期datetime

省略打印语句或pass在不需要它们的地方使用

import calendar
import datetime
import pprint

month_number = int(datetime.datetime.now().strftime('%m'))
year_number = int(datetime.datetime.now().strftime('%Y'))
date_number = int(datetime.datetime.now().strftime('%d'))
day_ofweek = str(datetime.datetime.now().strftime('%A'))


def weekday_occurance():
print "\nFinding current date here\n"
for week in xrange(5):
    try:
        calendar.monthcalendar(year_number, month_number)[week].index(date_number)
        occurance = week + 1
        print "Date %s of month %s and year %s is %s #%s in this month." % (date_number,month_number,year_number,day_ofweek,occurance)
        return occurance
        break
    except ValueError as e:
        print "The date specified is %s which is week %s" % (e,week)



myocc = weekday_occurance()
print myocc
于 2018-06-15T09:37:18.720 回答
0

作为Python dateutil 包( )的扩展的relativedelta模块完全符合您的要求:pip install python-dateutil

from dateutil import relativedelta
import datetime


def nth_weekday(the_date, nth_week, week_day):
    return the_date.replace(day=1) + relativedelta.relativedelta(
        weekday=week_day(nth_week)
    )

print(nth_weekday(datetime.date.today(), 3, relativedelta.FR))

这里的关键部分评估为weekday=relativedelta.FR(3):本月的第三个星期五。以下是参数文档的相关部分,weekday

工作日:

relativedelta 模块中可用的工作日实例之一(MO、TU 等)。这些实例可能会收到一个参数 N,指定第 N 个工作日,它可以是正数或负数(如 MO(+1) 或 MO(-2))。

于 2021-09-03T18:16:14.047 回答