Как я могу получить третью пятницу месяца в Python?

Я пытаюсь получить данные об акциях от Yahoo! Финансы с использованием Python 2.7.9, но мне нужны данные только за 3-ю пятницу месяца. У меня есть функция для получения данных, но мне нужен способ получить даты. Я хочу что-то вроде этого:

def get_third_fris(how_many):
    # code and stuff
    return list_of_fris

Так что зовет get_third_fris(6) вернет список из 3 пятниц длиной в 6 пунктов после текущей даты. Даты должны быть метками времени Unix.

(У меня практически нет опыта работы с time или же datetimeпоэтому, пожалуйста, объясните, что делает ваш код.)

Спасибо!

15 ответов

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

import calendar

c = calendar.Calendar(firstweekday=calendar.SUNDAY)

year = 2015; month = 2

monthcal = c.monthdatescalendar(year,month)
third_friday = [day for week in monthcal for day in week if \
                day.weekday() == calendar.FRIDAY and \
                day.month == month][2]

Вы можете форматировать в метку времени Unix, но это нетривиально. Я отсылаю вас к этому превосходному ответу, в котором есть информация, основанная на том, учитывает ли ваша дата часовой пояс.

Нам не нужно импортировать что-либо кроме datetime. Мы можем принять 7 дней в неделю и будний день 0 == понедельник.

import datetime

def third_friday(year, month):
    """Return datetime.date for monthly option expiration given year and
    month
    """
    # The 15th is the lowest third day in the month
    third = datetime.date(year, month, 15)
    # What day of the week is the 15th?
    w = third.weekday()
    # Friday is weekday 4
    if w != 4:
        # Replace just the day (of month)
        third = third.replace(day=(15 + (4 - w) % 7))
    return third

Предполагая, что вам нужен диапазон каждой третьей пятницы, вы можете просто использовать pandas, пример кода:

import pandas as pd

pd.date_range('2017-12-02','2020-08-31',freq='WOM-3FRI')

Output:
DatetimeIndex(['2017-12-15', '2018-01-19', '2018-02-16', '2018-03-16',
               '2018-04-20', '2018-05-18', '2018-06-15', '2018-07-20',
               '2018-08-17', '2018-09-21', '2018-10-19', '2018-11-16',
               '2018-12-21', '2019-01-18', '2019-02-15', '2019-03-15',
               '2019-04-19', '2019-05-17', '2019-06-21', '2019-07-19',
               '2019-08-16', '2019-09-20', '2019-10-18', '2019-11-15',
               '2019-12-20', '2020-01-17', '2020-02-21', '2020-03-20',
               '2020-04-17', '2020-05-15', '2020-06-19', '2020-07-17',
               '2020-08-21'],
              dtype='datetime64[ns]', freq='WOM-3FRI')

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

from datetime import timedelta, date
import calendar

def next_third_friday(d):
    """ Given a third friday find next third friday"""
    d += timedelta(weeks=4)
    return d if d.day >= 15 else d + timedelta(weeks=1)

def third_fridays(d, n):
    """Given a date, calculates n next third fridays"""

    # Find closest friday to 15th of month
    s = date(d.year, d.month, 15)
    result = [s + timedelta(days=(calendar.FRIDAY - s.weekday()) % 7)]

    # This month's third friday passed. Find next.
    if result[0] < d:
        result[0] = next_third_friday(result[0])

    for i in range(n - 1):
        result.append(next_third_friday(result[-1]))

    return result

Мы можем применить вышеупомянутую функцию, чтобы получить метки времени следующих пятниц:

import time

def timestamp(d):
    return int(time.mktime(d.timetuple()))

fridays = third_fridays(date.today(), 2)

print(fridays)
print(map(timestamp, fridays))

Выход:

[datetime.date(2015, 3, 20), datetime.date(2015, 4, 17)]   
[1426802400, 1429218000]

Как насчет более простого ответа:

import calendar 
c = calendar.Calendar(firstweekday=calendar.SATURDAY)
monthcal = c.monthdatescalendar(my_year, my_month)
monthly_expire_date = monthcal[2][-1]

Я обобщил ответ @pourhaus, чтобы найти n-й день любого месяца:

def nth_day_of_month(month, year, day_of_week, n):
    first_possible_day = {1: 1, 2: 8, 3: 15, 4: 22, 5: 29}[n]
    d = datetime.date(year, month, first_possible_day)
    w = d.weekday()
    if w != day_of_week:
        d = d.replace(day=(first_possible_day + (day_of_week - w) % 7))
    return d

Чистый питон без внешних библиотек.
Возвращает ожидаемый день месяца.
Примечание. На основе ответа @autonopy, но работает.

      from datetime import datetime
def get_nth_day_of_month(year, month, Nth, weekday):
    first_of_month_weekday = datetime(year, month, 1).weekday()
    # Find weekday offset from beginning of month
    day_offset = (weekday - first_of_month_weekday) + 1
    if day_offset < 1:
        day_offset += 7  # correction for some 1st-weekday situations
    # Add N weeks
    return 7 * (Nth - 1) + day_offset

Тесты:

      >>> # first Monday of Nov 2021
>>> get_nth_day_of_month(2021, 11, 1, 0)
1

>>> # first Monday of January 2022
>>> get_nth_day_of_month(2022, 1, 1, 0)
3

>>> # first Monday of May 2022
>>> get_nth_day_of_month(2022, 5, 1, 0)
2

>>> # Mother's day 2022
>>> get_nth_day_of_month(2022, 5, 2, 0)
9

Его легко использовать dateutil, чтобы получить следующую пятницу

import dateutil.parser as dparse
from datetime import timedelta
next_friday = dparse.parse("Friday")
one_week = timedelta(days=7)
friday_after_next = next_friday + one_week
last_friday = friday_after_next + one_week

это использует тот факт, что между пятницами всегда есть неделя... хотя я не уверен, что это отвечает на ваш вопрос, это должно как минимум дать вам хорошую отправную точку

Предполагая, что вы используете панды:

def exp_friday(df):
mask = np.where((df.index.day > 14) & 
                (df.index.day < 22) & 
                (df.index.dayofweek == 4), True, False)
return df[mask]

С помощью dateutil.relativedelta:

from dateutil.relativedelta import relativedelta, FR # $ pip install python-dateutil

def third_friday_dateutil(now):
    """the 3rd Friday of the month, not the 3rd Friday after today."""
    now = now.replace(day=1) # 1st day of the month
    now += relativedelta(weeks=2, weekday=FR)
    return now

Или используя dateutil.rrule:

from datetime import date, timedelta
from dateutil.rrule import rrule, MONTHLY, FR

def third_friday_rrule(now):
    return rrule(MONTHLY, count=1, byweekday=FR, bysetpos=3, dtstart=now.replace(day=1))[0]

def get_third_fris_rrule(how_many):
    return list(rrule(MONTHLY, count=how_many, byweekday=FR, bysetpos=3, dtstart=date.today()+timedelta(1)))

Вот решение грубой силы (в 15 раз быстрее):

#!/usr/bin/env python
import calendar
from datetime import date, timedelta
from itertools import islice

DAY = timedelta(1)
WEEK = 7*DAY

def fridays(now):
    while True:
        if now.weekday() == calendar.FRIDAY:
            while True:
                yield now
                now += WEEK
        now += DAY

def next_month(now):
    """Return the first date that is in the next month."""
    return (now.replace(day=15) + 20*DAY).replace(day=1)

def third_friday_brute_force(now):
    """the 3rd Friday of the month, not the 3rd Friday after today."""
    return next(islice(fridays(now.replace(day=1)), 2, 3))

def get_third_fris(how_many):
    result = []
    now = date.today()
    while len(result) < how_many:
        fr = third_friday_brute_force(now)
        if fr > now: # use only the 3rd Friday after today
            result.append(fr)
        now = next_month(now)
    return result

print(get_third_fris(6))

Выход

[datetime.date(2015, 3, 20),
 datetime.date(2015, 4, 17),
 datetime.date(2015, 5, 15),
 datetime.date(2015, 6, 19),
 datetime.date(2015, 7, 17),
 datetime.date(2015, 8, 21)]

См. Преобразование datetime.date в метку времени UTC в Python.

Вот сравнение с другими решениями и тестами (для всех возможных 400-летних моделей).

Я обобщил свой ответ, чтобы каждый мог использовать его для любого Nthдень недели месяца и использование минимальных библиотек по умолчанию. Мое использование состояло в том, чтобы найти даты перехода на летнее время (летнее время) для года (2-е воскресенье марта и 1-е воскресенье ноября).

      # Libraries:
from datetime import datetime

# Function:
def get_nth_day_of_month(year, month, Nth, weekday):
   # Process is to find out what weekday the 1st of the month is
   # And then go straight to the desired date by calculating it
   first_of_month_weekday = datetime(year, month, 1).weekday()
   day_desired = 7 * (Nth-1) + (weekday - first_of_month_weekday)
   if day_desired < 1 : day_desired += 7 #correction for some 1st-weekday situations
   return datetime(year, month, day_desired)

# Config: 
year = 2022
month = 3 #DST starts in March
weekday = 6 #sunday
Nth = 2  #2nd sunday

dst_start = get_nth_day_of_month(year, month, Nth, weekday)

В моем случае это генерирует начало летнего времени в этом году:

      In [2]: dst_start
Out [2]: datetime.datetime(2022, 3, 13, 0, 0)

Затем для окончания летнего времени в 2022 году:

      month = 11
Nth = 1

dst_end = get_nth_day_of_month(year, month, Nth, weekday)

Результат:

      In[4]: dst_end
Out[4]: datetime.datetime(2022, 11, 5, 0, 0)

Таким образом, в 2022 году переход на летнее время длится с 13 марта 2022 года по 05 ноября 2022 года.

Стандарт: дни пронумерованы с понедельника = 0 до воскресенья = 6.

Вот решение, в котором кто-то уже понял: модуль который является расширением пакета Python dateutil ( pip install python-dateutil).

      import datetime
from dateutil import relativedelta


def third_fridays(n):
    first_of_this_month = datetime.date.today().replace(day=1)
    return (
        first_of_this_month
        + relativedelta.relativedelta(weekday=relativedelta.FR(3), months=i)
        for i in range(n)
    )

Ключевым моментом здесь, конечно же, является weekday=relativedelta.FR(3)который говорит именно то, что нужно: третья пятница месяца. Вот соответствующая часть relativedelta,документации для weekday параметр,

будний день:

Один из экземпляров дня недели (MO, TU и т. Д.), Доступных в модуле relativedelta. Эти экземпляры могут получать параметр N, указывающий N-й день недели, который может быть положительным или отрицательным (например, MO(+1) или MO(-2)).

(Для тех, кто плохо знаком с Python return (...)является выражением генератора, которое вы можете просто рассматривать как что-то, что нужно перебирать, например, for friday in third_fridays(18): print(friday))

Я предлагаю начать с первого дня месяца, а затем найти ближайшую пятницу.

4 представляется как пятница из метода datetime.weekday().

Затем мы вычитаем день недели первого числа месяца из 4(пятница). Если результат отрицательный, ближайшая найденная пятница была в предыдущем месяце, поэтому мы добавляем 7 дней, в противном случае у нас уже есть первая пятница.

Тогда результат так же прост, как добавить еще 14 дней, чтобы получить третью пятницу, а затем добавить timedelta, представляющий третью пятницу, к первому дню месяца.

      from datetime import datetime, timedelta

def get_third_friday(year, month):
    first_day_of_month = datetime(year, month, 1)
    closest_friday = 4 - first_day_of_month.weekday()
    if closest_friday < 0:
        first_friday = closest_friday + 7
    else:
        first_friday = closest_friday

    third_friday = first_friday + 14
    return first_day_of_month + timedelta(third_friday)

Это общая функция, которая дает вам все даты определенной недели в виде списка.

def frecuencia_daymng(self, start_day, year, month, dayofweek):
    """dayofweek starts on MONDAY in 0 index"""
    c = calendar.Calendar(firstweekday=start_day)
    monthcal = c.monthdatescalendar(year, month)
    ldates = []
    for tdate in monthcal:
        if tdate[dayofweek].month == month:
            ldates.append(tdate[dayofweek])
    return ldates

Допустим, вам нужны все понедельники 2020 года 10.

frecuencia_daymng(calendar.MONDAY, 2020, 10, 0)

Это даст вам результат.

[datetime.date(2020, 10, 5),
 datetime.date(2020, 10, 12),
 datetime.date(2020, 10, 19),
 datetime.date(2020, 10, 26)]

Итак, теперь у вас есть первый, второй... и т.д. понедельник месяца.

from dateutil.relativedelta import *
from datetime import *

def find_mth_friday(your_date,m):
    mth_friday = your_date + relativedelta(day=1, weekday=FR(m)) #sets day=1 in your_date and adds m fridays to it.
    mth_friday_timestamp = int(mth_friday.strftime("%s")) #converting datetime to unix timestamp
    return mth_friday_timestamp

def get_third_fris(n):
    output_timestamps = []
    today = datetime.now() #gets current system date
    for i in range(1,n+1): #value of i varies from 1 to 6 if n=6
        next_month = today + relativedelta(months=+i) #adds i months to current system date
        third_friday = find_mth_friday(next_month,3) #finds third friday of the month using 'find_mth_friday()', the function we defined
        output_timestamps.append(third_friday)
    return output_timestamps

print(get_third_fris(6)) #let's try invoking our function with n=6 dates

Это то, что вы хотели, верно?

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