在给定日期后找到第一个星期一的日期

97

给定一个特定的日期,例如2011-07-02,我如何找到该日期之后的下一个星期一(或任何工作日)的日期?


2
http://docs.python.org/library/datetime.html#datetime.date.weekday - Juan Gomez
13个回答

167
import datetime
def next_weekday(d, weekday):
    days_ahead = weekday - d.weekday()
    if days_ahead <= 0: # Target day already happened this week
        days_ahead += 7
    return d + datetime.timedelta(days_ahead)

d = datetime.date(2011, 7, 2)
next_monday = next_weekday(d, 0) # 0 = Monday, 1=Tuesday, 2=Wednesday...
print(next_monday)

9
你也可以使用((day - today) + 7) % 7来获取日期差。 - jstaab
6
简化为(day - today) % 7。在减法中出现的负数将按您的预期进行处理。例如,-1 % 7的结果为6 - Code-Apprentice

74

以下是一个简洁且通用的替代方案,与上面略微复杂的答案相比。

def onDay(date, day):
    """
    Returns the date of the next given weekday after
    the given date. For example, the date of next Monday.

    NB: if it IS the day we're looking for, this returns 0.
    consider then doing onDay(foo, day + 1).
    """
    days = (day - date.weekday() + 7) % 7
    return date + datetime.timedelta(days=days)

2
  1. 如果日期和date.weekday()的星期匹配,则返回相同的日期。
  2. day应该采用weekday()格式,而不是isoweekday()。
- bhtabor
10
功能正常,但在使用 %7 进行计算时,不需要 +7。可以改为:onDay = lambda date, day: date + datetime.timedelta(days=(day-date.weekday())%7) - Zach Siegel
3
此外,由于Python的datetime包中有一个date类(我个人经常导入),所以我将date视为关键字,不给变量命名为date。我会将代码更改为:onDay = lambda dt, day: dt + datetime.timedelta(days=(day-dt.weekday())%7),以提醒自己输入的格式应该是Python的datetime.date变量。(同样地,在Javascript中,date是一个标准类型,你不应该将变量命名为date。) - Zach Siegel
1
针对bhtabor的问题:1. 为了确保返回下周的星期一,即使date本身就是星期一,更改为 onDay = lambda date, day: date + datetime.timedelta(days=(day-date.weekday()-1)%7+1)。不太重要的是:2. 如果day应该是值1..7而不是0..6表示星期一..星期日,则将date.weekday()更改为date.isoweekday() - ElRudi

30

试一试

>>> dt = datetime(2011, 7, 2)
>>> dt + timedelta(days=(7 - dt.weekday()))
datetime.datetime(2011, 7, 4, 0, 0)

使用Python的datetime类型,星期一用0表示,星期日用6表示。根据这个规则,下一个星期一是当前星期一后7天,星期二后6天,以此类推。


有趣的是 - 当我尝试使用 "datetime(2013, 1, 1) + timedelta(days=(7 - datetime(2013, 1, 1).weekday()))" 查找2013年1月1日之后的下一个星期天时,我得到的结果是 "datetime.datetime(2013, 1, 7, 0, 0)",这是一个星期一。 - fixxxer
2
@fixxxer - 正如答案中所述:代码使用了datetime API的一些属性,这些属性只适用于查找星期一。由phihag提供的代码更通用,可以查找任何即将到来的星期几。 - Dirk
你认为在timedelta中使用6而不是7会有帮助吗? - fixxxer
@fixxxer:为什么不直接使用phihag提供的代码呢?它可以实现你想要的功能:next_weekday(datetime.date(2013,1,1), 6)datetime.date(2013, 1, 6),也就是星期天。我的代码在(滥)用Python的datetime API的某些属性方面很“可爱”,只回答了“找到下一个星期一”这个问题,但不足以回答“找到下一个xxx日”的问题。 - Dirk
@fixxxer:使用“可爱”的代码查找下一个星期日:d + timedelta(6 - d.weekday() or 13 - d.weekday())(如果Sunday == 6是一周的最后一天,则等同于phihag的代码)。 - jfs

9
这是关于在环(mod 7)中进行计算的示例。
import datetime


def next_day(given_date, weekday):
    day_shift = (weekday - given_date.weekday()) % 7
    return given_date + datetime.timedelta(days=day_shift)

now = datetime.date(2018, 4, 15) # sunday
names = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday',    
         'saturday', 'sunday']
for weekday in range(7):
    print(names[weekday], next_day(now, weekday))

将会打印:

monday 2018-04-16
tuesday 2018-04-17
wednesday 2018-04-18
thursday 2018-04-19
friday 2018-04-20
saturday 2018-04-21
sunday 2018-04-15

正如您所看到的,它正确地给出了下周一、二、三、四、五和六。它也理解2018-04-15是星期天,并返回当前星期天而不是下一个。

我相信在7年后,您会发现这个答案非常有帮助;-)


9

另一种选择是使用rrule。

from dateutil.rrule import rrule, WEEKLY, MO
from datetime import date

next_monday = rrule(freq=WEEKLY, dtstart=date.today(), byweekday=MO, count=1)[0]

rrule文档:https://dateutil.readthedocs.io/en/stable/rrule.html


这是关于it技术的rrule文档,详细介绍了如何使用该工具来处理日期和时间。如果您需要更多帮助,请查看以上链接。

这就是我需要的答案。relativedelta 版本在给定日期之后的时间上运行良好。例如,当请求下一个星期一的 11:00,并且开始时间是星期一的 13:00 时,relativedelta 仍会返回过去的当天的 11:00 - 这个时间已经过去了。另一方面,rrule 查找 dtstart 之后的下一个实例。因此,对于 dtstart 是星期一的 10:00 的情况,它将正确返回星期一的 11:00,并且对于 dtstart 是星期一的 13:00 的情况,它将返回_下一个_星期一的 11:00。 - Nick K9
绝对是最好的答案,非常感谢。 - quartin

7
另一个简单而优雅的解决方案是使用pandas偏移量。
我发现在处理日期时非常有用和强大。
- 如果您想要第一个星期日,只需将频率修改为freq ='W-SUN'。 - 如果您想要接下来的几个星期日,请更改offsets.Day(days)。 - 使用pandas偏移量可以让您忽略节假日,仅使用工作日等等。
您还可以使用“apply”方法轻松地将此方法应用于整个DataFrame。
import pandas as pd
import datetime

# 1. Getting the closest monday from a given date
date = datetime.date(2011, 7, 2)
closest_monday = pd.date_range(start=date, end=date + pd.offsets.Day(6), freq="W-MON")[
    0
]

# 2. Adding a 'ClosestMonday' column with the closest monday for each row in
# a pandas df using apply. Requires you to have a 'Date' column in your df
def get_closest_monday(row):
    return pd.date_range(
        start=row.Date, end=row.Date + pd.offsets.Day(6), freq="W-MON"
    )[0]


df = pd.DataFrame([datetime.date(2011, 7, 2)], columns=["Date"])
df["ClosestMonday"] = df.apply(lambda row: get_closest_monday(row), axis=1)
print(df)

是的,date_range + W-MON + offsets.Day(6) 是查询“过去固定日期和现在之间的每个星期一”的最佳解决方案,而且完全不需要手动调整工作日。感谢!这个答案应该有更多的投票。 - ojdo
offsets in this example should be pd.offsets - henrycjc

5
你可以开始将一天添加到日期对象中,并在星期一停止。
>>> d = datetime.date(2011, 7, 2)
>>> while d.weekday() != 0: #0 for monday
...     d += datetime.timedelta(days=1)
... 
>>> d
datetime.date(2011, 7, 4)

5

dateutil 有一个特殊的功能来执行这种操作,这是我迄今为止见过的最优雅的方式。

from datetime import datetime
from dateutil.relativedelta import relativedelta, MO

first_monday_date = (datetime(2011,7,2) + relativedelta(weekday=MO(0))).date()

如果您需要日期时间,只需:

first_monday_date = datetime(2011,7,2) + relativedelta(weekday=MO(0))

3
import datetime

d = datetime.date(2011, 7, 2)
while d.weekday() != 0:
    d += datetime.timedelta(1)

1
weekday = 0 ## Monday
dt = datetime.datetime.now().replace(hour=0, minute=0, second=0) ## or any specific date
days_remaining = (weekday - dt.weekday() - 1) % 7 + 1
next_dt = dt + datetime.timedelta(days_remaining)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接