Останній попередній робочий день у Python


81

Мені потрібно відняти робочі дні з поточної дати.

На даний момент у мене є якийсь код, який завжди повинен працювати в останній робочий день. Отже, це може бути сьогодні, якщо ми з понеділка по п’ятницю, але якщо це субота чи неділя, то мені потрібно повернути його на п’ятницю перед вихідними. На даний момент у мене є досить незграбний код для цього:

 lastBusDay = datetime.datetime.today()
 if datetime.date.weekday(lastBusDay) == 5:      #if it's Saturday
     lastBusDay = lastBusDay - datetime.timedelta(days = 1) #then make it Friday
 elif datetime.date.weekday(lastBusDay) == 6:      #if it's Sunday
     lastBusDay = lastBusDay - datetime.timedelta(days = 2); #then make it Friday

Чи є кращий спосіб?

Чи можу я сказати timedelta працювати в будні, а не в календарні дні, наприклад?


7
А як щодо свят?
Слакс

Ось фрагмент із dzzone, який може вам допомогти: snippets.dzone.com/posts/show/9173
Девід Андерхілл,

так, я вже дбаю про них: моя база даних завжди заповнює свята, поки вони припадають на будній день. Але я згоден, свята загалом теж є проблемою. Я маю на увазі, що я міг би почати захоплюватися і використовувати sckits.timeseries, але насправді я хочу щось простіше.
Thomas Browne

1
привіт, я запізнився на вечірку, вибач. одна простіша річ, яку міг би зробити OP, - перевірити, чи datetime.date.weekday (lastBusDay)> = 5, замість того, щоб перевіряти суботу та неділю окремо. але так .. у будь-якому разі нижче є інші кращі відповіді.
тагома

Відповіді:


142

Використовуйте панди!

import datetime
# BDay is business day, not birthday...
from pandas.tseries.offsets import BDay

today = datetime.datetime.today()
print(today - BDay(4))

Оскільки сьогодні четвер, 26 вересня, це дасть вам результат:

datetime.datetime(2013, 9, 20, 14, 8, 4, 89761)

2
Приємно. це правильна відповідь, сьогодні. Коли я запитав Q, панди все ще були трохи неповними.
Томас Браун,

6
Останній випуск панд (0.14.0) також підтримує святкові календарі
фантастичний

1
Встановлення панд додає 233M до vps. Тож я заново винайшов колесо без голови.
Стюарт


13

Здається, є кілька варіантів, якщо ви відкриті для встановлення додаткових бібліотек.

Цей пост описує спосіб визначення робочих днів за допомогою dateutil .

http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-09/3758.html

BusinessHours дозволяє вам визначити ваш перелік святкових днів тощо, щоб визначити, коли ваш робочий час (і, подовжуючи робочі дні).

http://pypi.python.org/pypi/BusinessHours/


приємна Елісон. Все ще не дуже просто, хоча, на жаль. Я збираюся піти вашим маршрутом, хоча. Дякую за допомогу.
Thomas Browne

2
Визначення робочих днів у всіх культурах навряд чи буде достатньо простою проблемою для включення до стандартної бібліотеки.
Елісон Р.

2
Важливо, оскільки моя заявка стосується фінансових ринків, а Єгипет та Ізраїль працюють у неділю.
Thomas Browne

2
Хтось навіть використовує BuinessHours? Протягом 1 хвилини я виявив, що рядок 87 повинен читатися self.worktiming[1](відсутній self ), а рядок 51 повинен читатися extradays(відсутній s ). Сам вихідний код виглядає досить бідно, крапки з комою розкидані по всьому.
Пакман,

12

Якщо ви хочете пропустити американські свята, а також вихідні, це спрацювало для мене (використовуючи панди 0.23.3):

import pandas as pd
from pandas.tseries.holiday import USFederalHolidayCalendar
from pandas.tseries.offsets import CustomBusinessDay
US_BUSINESS_DAY = CustomBusinessDay(calendar=USFederalHolidayCalendar())
july_5 = pd.datetime(2018, 7, 5)
result = july_5 - 2 * US_BUSINESS_DAY # 2018-7-2

Щоб перетворити на об'єкт дати python, я зробив це:

result.to_pydatetime().date()

11

Можливо, цей код може допомогти:

lastBusDay = datetime.datetime.today()
shift = datetime.timedelta(max(1,(lastBusDay.weekday() + 6) % 7 - 3))
lastBusDay = lastBusDay - shift

Ідея полягає в тому, що в понеділок ви повинні повернутися назад на 3 дні, у неділю 2 і 1 в будь-який інший день.

Заява (lastBusDay.weekday() + 6) % 7 просто перевизначає понеділок з 0 до 6.

Дійсно, не знаю, чи це буде краще з точки зору продуктивності.


10

ВІДМОВА: Я автор ...

Я написав пакет, який робить саме це, розрахунки дат бізнесу. Ви можете використовувати спеціальну специфікацію тижня та святкові дні.

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

Сподіваюся, це корисно для інших людей.

https://pypi.python.org/pypi/business_calendar/


1
велике спасибі, ваша бібліотека чудово працювала для мене. Можливо, вам слід додати до своєї документації, що ваша бібліотека підтримує негативні дні, якщо ви хочете відняти дні, і що це закінчується на pip
guinunez

Ей, я знаю, що це може бути не найкращим способом зв’язатися з вами, але я просто хотів повідомити про проблему, з якою я стикався з вашим модулем business_calendar. Я створив календар із федеральними святами США: ['01.01.2015', '2015-01-19', '2015-02-16', '2015-05-25', '2015-07-03' , '2015-09-07', '2015-10-12', '2015-11-11', '2015-11-26', '2015-12-25'], потім спробував обчислити різницю між датою та часом ( 2015, 1, 16, 15, 28, 40) і datetime (2015, 1, 23, 11, 58, 0), але постійно повертає -1. Зняття год / м / с з результатів часу (правильно) в 4.
Даррен Рінгер,

1
Насправді при подальшому тестуванні певні порівняння дат просто блокують, без видимих ​​причин, і ніколи, здається, не повертають результат. У наведеному вище прикладі спроба порівняти datetime (2015, 1, 16) з datetime (2015, 1, 25) веде до такого блоку, з явним встановленням будь-яких свят. Після розслідування це відбувається, коли параметром date2 є дата, яка не є робочим днем.
Даррен Рінгер

6

timeboard пакет робить це.

Припустимо, ваша дата - 04 вересня 2017 року. Незважаючи на те, що це понеділок, це було свято в США (День праці). Отже, останнім робочим днем ​​була п’ятниця, 1 вересня.

>>> import timeboard.calendars.US as US
>>> clnd = US.Weekly8x5()
>>> clnd('04 Sep 2017').rollback().to_timestamp().date()
datetime.date(2017, 9, 1)

У Великобританії 04 вересня 2017 року був звичайним робочим днем, тож останнім робочим днем ​​був сам.

>>> import timeboard.calendars.UK as UK
>>> clnd = UK.Weekly8x5()
>>> clnd('04 Sep 2017').rollback().to_timestamp().date()
datetime.date(2017, 9, 4)

ЗАМОВЛЕННЯ: Я є автором таймбордів.


4

Якщо хтось шукає рішення щодо свят (без величезної бібліотеки, як панди), спробуйте його функції:

import holidays
import datetime


def previous_working_day(check_day_, holidays=holidays.US()):
    offset = max(1, (check_day_.weekday() + 6) % 7 - 3)
    most_recent = check_day_ - datetime.timedelta(offset)
    if most_recent not in holidays:
        return most_recent
    else:
        return previous_working_day(most_recent, holidays)

check_day = datetime.date(2020, 12, 28)
previous_working_day(check_day)

які виробляють:

datetime.date(2020, 12, 24)

2

Це дасть генератор робочих днів, звичайно без свят, зупинка datetime.datetime об'єкт. Якщо вам потрібні канікули, просто наведіть додатковий аргумент із переліком канікул і уточніть у "IFology" ;-)

def workingdays(stop, start=datetime.date.today()):
    while start != stop:
        if start.weekday() < 5:
            yield start
        start += datetime.timedelta(1)

Пізніше ви зможете порахувати їх як "подобається"

workdays = workingdays(datetime.datetime(2015, 8, 8))
len(list(workdays))

0

Чому б вам не спробувати щось на зразок:

lastBusDay = datetime.datetime.today()
if datetime.date.weekday(lastBusDay) not in range(0,5):
    lastBusDay = 5

0
 def getNthBusinessDay(startDate, businessDaysInBetween):
    currentDate = startDate
    daysToAdd = businessDaysInBetween
    while daysToAdd > 0:
        currentDate += relativedelta(days=1)
        day = currentDate.weekday()
        if day < 5:
            daysToAdd -= 1

    return currentDate 

0

інша спрощена версія

lastBusDay = datetime.datetime.today()
wk_day = datetime.date.weekday(lastBusDay)
if wk_day > 4:      #if it's Saturday or Sunday
    lastBusDay = lastBusDay - datetime.timedelta(days = wk_day-4) #then make it Friday

0

Рішення незалежно від різних юрисдикцій, що мають різні свята:

Якщо вам потрібно знайти правильний ідентифікатор у таблиці, ви можете використовувати цей фрагмент. Модель таблиці - це модель sqlalchemy, і дати пошуку, за якими проводиться день поля.

def last_relevant_date(db: Session, given_date: date) -> int:
    available_days = (db.query(Table.id, Table.day)
                      .order_by(desc(Table.day))
                      .limit(100).all())
    close_dates = pd.DataFrame(available_days)
    close_dates['delta'] = close_dates['day'] - given_date
    past_dates = (close_dates
                  .loc[close_dates['delta'] < pd.Timedelta(0, unit='d')])
    table_id = int(past_dates.loc[past_dates['delta'].idxmax()]['id'])
    return table_id

Це не рішення, яке я б рекомендував, коли вам доведеться конвертувати оптом. Це досить загально і дорого, оскільки ви не використовуєте об’єднання. Більше того, передбачається, що у вас є відповідний день, який є одним із 100 останніх днів у таблиці моделей. Тому він вирішує питання введення даних, які можуть мати різні дати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.