9

特定の日付のn番目の平日を計算しようとしています。たとえば、特定の日付の月の第3水曜日を計算できるはずです。

私はそれを行うことになっている関数の2つのバージョンを書きました:

from datetime import datetime, timedelta

### version 1
def nth_weekday(the_date, nth_week, week_day):
    temp = the_date.replace(day=1)
    adj = (nth_week-1)*7 + temp.weekday()-week_day
    return temp + timedelta(days=adj)

### version 2
def nth_weekday(the_date, nth_week, week_day):
    temp = the_date.replace(day=1)
    adj = temp.weekday()-week_day
    temp += timedelta(days=adj)
    temp += timedelta(weeks=nth_week)
    return temp

コンソール出力

# Calculate the 3rd Friday for the date 2011-08-09
x=nth_weekday(datetime(year=2011,month=8,day=9),3,4)
print 'output:',x.strftime('%d%b%y') 

# output: 11Aug11 (Expected: '19Aug11')

両方の関数のロジックは明らかに間違っていますが、バグを見つけることができないようです-誰かがコードの何が悪いのかを見つけることができます-正しい値を返すように修正するにはどうすればよいですか?

4

8 に答える 8

10

あなたの問題はここにあります:

adj = temp.weekday()-week_day

まず第一に、あなたは物事を間違った方法で減算しています。あなたは希望の日から実際の日を減算する必要があり、その逆ではありません。

次に、減算の結果が負にならないようにする必要があります。を使用して0〜6の範囲に配置する必要があります% 7

結果:

adj = (week_day - temp.weekday()) % 7

さらに、2番目のバージョンでnth_week-1は、最初のバージョンと同じように週を追加する必要があります。

完全な例:

def nth_weekday(the_date, nth_week, week_day):
    temp = the_date.replace(day=1)
    adj = (week_day - temp.weekday()) % 7
    temp += timedelta(days=adj)
    temp += timedelta(weeks=nth_week-1)
    return temp

>>> nth_weekday(datetime(2011,8,9), 3, 4)
datetime.datetime(2011, 8, 19, 0, 0)
于 2012-08-09T12:30:01.840 に答える
9

一発ギャグ

標準ライブラリのカレンダーを使用するワンライナーでn番目の平日を見つけることができます。

import calendar
calendar.Calendar(x).monthdatescalendar(year, month)[n][0]

どこ:

  • x:平日を表す整数(0は月曜日)

  • n:質問の「n番目」の部分

  • 年、月:整数の年と月

これにより、datetime.dateオブジェクトが返されます。

故障

このように分解することができます:

calendar.Calendar(x)

必要な平日から始まる平日でカレンダーオブジェクトを作成します。

.monthdatescalendar(year, month)

その月のすべての暦日を返します。

[n][0]

n番目の週(x日から始まるその週の最初の日)の0インデックス値を返します。

なぜそれが機能するのか

必要な平日に週を開始する理由は、デフォルトでは0(月曜日)が週の最初の日として使用され、月が水曜日に始まる場合、カレンダーは最初の週が最初に発生したときに開始すると見なすためです。月曜日(つまり、第2週)、1週間遅れます。

2013年9月の第3土曜日(その月の米国ストックオプションの有効期限)が必要な場合は、次を使用します。

calendar.Calendar(5).monthdatescalendar(2013,9)[3][0]
于 2013-09-21T04:00:16.680 に答える
2

投票数が最も多いワンライナーの問題は、機能しないことです。

ただし、これは改良の基礎として使用できます。

あなたはこれがあなたが得るものであることがわかります:

c = calendar.Calendar(calendar.SUNDAY).monthdatescalendar(2018, 7)
for c2 in c:
    print(c2[0])

2018-07-01
2018-07-08
2018-07-15
2018-07-22
2018-07-29

c = calendar.Calendar(calendar.SUNDAY).monthdatescalendar(2018, 8)
for c2 in c:
    print(c2[0])

2018-07-29
2018-08-05
2018-08-12
2018-08-19
2018-08-26

考えてみれば、カレンダーをネストされたリストに整理して、一度に1週間分の日付を印刷しようとしています。そのため、他の月のストラグラーが登場します。その月に該当する有効な日の新しいリストを使用することで、これでうまくいきます。


リストを追加して回答

import calendar
import datetime
def get_nth_DOW_for_YY_MM(dow, yy, mm, nth) -> datetime.date:
    #dow - Python Cal - 6 Sun 0 Mon ...  5 Sat
    #nth is 1 based... -1. is ok for last.
    i = -1 if nth == -1 or nth == 5 else nth -1
    valid_days = []
    for d in calendar.Calendar(dow).monthdatescalendar(yy, mm):
        if d[0].month == mm:
            valid_days.append(d[0])
    return valid_days[i]

それで、これがどのように呼ばれることができるかです:

firstSundayInJuly2018 = get_nth_DOW_for_YY_MM(calendar.SUNDAY, 2018, 7, 1)
firstSundayInAugust2018 = get_nth_DOW_for_YY_MM(calendar.SUNDAY, 2018, 8, 1)
print(firstSundayInJuly2018)
print(firstSundayInAugust2018)

そしてここに出力があります:

2018-07-01 
2018-08-05

get_nth_DOW_for_YY_MM()次のようなラムダ式を使用してリファクタリングできます。

ラムダ式リファクタリングで答える

import calendar
import datetime
def get_nth_DOW_for_YY_MM(dow, yy, mm, nth) -> datetime.date:
    #dow - Python Cal - 6 Sun 0 Mon ...  5 Sat
    #nth is 1 based... -1. is ok for last.
    i = -1 if nth == -1 or nth == 5 else nth -1
    return list(filter(lambda x: x.month == mm, \
          list(map(lambda x: x[0], \ 
            calendar.Calendar(dow).monthdatescalendar(yy, mm) \
          )) \
        ))[i]

于 2018-07-06T22:33:09.003 に答える
1

対象日が月の1日である場合、ワンライナーの回答は機能しないようです。たとえば、毎月第2金曜日が必要な場合は、ワンライナーアプローチ

calendar.Calendar(4).monthdatescalendar(year, month)[2][0]

2013年3月の場合は2013年3月8日になるはずの2013年3月15日を返します。おそらく次のようなチェックを追加します。

if date(year, month, 1).weekday() == x:
    delivery_date.append(calendar.Calendar(x).monthdatescalendar(year, month)[n-1][0])
else:
    delivery_date.append(calendar.Calendar(x).monthdatescalendar(year, month)[n][0])
于 2016-10-03T09:07:15.840 に答える
0

または、これはPython 2でも機能し、その月の平日の出現を返します。つまり、2018年6月16日が入力の場合、2018年6月16日の日の出現を返します。

月/年/日付の整数を任意の整数に置き換えることができます-現在、システムから入力/日付を取得していますdatetime

印刷ステートメントを省略するかpass、不要な場所で使用してください

import calendar
import datetime
import pprint

month_number = int(datetime.datetime.now().strftime('%m'))
year_number = int(datetime.datetime.now().strftime('%Y'))
date_number = int(datetime.datetime.now().strftime('%d'))
day_ofweek = str(datetime.datetime.now().strftime('%A'))


def weekday_occurance():
print "\nFinding current date here\n"
for week in xrange(5):
    try:
        calendar.monthcalendar(year_number, month_number)[week].index(date_number)
        occurance = week + 1
        print "Date %s of month %s and year %s is %s #%s in this month." % (date_number,month_number,year_number,day_ofweek,occurance)
        return occurance
        break
    except ValueError as e:
        print "The date specified is %s which is week %s" % (e,week)



myocc = weekday_occurance()
print myocc
于 2018-06-15T09:37:18.720 に答える
0

少し調整すると、ワンライナーが正しく機能します。

import calendar
calendar.Calendar((weekday+1)%7).monthdatescalendar(year, month)[n_th][-1]

ここでn_th は、cスタイルとして解釈する必要があります。たとえば、0が最初のインデックスです。

例:2018年7月の第1日曜日を見つけるには、次のように入力します。

>>> calendar.Calendar(0).monthdatescalendar(2018, 7)[0][-1]
datetime.date(2018, 7, 1)
于 2021-01-21T12:30:08.767 に答える
0

ここの人々はワンライナーが好きなようです、私は以下に提案します。

import calendar
[cal[0] for cal in calendar.Calendar(x).monthdatescalendar(year, month) if cal[0].month == month][n]
于 2021-01-22T03:04:19.587 に答える
0

Python dateutilパッケージ( )の拡張機能であるrelativedeltaモジュールは、まさにあなたが望むことを実行します。pip install python-dateutil

from dateutil import relativedelta
import datetime


def nth_weekday(the_date, nth_week, week_day):
    return the_date.replace(day=1) + relativedelta.relativedelta(
        weekday=week_day(nth_week)
    )

print(nth_weekday(datetime.date.today(), 3, relativedelta.FR))

ここで重要な部分weekday=relativedelta.FR(3)は、その月の第3金曜日に評価されます。パラメータのドキュメントの関連部分は次のとおりです。weekday

平日:

Relativedeltaモジュールで使用可能な平日のインスタンス(MO、TUなど)の1つ。これらのインスタンスは、正または負のN番目の平日を指定するパラメーターNを受け取る場合があります(MO(+1)またはMO(-2)など)。

于 2021-09-03T18:16:14.047 に答える