Python:知道下个月的第二个星期三,给定日期

时间:2011-08-11 11:26:23

标签: python calendar date

我想知道这个:

我有这样的日期:

2011-08-10 wednesday

我想知道下个月的下一个星期三: 答案应该是2011-09-14 wednesday

6 个答案:

答案 0 :(得分:9)

在评论中解释说OP正在寻找一个映射

的函数
  1. 2011-08-25(第四个星期四)至2011-09-22(第四个星期四) 下个月)和
  2. 2011-08-30(第五个星期二)至2011-09-27(第四个星期二, 因为九月没有第五个星期二。)

  3. 使用dateutil

    import datetime
    import dateutil.relativedelta as relativedelta
    
    def next_month(date):
        weekday=relativedelta.weekday(date.isoweekday()-1)   
        weeknum=(date.day-1)//7+1
        weeknum=weeknum if weeknum<=4 else 4
        next_date=date+relativedelta.relativedelta(
            months=1,day=1,weekday=weekday(weeknum))
        return next_date
    
    start=datetime.date(2011,8,1)
    for i in range(31):
        date=start+datetime.timedelta(days=i)
        next_date=next_month(date)    
        print('{d} --> {n}'.format(d=date,n=next_date))
    

    产量

    2011-08-01 --> 2011-09-05
    2011-08-02 --> 2011-09-06
    2011-08-03 --> 2011-09-07
    2011-08-04 --> 2011-09-01
    2011-08-05 --> 2011-09-02
    2011-08-06 --> 2011-09-03
    2011-08-07 --> 2011-09-04
    2011-08-08 --> 2011-09-12
    2011-08-09 --> 2011-09-13
    2011-08-10 --> 2011-09-14
    2011-08-11 --> 2011-09-08
    2011-08-12 --> 2011-09-09
    2011-08-13 --> 2011-09-10
    2011-08-14 --> 2011-09-11
    2011-08-15 --> 2011-09-19
    2011-08-16 --> 2011-09-20
    2011-08-17 --> 2011-09-21
    2011-08-18 --> 2011-09-15
    2011-08-19 --> 2011-09-16
    2011-08-20 --> 2011-09-17
    2011-08-21 --> 2011-09-18
    2011-08-22 --> 2011-09-26
    2011-08-23 --> 2011-09-27
    2011-08-24 --> 2011-09-28
    2011-08-25 --> 2011-09-22 # Oddly non-monotonic, but correct according to specifications
    2011-08-26 --> 2011-09-23
    2011-08-27 --> 2011-09-24
    2011-08-28 --> 2011-09-25
    2011-08-29 --> 2011-09-26 # 5th Monday maps to 4th Monday since there is no 5th Monday in September
    2011-08-30 --> 2011-09-27
    2011-08-31 --> 2011-09-28
    

答案 1 :(得分:1)

为什么你明确需要第二个星期三? 如果您知道当月的工作日位置,那将更有价值

答案 2 :(得分:1)

from time import *
import re
from sys import exit


dico = {'Monday':0,'monday':0,'Tuesday':1,'tuesday':1,
        'Wednesday':2,'wednesday':2,'Thursday':3,'thursday':3,
        'Friday':4,'friday':4,'Saturday':5,'saturday':5,
        'Sunday':6,'sunday':6}

regx = re.compile('((\d{4})-(\d\d)-(\d\d))\s+(%s)' % '|'.join(dico.iterkeys()))



for s in ('2011-08-10  Wednesday', '2011-08-25    Thursday', '2011-08-30  Tuesday',
          '2011-12-04 Sunday', '2011-12-30   Friday',
          '2012-02-18 Saturday', '2012-02-25 Saturday', '2012-02-29   Wednesday '):

    print 's == ' + s

    try:
        the_date,y,m,d,day_name = regx.match(s).groups()
        wday = dico[day_name]
    except:
        mess = "The string isn't expressing a date correctly"
        exit(mess)

    try:
        s_strp = strptime(the_date,'%Y-%m-%d')
    except:
        mess = "The date isn't an existing date"
        exit(mess)

    if s_strp.tm_wday != wday:
        mess = 'The name of week-day in the string is incoherent with the date'
        exit(mess)

    n = (int(d)-1)//7
    print '(y,m,d,day_name,wday,n) ==',(y,m,d,day_name,wday,n)
    print 'The day %s is the %sth %s in the month %s\n' % (d,n+1,day_name,y+'-'+m)


    yp,mp = (int(y)+1, 1) if m=='12' else (int(y), int(m)+1)
    next_month = ('%s-%s-%s' % (yp,mp,dp) for dp in xrange(1,32))
    same_days = []
    for day in next_month:
        try:
            if strptime(day,'%Y-%m-%d').tm_wday==wday:  same_days.append(day)
        except:
            break
    print '%s days in the next month are :\n%s' % (day_name,same_days)
    try:
        print 'The %sth %s in the next month is on date %s' % (n+1,day_name,same_days[n])
    except:
        print 'The last %s (%sth) in the next month is on date %s' % (day_name,n,same_days[n-1])
    print '\n-------------------------------------------------------------'

结果

s == 2011-08-10  Wednesday
(y,m,d,day_name,wday,n) == ('2011', '08', '10', 'Wednesday', 2, 1)
The day 10 is the 2th Wednesday in the month 2011-08

Wednesday days in the next month are :
['2011-9-7', '2011-9-14', '2011-9-21', '2011-9-28']
The 2th Wednesday in the next month is on date 2011-9-14

-------------------------------------------------------------
s == 2011-08-25    Thursday
(y,m,d,day_name,wday,n) == ('2011', '08', '25', 'Thursday', 3, 3)
The day 25 is the 4th Thursday in the month 2011-08

Thursday days in the next month are :
['2011-9-1', '2011-9-8', '2011-9-15', '2011-9-22', '2011-9-29']
The 4th Thursday in the next month is on date 2011-9-22

-------------------------------------------------------------
s == 2011-08-30  Tuesday
(y,m,d,day_name,wday,n) == ('2011', '08', '30', 'Tuesday', 1, 4)
The day 30 is the 5th Tuesday in the month 2011-08

Tuesday days in the next month are :
['2011-9-6', '2011-9-13', '2011-9-20', '2011-9-27']
The last Tuesday (4th) in the next month is on date 2011-9-27

-------------------------------------------------------------
s == 2011-12-04 Sunday
(y,m,d,day_name,wday,n) == ('2011', '12', '04', 'Sunday', 6, 0)
The day 04 is the 1th Sunday in the month 2011-12

Sunday days in the next month are :
['2012-1-1', '2012-1-8', '2012-1-15', '2012-1-22', '2012-1-29']
The 1th Sunday in the next month is on date 2012-1-1

-------------------------------------------------------------
s == 2011-12-30   Friday
(y,m,d,day_name,wday,n) == ('2011', '12', '30', 'Friday', 4, 4)
The day 30 is the 5th Friday in the month 2011-12

Friday days in the next month are :
['2012-1-6', '2012-1-13', '2012-1-20', '2012-1-27']
The last Friday (4th) in the next month is on date 2012-1-27

-------------------------------------------------------------
s == 2012-02-18 Saturday
(y,m,d,day_name,wday,n) == ('2012', '02', '18', 'Saturday', 5, 2)
The day 18 is the 3th Saturday in the month 2012-02

Saturday days in the next month are :
['2012-3-3', '2012-3-10', '2012-3-17', '2012-3-24', '2012-3-31']
The 3th Saturday in the next month is on date 2012-3-17

-------------------------------------------------------------
s == 2012-02-25 Saturday
(y,m,d,day_name,wday,n) == ('2012', '02', '25', 'Saturday', 5, 3)
The day 25 is the 4th Saturday in the month 2012-02

Saturday days in the next month are :
['2012-3-3', '2012-3-10', '2012-3-17', '2012-3-24', '2012-3-31']
The 4th Saturday in the next month is on date 2012-3-24

-------------------------------------------------------------
s == 2012-02-29   Wednesday 
(y,m,d,day_name,wday,n) == ('2012', '02', '29', 'Wednesday', 2, 4)
The day 29 is the 5th Wednesday in the month 2012-02

Wednesday days in the next month are :
['2012-3-7', '2012-3-14', '2012-3-21', '2012-3-28']
The last Wednesday (4th) in the next month is on date 2012-3-28

-------------------------------------------------------------

使用字符串'2011-08-11 Monday',结果是:

Traceback (most recent call last):
  File "I:\wednesday.py", line 37, in <module>
    exit(mess)
SystemExit: The name of week-day in the string is incoherent with the date

使用字符串'2011-34-58 Monday',会产生错误:

Traceback (most recent call last):
  File "I:\wednesday.py", line 33, in <module>
    exit(mess)
SystemExit: The date isn't an existing date

编辑1

我对我的代码不满意:它的可读性差 以下是更容易理解的

请注意,在此新代码中, N 与前代码中的 n 含义不同

from time import strptime
import re
from sys import exit
from datetime import date,timedelta


dico = {'Monday':0,'monday':0,'Tuesday':1,'tuesday':1,
        'Wednesday':2,'wednesday':2,'Thursday':3,'thursday':3,
        'Friday':4,'friday':4,'Saturday':5,'saturday':5,
        'Sunday':6,'sunday':6}

regx = re.compile('((\d{4})-(\d\d)-(\d\d))\s+(%s)' % '|'.join(dico.iterkeys()))



for s in ('2011-08-10  Wednesday', '2011-08-25    Thursday', '2011-08-30  Tuesday',
          '2011-12-04 Sunday', '2011-12-30   Friday',
          '2012-02-18 Saturday', '2012-02-25 Saturday', '2012-02-29   Wednesday ',
          '2011-07-24 sunday', '2011-07-25 monday',
          '2011-10-28 friday', '2011-10-30 monday'):

    print 's == ' + s

    # Verifications ----------------------------------------------------------------
    try:
        the_date,y,m,d,day_name = regx.match(s).groups()
        wday = dico[day_name]
    except:
        mess = "The string isn't expressing a date correctly"
        exit(mess)
    else:
        try:
            s_strp = strptime(the_date,'%Y-%m-%d')
        except:
            mess = "The date isn't an existing date"
            exit(mess)
        else:
            if s_strp.tm_wday != wday:
                mess = 'The name of week-day in the string is incoherent with the date'
                exit(mess)

    # Extraction of needed info -----------------------------------------------------
    y,m,d = map(int,(y,m,d))  # y,m,d = year,month,day
    N = (d-1)//7 + 1  # N is in (1,2,3,4,5) , it tells if the week-day is the first/2nd/3nd/4th/5th in the month 
    print '(y,m,d,day_name,wday,N) ==',(y,m,d,day_name,wday,N)
    print 'The day %s is the %sth %s in the month %s-%s\n' % (d,N,day_name,y,m)

    # Finding the desired next date ------------------------------------------------- 
    ahead = (date(y,m,d) + timedelta(weeks=i) for i in (1,2,3,4,5))
    # this generator yields the 5 next dates of same week-day name after the date 'y-m-d' because the date 'y-m-d'
    # and the date of same week-day name and same position in the next month can't be separated by more than 5 weeks

    cnt = 0
    for xd in ahead:
        cnt += (xd.month != m) # cnt is incremented only if xd is a same week-day in the next month
        if cnt in (N,4):
            # There is no couple of adjacent months in a year
            # having a given week-day name present 5 times in each of them
            # Then if N==5, cnt can't be 5
            print 'The %sth %s %s in the next month is on date %s' % (cnt,day_name,'(the last)' if N==5 else '',xd)
            break
    print '\n-------------------------------------------------------------'

编辑2

Aaaaaaaaah!我感觉结果可以在很少的线条中找到,我终于得到了它 不需要dateutil,不需要我复杂的先前解决方案,下面的代码可以完成4行的工作!

import re
from sys import exit
from datetime import date,timedelta
from time import strptime


dico = {'Monday':0,'monday':0,'Tuesday':1,'tuesday':1,
        'Wednesday':2,'wednesday':2,'Thursday':3,'thursday':3,
        'Friday':4,'friday':4,'Saturday':5,'saturday':5,
        'Sunday':6,'sunday':6}

regx = re.compile('((\d{4})-(\d\d)-(\d\d))\s+(%s)' % '|'.join(dico.iterkeys()))



for s in ('2011-08-10  Wednesday', '2011-08-25    Thursday', '2011-08-30  Tuesday',
          '2011-12-04 Sunday', '2011-12-30   Friday',
          '2012-02-18 Saturday', '2012-02-25 Saturday', '2012-02-29   Wednesday ',
          '2011-07-24 sunday', '2011-07-25 monday',
          '2011-10-28 friday', '2011-10-30 monday'):

    print 's == ' + s

    # --- Verifications ----------------------------------------------------------------
    mess = ("The string isn't expressing a date correctly",
            "The date isn't an existing date",
            'The name of week-day in the string is incoherent with the date')   
    try:
        the_date,y,m,d,weekday_name = regx.match(s).groups()    
    except:
        exit(mess[0])
    else:
        try:
            y,m,d = map(int,(y,m,d))
            xdate = date(y,m,d)
        except:
            exit(mess[1])
        else:
            if strptime(the_date,'%Y-%m-%d').tm_wday != dico[weekday_name]:
                exit(mess[2])


    # --- Extraction of the position of the day in the month ----------------------------
    n = (d-1)//7
    # n is in (0,1,2,3,4) , it tells the position of the input day
    # among the list of same week-days in the month


    # --- Going to the first next same week-day in the next month------------------------
    while xdate.month == m:
        xdate = xdate + timedelta(weeks=1)
    # this loop makes xdate to be incremented of one week until reaching the next month


    # --- Displaying the extracted data and the result ----------------------------------
    print '(y,m,d,weekday_name,dico[weekday_name],n) ==',(y,m,d,weekday_name,dico[weekday_name],n)
    print 'The day %s is the %sth %s in the month %s-%s\n' % (d,n+1,weekday_name,y,m)
    # There is no couple of adjacent months in a year having a given week-day name
    # present 5 times (that is to say at position 4) in each of them.
    # Then if n==4, the desired next date can't be xdate + timedelta(weeks=4))
    print 'The %sth %s %s in the next month is on date %s' \
          % (min(n,3)+1,weekday_name,'(the last)' if n==4 else '',xdate + timedelta(weeks=min(n,3)))


    print '\n-------------------------------------------------------------'

编辑3

考虑到 unutbu 的注释,发现我的代码不清楚有必要写timedelta(weeks=min(n,3))的原因,我试图找到另一种算法来避免这个min(n, 3)指示 但我终于意识到,当下个月无法找到第5个同一个工作日时,没有算法可以自己决定它必须做什么。对于代码的编写者来说,没有其他选择能够实现这种情况,并决定采用第4个工作日而不是第5个工作日。

所以我保留了我的代码的一般原则。但是我稍微改了一下,使用 weekday_appear 值而不是前者 n 更容易理解。我认为以下容易理解的代码是更清晰,更简单的代码。

for s in ('2011-08-10  Wednesday', '2011-08-25 Thursday', '2011-08-30  Tuesday',
          '2011-12-04 Sunday', '2011-12-30   Friday',
          '2012-02-18 Saturday', '2012-02-25 Saturday', '2012-02-29 Wednesday ',
          '2011-07-24 sunday', '2011-07-25 monday',
          '2011-10-28 friday', '2011-10-30 monday'):

    print 's == ' + s

    # --- Verifications --------------------------------------------------------
    mess = ("The string isn't expressing a date correctly",
            "The date isn't an existing date",
            'The name of week-day in the string is incoherent with the date')   
    try:
        the_date,y,m,d,wkd_name = regx.match(s).groups()
        print '(y,m,d,wkd_name) ==',(y,m,d,wkd_name)
    except:
        exit(mess[0])
    else:
        try:
            y,m,d = map(int,(y,m,d))
            xdate = date(y,m,d)
        except:
            exit(mess[1])
        else:
            if xdate.weekday() != dico[wkd_name]:
                exit(mess[2])


    # --- Extraction of the number of the weekday in the month ---------------
    weekday_appear = (d+6)//7
    print 'weekday_appear == %s' % weekday_appear
    # weekday_appear is 1 if 1<=d<=7, 2 if 8<=d<=14 ... 5 if 29<=d<=31
    # It tells if the input weekday is the 1st/2nd/3rd/4th/5th
    # among the list of same weekdays in the month


    # --- Going to the last same weekday in the month-------------------------
    for deltadays in (7,14,21,28):
        try:
            xdate= date(y,m,d+deltadays)
        except:
            break
    # after this loop, xdate is the last date in the month having the same
    # weekday name as input date


    # --- Displaying the result ----------------------------------------------
    # There is no couple of adjacent months in a year in which a given weekday
    # name can be present 5 times in each of the two months .
    # Then, when it happens that wkd_appear==5 in the input month, it is sure
    # that the desired weekday in the next month can't be the 5th one in this
    # month. In the case, we will take the last of the dates with same weekday
    # name in the next month, that is to say the 4th such date.
    # That's the reason of the following reduction:
    next_appear = min(4, weekday_appear)
    # By the way, the fact that every month in a year have a minimum of 4*7
    # days is the reason why it is sure to find at least 4 times in a month
    # any weekday name, whatever which one it is.

    print 'The %sth %s %s in the next month is on date %s' \
          % (next_appear, wkd_name, ('','(the last)')[weekday_appear==5],
             xdate + timedelta(weeks=next_appear))


    print '\n-------------------------------------------------------------'

答案 3 :(得分:0)

如果您当前的日期也是第二个星期三,那么您可以使用timedelta,我猜:

from datetime import datetime, timedelta
d = datetime(2011,8,10)
newDate = (d + timedelta(weeks=4))# 4 weeks to next month
if newDate.weekday() == 2 and newDate.day < 8:
    newDate = (d + timedelta(days=1))        
    while newDate.weekday() != 2:
        newDate = (d + timedelta(days=1))                 

其他类似的事情:

from datetime import datetime, timedelta

d = datetime(2011,8,10)
t = timedelta(days=1)
newDate = d+t

wedCount = 0

while wedCount != 2:
    newDate += t
    if abs(newDate.month-d.month) == 1 and newDate.weekday() == 2:
        wedCount += 1    

答案 4 :(得分:0)

如果您不关心当月的第二个星期三,您应该找到下个月的第8天,而当天不是星期三,请添加一天。

#!/usr/bin/env python
import datetime                                                                 
from datetime import timedelta                                                  
origDate = datetime.date(2011,8,10)                                              
oneday = timedelta(days=1)                                                      
WEDNESDAY = 2                                                                   
nextMonth = (origDate.month % 12) + 1                                           
newDate = datetime.date(origDate.year, nextMonth, 8)                            
while newDate.weekday() != WEDNESDAY:                                           
    newDate += oneday                                                           
print newDate

给出了

2011-09-14

可以通过将其替换为:

来优化最后一个循环
newDate += timedelta(days=(7 + WEDNESDAY - newDate.weekday()) % 7)

答案 5 :(得分:0)

您可以在 Pandas 中使用 WeekOfMonth 偏移量。

假设您从第一个月的第二个星期三或下个月的第二个星期三之前的任何后续日期开始:

import pandas as pd

#Create a starting timestamp:
d1 = pd.Timestamp('2011-08-10')

#Add on a WeekOfMonth offset for the second week of month, and the third weekday:
d2 = d1 + pd.offsets.WeekOfMonth(week=1,weekday=2)

print(f'{d2:%Y-%m-%d %A}')
>>> 2011-09-14 Wednesday

有关更多选项,请参阅 pandas.tseries.offsets.WeekOfMonth 的文档: https://pandas.pydata.org/pandas-docs/version/1.0.1/reference/api/pandas.tseries.offsets.WeekOfMonth.html