Python中的第n个工作日计算 - 这段代码有什么问题?

时间:2012-08-09 12:04:37

标签: python datetime

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

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

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

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

7 个答案:

答案 0 :(得分:10)

<强>单行

您可以使用标准库中的日历找到第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周的0索引值(该周的第一天,从第x天开始)。

工作原理

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

示例

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

calendar.Calendar(5).monthdatescalendar(2013,9)[3][0]

答案 1 :(得分:7)

你的问题在这里:

adj = temp.weekday()-week_day

首先,你正在以错误的方式减去事物:你需要从所需的那一天减去实际的一天,而不是相反。

其次,您需要确保减法的结果不是负数 - 应使用% 7将其置于0-6范围内。

结果:

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)

答案 2 :(得分:1)

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

但是它可以用作完善的基础:

您看到的是您得到的:

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
可以使用如下lambda表达式重构

get_nth_DOW_for_YY_MM()

具有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]

答案 3 :(得分:0)

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

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

答案 4 :(得分: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

答案 5 :(得分:0)

稍微调整一下就可以使单行正常工作:

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

这里的 n_th 应该被解释为 c-style,例如0 是第一个索引。

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

>>> calendar.Calendar(0).monthdatescalendar(2018, 7)[0][-1]
datetime.date(2018, 7, 1)

答案 6 :(得分:0)

这里的人似乎喜欢单线,我会在下面推荐。

import calendar
[cal[0] for cal in calendar.Calendar(x).monthdatescalendar(year, month) if cal[0].month == month][n]