简体   繁体   中英

Python: Number of the Week in a Month

When specifying a date:

datetime.datetime(2011, 8, 15)

How can i get to know that this is the third week of the month?

What if I want this dates?

datetime.datetime(2011, 2, 28) //should return 4
datetime.datetime(2011, 8, 29) //should return 5

I understand that only February has 4 weeks if the given date is in a leap year (bissextile)

The modulo-style approach shown in other answers can be misleading. Imagine weeks in a year. THere are 52 chunks of 7 days in any 365 day year, with one day left over. So if for my first year, the 52nd week ends on Dec 30, and I have Dec 31 left to worry about.

I could either consider that there are 53 weeks in the year, and have the 53rd week be 31 Dec, 1 Jan, 2 Jan, 3 Jan... Or, more conventionally, I consider that the first week of the next year actually begins on Dec 31. This is how your diary pocket book does it.

Of course this means that the next year the 52nd week ends not on Dec 30 now, but Dec 29. And each year it creeps back one day at a time, until the 6th year where we have moved the end of 52nd week back 6 days (and thrown in a leap day for good measure) so the whole of the 1st week of 2017 would be contained in the year 2016, and that would be silly. So 2016 will have 53 weeks in it.

The exact same logic applies to months, however it can be harder to spot. Unfortunately you choose August 2011 which has a neat arrangement of starting the 1st of the month on a monday.

>>> print calendar.month(2011,8)
    August 2011
Mo Tu We Th Fr Sa Su
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

>>> print calendar.month(2011,9)
   September 2011
Mo Tu We Th Fr Sa Su
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30

29 of Aug is in 5th week of Aug, but with same logic, the 1st, 2nd 3rd 4th of Sept would also be in the 5th week of Aug, and so cannot be in 1st week of Sept.

So 29 Sept would be, by the simple divide number of days by 7 approach, be in 5th week of September, but looking at calendar above, if 1-4 of sept are in August, then 29 Sept is in 4th week of Sept.

It all depends on your defintion of when a week begins.

import datetime
import calendar

# I am assuming that the first week of a month starts with the first monday of a month...
#I *think* my logic is OK - if Monday (0) is the start of the week, then
#any dayof the month minus its own day of week (0,1,2...) must be positive
#if that day is on or after the first monday of the month

def week_of_month(tgtdate):

    days_this_month = calendar.mdays[tgtdate.month]
    for i in range(1, days_this_month):
        d = datetime.date(tgtdate.year, tgtdate.month, i)
        if d.day - d.weekday() > 0:
            startdate = d
            break
    # now we canuse the modulo 7 appraoch
    return (tgtdate - startdate).days //7 + 1

tgtdates = [datetime.date(2011, 8, 29),
            datetime.date(2011, 8, 1)
            ]

for tgtdate in tgtdates:
    print tgtdate,
    print "is in week %s" % week_of_month(tgtdate)

print calendar.month(tgtdate.year,tgtdate.month)


 # 2011-09-29 is in week 4
 # 2011-09-01 is in week 0
 #    September 2011
 # Mo Tu We Th Fr Sa Su
 #           1  2  3  4
 #  5  6  7  8  9 10 11
 # 12 13 14 15 16 17 18
 # 19 20 21 22 23 24 25
 # 26 27 28 29 30

 # 2011-08-29 is in week 5
 # 2011-08-01 is in week 1
 #     August 2011
 # Mo Tu We Th Fr Sa Su
 #  1  2  3  4  5  6  7
 #  8  9 10 11 12 13 14
 # 15 16 17 18 19 20 21
 # 22 23 24 25 26 27 28
 # 29 30 31

Above, week 0 means the week is not considered part of this month. So 1st sept is in the 5th week of august.

NB

I would like to comment on @unutbu answer but I dont have enough points I guess. The modulo 7 approach fails in sept 2011.

>>> d = datetime.date(2011,9,28)
>>> (d.day-1)//7+1
4
>>> d = datetime.date(2011,9,1)
>>> 
>>> (d.day-1)//7+1
1

from the above calendar, 1st of sept is in week one, but that means the 28th cannot be in week 4 - it must be either in week 5, or 1st is in week 0...

In [115]: d=datetime.datetime(2011, 2, 28)

In [116]: (d.day-1)//7+1
Out[116]: 4

In [117]: d=datetime.datetime(2011, 8, 29)

In [118]: (d.day-1)//7+1
Out[118]: 5

Maybe http://labix.org/python-dateutil will help.

Other than that it's just math.

 from datetime import datetime, timedelta

    def week_of_month(date):
        month = date.month
        week = 0
        while date.month == month:
            week += 1
            date -= timedelta(days=7)

        return week

This (somewhat clumsy) example, using the calendar module...

import calendar
import datetime

def week(dt):
    mth = calendar.monthcalendar(dt.year, dt.month)
    for i, wk in enumerate(mth):
        if dt.day in wk:
            return i + 1

calendar.setfirstweekday(calendar.MONDAY)

week(datetime.datetime(2011, 8, 15)) # 3
week(datetime.datetime(2011, 2, 28)) # 5
week(datetime.datetime(2011, 8, 29)) # 5

... gets the answer wrong for 2011-02-28, based on the OP's expectation, unless weeks start on Tuesday.

How about this. We give the date that we wish to find the week for. We reset the date to the first day of the month in a new variable. We then create the week in the year for both the first day and our given date. Subtract the first isoweek from the given date's isoweek and add one. You add one to correct the zero index number. This should give the week number in the month.

import datetime

def _get_week_of_day(date):
    first_day = date.replace(day=1)
    iso_day_one = first_day.isocalendar()[1]
    iso_day_date = date.isocalendar()[1]
    adjusted_week = (iso_day_date - iso_day_one) + 1
    return adjusted_week

To take into account the comment of @Usagi:

datetime.datetime(2011, 8, 15) // should return 3
datetime.datetime(2011, 2, 28) // should return 5
datetime.datetime(2011, 8, 29) // should return 5

The following equation manage that the first day of a month is not necessary a monday:

(d.day-d.weekday() -2)//7+2

The -2 and +2 allow to get a week number between 1 and 6

d = datetime.datetime.today()
year = d.year
month = d.month
last_day = calendar.monthrange(year,month)[1]
d = datetime.datetime(year, month, last_day)
total_week = (d.day-1)//7+1
print(total_week)

Check answer here: https://stackoverflow.com/a/64192858/6089311

import pandas as pd

def weekinmonth(dates):
    """Get week number in a month.
    
    Parameters: 
        dates (pd.Series): Series of dates.
    Returns: 
        pd.Series: Week number in a month.
    """
    firstday_in_month = dates - pd.to_timedelta(dates.dt.day - 1, unit='d')
    return (dates.dt.day-1 + firstday_in_month.dt.weekday) // 7 + 1
    

df = pd.DataFrame(pd.date_range(' 1/ 1/ 2000', periods = 100, freq ='D'), columns=['Date'])
weekinmonth(df['Date'])
testdate = datetime.datetime(2011, 2, 28)
weekofmonth = (testdate.day+7-1)/7

In general to find the ceiling of division: ceiling of (x/y) = (x+y-1)/y

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM