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

Hom*_*lli 6 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
Run Code Online (Sandbox Code Playgroud)

控制台输出

# 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')
Run Code Online (Sandbox Code Playgroud)

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

小智 9

一个班轮

你可以找到第n个工作日,其中一个班轮使用标准库中的日历.

import calendar
calendar.Calendar(x).monthdatescalendar(year, month)[n][0]
Run Code Online (Sandbox Code Playgroud)

哪里:

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

  • n:问题的"第n"部分

  • 年,月:整数年和月

这将返回datetime.date对象.

坏了

它可以这样分解:

calendar.Calendar(x)
Run Code Online (Sandbox Code Playgroud)

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

.monthdatescalendar(year, month)
Run Code Online (Sandbox Code Playgroud)

返回该月的所有日历日.

[n][0]
Run Code Online (Sandbox Code Playgroud)

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

它为何有效

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

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

calendar.Calendar(5).monthdatescalendar(2013,9)[3][0]
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这个高投票的答案是不正确的.反例:2017年11月的第3个星期三:`calendar.Calendar(2).monthdatescalendar(2017,11)[3] [0]`返回datetime.date(2017,11,22),而正确的值是2017-11 -15 (3认同)

int*_*jay 7

你的问题在这里:

adj = temp.weekday()-week_day
Run Code Online (Sandbox Code Playgroud)

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

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

结果:

adj = (week_day - temp.weekday()) % 7
Run Code Online (Sandbox Code Playgroud)

此外,在第二个版本中,您需要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)
Run Code Online (Sandbox Code Playgroud)


JGF*_*FMK 5

获得最多票数的一句台词的问题是它不起作用。

然而,它可以用作细化的基础:

你看到这就是你得到的:

c = calendar.Calendar(calendar.SUNDAY).monthdatescalendar(2018, 7)
for c2 in c:
    print(c2[0])
 
Run Code Online (Sandbox Code Playgroud)
2018-07-01
2018-07-08
2018-07-15
2018-07-22
2018-07-29
Run Code Online (Sandbox Code Playgroud)
c = calendar.Calendar(calendar.SUNDAY).monthdatescalendar(2018, 8)
for c2 in c:
    print(c2[0])
Run Code Online (Sandbox Code Playgroud)
2018-07-29
2018-08-05
2018-08-12
2018-08-19
2018-08-26
Run Code Online (Sandbox Code Playgroud)

如果您考虑一下,它会尝试将日历组织到嵌套列表中,以便一次打印一周的日期。因此其他月份的掉队者开始发挥作用。通过使用该月有效天数的新列表 - 这就达到了目的。


回答并附上清单

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]
Run Code Online (Sandbox Code Playgroud)

所以它的调用方式如下:

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)
Run Code Online (Sandbox Code Playgroud)

这是输出:

2018-07-01 
2018-08-05
Run Code Online (Sandbox Code Playgroud)

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 in [-1,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]
Run Code Online (Sandbox Code Playgroud)