我正在尝试计算给定日期的第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)
你的问题在这里:
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)
获得最多票数的一句台词的问题是它不起作用。
然而,它可以用作细化的基础:
你看到这就是你得到的:
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)
| 归档时间: |
|
| 查看次数: |
3901 次 |
| 最近记录: |