Does python offer a way to easily get the current week of the month (1:4) ?
Does python offer a way to easily get the current week of the month (1:4) ?
def week_of_month(date_value):
week = date_value.isocalendar()[1] - date_value.replace(day=1).isocalendar()[1] + 1
return date_value.isocalendar()[1] if week < 0 else week
date_value should in timestamp format This will give the perfect answer in all the cases. It is purely based on ISO calendar
Josh' answer seems the best but I think that we should take into account the fact that a week belongs to a month only if its Thursday falls into that month. At least that's what the iso says.
According to that standard, a month can have up to 5 weeks. A day could belong to a month, but the week it belongs to may not.
I have taken into account that just by adding a simple
if (first_day.weekday()>3) :
return ret_val-1
else:
return ret_val
where ret_val is exactly Josh's calculated value. Tested on June 2017 (has 5 weeks) and on September 2017. Passing '2017-09-01' returns 0 because that day belongs to a week that does not belong to September.
The most correct way would be to have the method return both the week number and the month name the input day belongs to.
A variation on @Manuel Solorzano's answer:
from calendar import monthcalendar
def get_week_of_month(year, month, day):
return next(
(
week_number
for week_number, days_of_week in enumerate(monthcalendar(year, month), start=1)
if day in days_of_week
),
None,
)
E.g.:
>>> get_week_of_month(2020, 9, 1)
1
>>> get_week_of_month(2020, 9, 30)
5
>>> get_week_of_month(2020, 5, 35)
None
Say we have some month's calender as follows:
Mon Tue Wed Thur Fri Sat Sun
1 2 3
4 5 6 7 8 9 10
We say day 1 ~ 3 belongs to week 1 and day 4 ~ 10 belongs to week 2 etc.
In this case, I believe the week_of_month for a specific day should be calculated as follows:
import datetime
def week_of_month(year, month, day):
weekday_of_day_one = datetime.date(year, month, 1).weekday()
weekday_of_day = datetime.date(year, month, day)
return (day - 1)//7 + 1 + (weekday_of_day < weekday_of_day_one)
However, if instead we want to get the nth of the weekday that date is, such as day 1 is the 1st Friday, day 8 is the 2nd Friday, and day 6 is the 1st Wednesday, then we can simply return (day - 1)//7 + 1
The answer you are looking for is (dm-dw+(dw-dm)%7)/7+1 where dm is the day of the month, dw is the day of the week, and % is the positive remainder.
This comes from relating the month offset (mo) and the week of the month (wm), where the month offset is how far into the week the first day starts. If we consider all of these variables to start at 0 we have
wm*7+dw = dm+mo
You can solve this modulo 7 for mo as that causes the wm variable drops out as it only appears as a multiple of 7
dw = dm+mo (%7)
mo = dw-dm (%7)
mo = (dw-dm)%7 (since the month offset is 0-6)
Then you just substitute the month offset into the original equation and solve for wm
wm*7+dw = dm+mo
wm*7 = dm-dw + mo
wm*7 = dm-dw + (dw-dm)%7
wm = (dm-dw + (dw-dm)%7) / 7
As dm and dw are always paired, these can be offset by any amount, so, switching everything to start a 1 only changes the the equation to (dm-dw + (dw-dm)%7)/7 + 1.
Of course the python datetime library starts dm at 1 and dw at 0. So, assuming date is a datatime.date object, you can go with
(date.day-1-date.dayofweek() + (date.dayofweek()-date.day+1)%7) / 7 + 1
As the inner bit is always a multiple of 7 (it is literally dw*7), you can see that the first -date.dayofweek() simply adjusts the value backwards to closest multiple of 7. Integer division does this too, so it can be further simplified to
(date.day-1 + (date.dayofweek()-date.day+1)%7) // 7 + 1
Be aware that dayofweek() puts Sunday at the end of the week.
Move to last day of week in month and divide to 7
from math import ceil
def week_of_month(dt):
""" Returns the week of the month for the specified date.
"""
# weekday from monday == 0 ---> sunday == 6
last_day_of_week_of_month = dt.day + (7 - (1 + dt.weekday()))
return int(ceil(last_day_of_week_of_month/7.0))
You can simply do as follow:
df['month'] = df['Date'].dt.month
df['week'] = df['Date'].dt.week
df['weekOfMonth'] = df.groupby('month')["week"].rank("dense", ascending=False)
One more solution, where Sunday is first day of week, base Python only.
def week_of_month(dt):
""" Returns the week of the month for the specified date.
TREATS SUNDAY AS FIRST DAY OF WEEK!
"""
first_day = dt.replace(day=1)
dom = dt.day
adjusted_dom = dom + (first_day.weekday() + 1) % 7
return (adjusted_dom - 1) // 7 + 1
def week_number(time_ctime = None):
import time
import calendar
if time_ctime == None:
time_ctime = str(time.ctime())
date = time_ctime.replace(' ',' ').split(' ')
months = {'Jan':1,'Feb':2,'Mar':3,'Apr':4,'May':5,'Jun':6,'Jul':7,'Aug':8,'Sep':9,'Oct':10,'Nov':11,'Dec':12}
week, day, month, year = (-1, str(date[2]), months[date[1]], int(date[-1]))
cal = calendar.monthcalendar(year,month)
for wk in range(len(cal)):
wstr = [str(x) for x in cal[wk]]
if day in wstr:
week = wk
break
return week
import time
print(week_number())
print(week_number(time.ctime()))
import datetime
def week_number_of_month(date_value):
week_number = (date_value.isocalendar()[1] - date_value.replace(day=1).isocalendar()[1] + 1)
if week_number == -46:
week_number = 6
return week_number
date_given = datetime.datetime(year=2018, month=12, day=31).date()
week_number_of_month(date_given)