How to calculate the first Monday of the month; python 3.3+
Asked Answered
R

5

7

I need to run a monthly report on the first Monday of the month and calculate this day with Python. The code I have so far will go into a module in our ETL program and will determine if the date is actually the first day of the month. Ideally, what I need is if the Monday is the first Monday of the month, run the report (execute = 1) only on this day. Otherwise, do not run anything (execute = 0). What I have:

# Calculate first Monday of the month

# import module(s)

from datetime import datetime, date, timedelta

today = date.today() # - timedelta(days = 1)
datee = datetime.strptime(str(today), "%Y-%m-%d")

print(f'Today: {today}')

# function finds first Monday of the month given the date passed in "today"

def find_first_monday(year, month, day):
    d = datetime(year, int(month), int(day))
    offset = 0-d.weekday() #weekday = 0 means monday
    if offset < 0:
        offset+=7
    return d+timedelta(offset)

# converts datetime object to date
first_monday_of_month = find_first_monday(datee.year, datee.month, datee.day).date()

# prints the next Monday given the date that is passed as "today" 

print(f'Today\'s date: {today}')
print(f'First Monday of the month date: {first_monday_of_month}')

# if first Monday is true, execute = 1, else execute = 0; 1 will execute the next module of code

if today == first_monday_of_month:
  execute = 1
  print(execute) 
else:
  execute = 0
  print(execute)

It works assuming the date in "today" is not after the first Monday of the month. When "today" is after the first Monday of the month, it prints the next coming Monday.

Our ETL scheduler allows us to run daily, weekly, or monthly. I'm thinking I'll have to run this daily, even though this is a monthly report, and the module with this code will determine if "today" is the first Monday of the month or not. If it's not the first Monday, it will not execute the next code modules (execute = 0). I'm not confident this will actually run if "today" is the first Monday of the month since it prints the next coming Monday for any date passed in "today."

I can't seem to find the answer I need for making sure it only calculates the first Monday of the month and only runs the report on that day. Thanks in advance.

Redwood answered 4/5, 2021 at 3:19 Comment(0)
T
15

One way to do this is to ignore the current day value, and just use 7 instead; then you can simply subtract the weekday offset:

from datetime import datetime, timedelta

# note change to function signature
def find_first_monday(year, month):
    d = datetime(year, int(month), 7)
    offset = -d.weekday() # weekday == 0 means Monday
    return d + timedelta(offset)

This can be generalised to find the first of any day of the week in the month with a dow input where Monday = 0 and Sunday = 6. For example:

def find_first_dow(year, month, dow):
    d = datetime(year, int(month), 7)
    offset = -((d.weekday() - dow) % 7)
    return d + timedelta(offset)

for i in range(7):
     find_first_day(2024, 2, i)

Output:

datetime.datetime(2024, 2, 5, 0, 0)
datetime.datetime(2024, 2, 6, 0, 0)
datetime.datetime(2024, 2, 7, 0, 0)
datetime.datetime(2024, 2, 1, 0, 0)
datetime.datetime(2024, 2, 2, 0, 0)
datetime.datetime(2024, 2, 3, 0, 0)
datetime.datetime(2024, 2, 4, 0, 0)
Tether answered 4/5, 2021 at 3:48 Comment(2)
To generalise for any first target_dow of the month, can you just do offset = -((d.weekday() - target_dow) % 7)?Phore
@ThomChubb yes you can indeed. I've added a generalised function to the answer.Tether
Z
4

With numpy, calculating the first Monday of the month is much simpler that that.

import datetime
import numpy as np

any_date_in_month = datetime.datetime(year, month, day)

year_month = any_date_in_month.strftime('%Y-%m')
first_monday = np.busday_offset(year_month, 0, roll='forward', weekmask='Mon')

so just check anything you need against first_monday and you are set.

Zorazorah answered 28/6, 2022 at 21:2 Comment(0)
L
1

I came across a similar issue to the original poster. The top answer worked great for me, @Nick. However; I also needed to find the 2nd Tuesday, 4th Thursday, etc. I used the top answer as a template. I added in extra parameters to calculate any day of week and week of month combination.

from datetime import datetime, timedelta, date

def date_of_month(year = date.today().year, month = date.today().month, day_of_week = 0, week_of_month = 0):
"""
year: Year of the current date.
month: Month of current date.
day_of_week: Day of week you want to calculate. [Monday = 0, Tuesday = 1, ...]
week_of_month: Week of the month to calculate. [1st Week = 0, 2nd Week = 1, ...]

Default Values: Date input defaults to todays date. Calculates date for 1st Monday of the Month.
"""
d = datetime(year, int(month), 7)
offset = -d.weekday() # weekday == 0 means Monday
return d + timedelta(offset) + timedelta(days=day_of_week) + (timedelta(days=7) * week_of_month)

The function defaults to taking current date as input and calculating the first Monday of the month.

Laine answered 1/2, 2024 at 15:59 Comment(0)
A
0

A slightly different method – The date.weekday() function gives your an index of the day of the week (where Monday is 0 and Sunday is 6). You can use this value to directly calculate the which date any day of the week will fall on. For Mondays, something like this...

def first_monday(year, month):
    day = (8 - datetime.date(year, month, 1).weekday()) % 7
    return datetime.date(year, month, day)

Of course, you could make a generic version, that let you specify which day of the week you were after, like this:

def first_dow(year, month, dow):
    day = ((8 + dow) - datetime.date(year, month, 1).weekday()) % 7
    return datetime.date(year, month, day)

It accepts the same indexes as the date.weekday() function returns (Monday is 0, Sunday is 6). For example, to find the first Wednesday (2) of July 2022...

>>> first_dow(2022, 7, 2)
datetime.date(2022, 7, 6)
Approach answered 31/3, 2022 at 7:17 Comment(1)
For Sunday -> 6 first_dow(2022, 8, 6) it ends with and error: ValueError: day is out of range for monthRyder
H
0

here is a function that will find the first occurrence of a day in a given month.

def find_first_week_day(year: int, month: int, week_day: int) -> date:
    """
    Return the first weekday of the given month.

    :param year: Year
    :param month: Month
    :param week_day: Weekday to find [0-6]

    :return: Date of the first weekday of the given month
    """
    first_day = date(year, month, 1)
    if first_day.weekday() == week_day:
        return first_day
    if first_day.weekday() < week_day:
        return first_day + timedelta(week_day - first_day.weekday())
    return first_day + timedelta(7 - first_day.weekday() + week_day)
Hindermost answered 17/11, 2022 at 19:3 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.