Найдите первый рабочий день и последний рабочий день предыдущего месяца по дате

У меня есть такой вариант использования в Python: найти первый рабочий день и последний рабочий день предыдущего месяца по дате. Например, если дата 2024-06-10

first_business_day = '2024-05-01'
last_business_day = '2024-05-31'

Я пробовал, как показано ниже

run_date = '2024-06-10'
from datetime import datetime, timedelta

d = datetime.strptime(run_date, '%Y-%m-%d').date()
previous_month_first_business_day = (d - timedelta(days=d.day)).replace(day=1).strftime("%Y-%m-%d")
previous_month_last_business_day = (d - timedelta(days=d.day)).strftime("%Y-%m-%d")

Результат:

previous_month_first_business_day = '2024-05-01'
previous_month_last_business_day = '2024-05-31'

Это работает нормально в мае, но когда я хочу тот же результат в июне, то, используя приведенное выше, я получаю:

previous_month_first_business_day = '2024-06-01' # This should be '2024-06-03'
previous_month_last_business_day = '2024-06-30' # This should be '2024-06-29'

Что мне делать, чтобы добиться нужного результата?

Вы специально заменили день месяца на первый, без чего-либо, связанного с тем, каким будет рабочий день, поэтому вы получаете первое число месяца, как и ожидалось.

Thierry Lathuille 10.06.2024 20:16

Как в вашем случае определяются первый и последний рабочий день? Учитывается ли это банковскими/национальными праздниками? Работают ли рабочие по выходным?

s3dev 10.06.2024 20:21

«Это должно быть «29 июня 2024 г.» — Но 29-е число — суббота. Являются ли субботы рабочими днями в этой системе?

wjandrea 10.06.2024 20:22

Какие исследования вы провели? Это помогает? Первая деловая дата месяца Python

wjandrea 10.06.2024 20:24

У @wjandrea есть возможность использовать Pandas! особенно коллекция pandas USFederalHolidayCalendar может оказаться всем, что вам нужно, чтобы пропустить праздничные даты.

ti7 10.06.2024 20:30
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
5
93
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Взгляните на встроенный календарь вместо просто datetime

import calendar
import datetime

def month_start_end_work(date_src, fmt_date = "%Y-%m-%d"):
    dt = datetime.datetime.strptime(date_src, fmt_date)

    # roll around January -> December
    month = 12 if dt.month == 1 else (dt.month - 1)
    year = dt.year if month != 12 else (dt.year - 1)

    # discover last day of last month
    for day in (31, 30, 29, 28):  # handle Feb cases
        try:  # already the correct year for leap
            month_start = calendar.weekday(year, month, day)
        except ValueError:  # day is out of range for month
            continue  # month has fewer days (always decreases)
        if month_start == calendar.SATURDAY:
            day -= 1
        elif month_start == calendar.SUNDAY:
            day -= 2
        break  # last day name to be used
    else:  # did not solve and break
        raise RuntimeError("BUG: impossible code path reached")
    day_last = (year, month, day)  # TODO may want to make a datetime.datetime
    
    # discover first day of last month
    day = 1  # all months begin at 1
    month_start = calendar.weekday(year, month, day)
    if month_start == calendar.SATURDAY:
        day += 2
    elif month_start == calendar.SUNDAY:
        day += 1
    day_first = (year, month, day)

    return day_first, day_last
>>> month_start_end_work("2024-06-10")
((2024, 5, 1), (2024, 5, 31))
>>> month_start_end_work("2024-07-10")
((2024, 6, 3), (2024, 6, 28))
>>> month_start_end_work("2022-01-01")
((2021, 12, 1), (2021, 12, 31))

Если у вас есть особые дни, которые, как вы знаете, являются выходными (вероятно, 6–12 в год), соберите их в список и просто продолжайте уменьшать/увеличивать для точных совпадений.

Здесь праздничные дни считаются рабочими днями, нет удобного места для добавления проверки на праздничные дни, а функциональность, которая у него есть (проверка только на будние дни), неоправданно длинная и сложная.

TigerhawkT3 12.06.2024 01:41

действительно, хотя праздники - это всегда больная тема.. праздники , как вы предлагаете, отличный выбор вместе с таймсериями Панд (предложено по ссылке SO в комментариях) .. но в связи с регионом и спецификой работы, от всего сердца Компаниям предлагается опубликовать (внутреннюю) пару API для многолетнего окна праздников по региону и снова для всех известных временных интервалов компании, возможно, в виде статического ответа JSON, чтобы дополнить или переопределить общедоступные данные при запуске или сборке. время

ti7 12.06.2024 21:32

имея некоторый источник дополнительных данных о праздниках, пользователи могут преобразовать его в набор кортежей для сравнения, а также увеличить или уменьшить свой день в

ti7 12.06.2024 21:34

Вы были на правильном пути, получив первый и последний день предыдущего месяца. Следующим шагом будет увеличение/уменьшение, если эти дни являются выходными, что вы можете проверить с помощью метода weekday(): неделя начинается с 0 с понедельника и заканчивается в 6 с воскресенья. Убедившись, что этот день не является выходным с помощью weekday(), вы должны убедиться, что это также не выходной, используя библиотеку, например праздники. Например, чтобы проверить праздники в США:

from datetime import datetime, timedelta
import holidays

def previous_month_business_day_endpoints(d, h=holidays.US()):
    d = datetime.strptime(d, '%Y-%m-%d').date()
    end = d - timedelta(days=d.day)
    start = end.replace(day=1)
    while start.weekday() > 4 or start in h:
        start += timedelta(days=1)
    while end.weekday() > 4 or end in h:
        end -= timedelta(days=1)
    return start, end

Это будет правильно обрабатывать входные данные, такие как '2024-2-1', поскольку первый будний день января 2024 года приходится на Новый год.

Другие вопросы по теме