Python: знать вторую среду следующего месяца с указанной датой - PullRequest
4 голосов
/ 11 августа 2011

Я хотел бы знать это:

У меня есть, например, эта дата:

2011-08-10 wednesday

, и я хотел бы знать следующую вторую среду следующего месяца: Ответ долженбыть 2011-09-14 wednesday.

Ответы [ 5 ]

9 голосов
/ 11 августа 2011

В комментариях было объяснено, что ОП ищет функцию, которая отображает

  1. 2011-08-25 (четвертый четверг) до 2011-09-22 (четвертый четверг следующего месяца) и
  2. 2011-08-30 (пятый вторник) - 2011-09-27 (четвертый вторник, потому что в сентябре нет пятого вторника.)

Использование 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 голос
/ 11 августа 2011
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 Понедельник' результат:

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

Аааааааааа! У меня было чувство, что результат может быть найден в очень немногих строках, и я, наконец, получил его.
Нет необходимости в 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) .
Но в конце концов я понял, что ни один алгоритм не может решить сам, что он должен делать, когда невозможно найти пятый день недели в следующем месяце. У автора кода нет другого выбора, чтобы тот, кто понимает, что этот случай существует, и решил принять 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-------------------------------------------------------------'
1 голос
/ 11 августа 2011

Зачем вам явно нужна вторая среда?Было бы более ценно, если бы вы знали положение дня недели в месяце

0 голосов
/ 11 августа 2011

Если вас не волнует вторая среда в текущем месяце, вы должны найти 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)
0 голосов
/ 11 августа 2011

Если ваша текущая дата также является второй средой, чем вы можете использовать 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    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...