<p>除非这是一种非常常见的计算周数的方法,否则我不知道您是否会找到一个库来为您精确地计算周数,但是使用<code>dateutil</code>的<code>relativedelta</code>和一点逻辑很容易完成。下面是一个返回元组<code>(quarter, week)</code>的简单实现。由于您说过Q1从4月1日开始,我假设从1月1日到4月1日的时间段称为Q0:</p>
<pre><code>from datetime import date, datetime, timedelta
import typing
from dateutil import relativedelta
NEXT_MONDAY = relativedelta.relativedelta(weekday=relativedelta.MO)
LAST_MONDAY = relativedelta.relativedelta(weekday=relativedelta.MO(-1))
ONE_WEEK = timedelta(weeks=1)
def week_in_quarter(dt: datetime) -> typing.Tuple[int, int]:
d: date = dt.date()
year = d.year
# Q0 = January 1, Q1 = April 1, Q2 = July 1, Q3 = October 1
quarter = ((d.month - 1) // 3)
quarter_start = date(year, (quarter * 3) + 1, 1)
quarter_week_2_monday = quarter_start + NEXT_MONDAY
if d < quarter_week_2_monday:
week = 1
else:
cur_week_monday = d + LAST_MONDAY
week = int((cur_week_monday - quarter_week_2_monday) / ONE_WEEK) + 2
return quarter, week
</code></pre>
<p>返回:</p>
<pre><code>$ python week_in_quarter.py
2020-01-01: Q0-W01
2020-02-01: Q0-W05
2020-02-29: Q0-W09
2020-03-01: Q0-W09
2020-06-30: Q1-W14
2020-07-01: Q2-W01
2020-09-04: Q2-W10
2020-12-31: Q3-W14
</code></pre>
<p>如果我误解了日历年的第一个季度,而实际上X年的1月1日至4月1日被视为X-1年的第四季度,那么您可以将末尾的<code>return quarter, week</code>行更改为该行(并更改返回类型注释):</p>
<pre><code>if quarter == 0:
year -= 1
quarter = 4
return year, quarter, week
</code></pre>
<p>将返回值更改为:</p>
<pre><code>$ python week_in_quarter.py
2020-01-01: FY2019-Q4-W01
2020-02-01: FY2019-Q4-W05
2020-02-29: FY2019-Q4-W09
2020-03-01: FY2019-Q4-W09
2020-06-30: FY2020-Q1-W14
2020-07-01: FY2020-Q2-W01
2020-09-04: FY2020-Q2-W10
2020-12-31: FY2020-Q3-W14
</code></pre>
<p>如果这是一个速度瓶颈,那么编写一个不使用<code>dateutil.relativedelta</code>的优化版本应该很容易,而是根据星期几、一年中的哪一天以及这是否是闰年来计算(如果可以尽早将Python中的日历计算转换为整数操作,那么通常会更快),但我认为在大多数情况下,这个版本应该是最容易阅读和理解的</p>
<p>如果要避免对<code>dateutil</code>的依赖,可以用简单函数替换<code>NEXT_MONDAY</code>和<code>LAST_MONDAY</code>:</p>
<pre><code>def next_monday(dt: date) -> date:
weekday = dt.weekday()
return dt + timedelta(days=(7 - weekday) % 7)
def last_monday(dt: date) -> date:
weekday = dt.weekday()
return dt - timedelta(days=weekday)
</code></pre>
<p>在这种情况下,将两个<code>_monday</code>变量分别指定为<code>quarter_week_2_monday = next_monday(quarter_start)</code>和<code>cur_week_monday = last_monday(dt)</code></p>
<p>注意:如果我编写这个函数,我可能不会让它返回一个简单的整数元组,而是使用<a href="https://www.attrs.org/en/stable/" rel="nofollow noreferrer">attrs</a>或<a href="https://docs.python.org/3/library/dataclasses.html" rel="nofollow noreferrer">dataclass</a>创建一个简单的类,如下所示:</p>
<pre><code>import attr
@attr.s(auto_attribs=True, frozen=True, slots=True)
class QuarterInWeek:
year: int
quarter: int
week: int
def __str__(self):
return f"FY{self.year}-Q{self.quarter}-W{self.week:02d}"
</code></pre>
<p>(请注意<code>slots=True</code>是可选的,我认为如果使用<code>dataclasses.dataclass</code>则不可用-只是这是一个简单的结构,我倾向于使用slots类作为简单的结构)</p>