rrule. Between 跳过缺少天数的月份

发布于 2025-01-10 22:51:30 字数 796 浏览 0 评论 0原文

我正在尝试为我正在处理的用例生成员工每月周年纪念日。 RRule 提供了一种非常方便的方法来做到这一点。但是,我无法从中得到我想要的结果。

请参阅下面的代码片段 -

import arrow
from dateutil.rrule import MONTHLY, rrule

by_monthday = 31
start = arrow.get("2021-12-31").to(tz="utc")
end = arrow.get("2022-06-01").to(tz="utc")

rule = rrule(
    freq=MONTHLY,
    bymonthday=by_monthday,
    byhour=0,
    byminute=0,
    bysecond=0,
    dtstart=start.datetime,
)

for r in rule.between(start.datetime, end.datetime, inc=True):
    print(r)

结果 -

2021-12-31 00:00:00+00:00
2022-01-31 00:00:00+00:00
2022-03-31 00:00:00+00:00
2022-05-31 00:00:00+00:00

rrule 跳过没有 31 号的月份,IMO 是库的预期行为。

如何以干净的方式处理缺失的月份并将其默认值设置为 2022-02-282022-04-30

I am trying to generate employee monthly anniversaries for a use-case I am working on. RRule offers a very convenient way to do this. However, I am unable to get the results I want from it.

See snippet below -

import arrow
from dateutil.rrule import MONTHLY, rrule

by_monthday = 31
start = arrow.get("2021-12-31").to(tz="utc")
end = arrow.get("2022-06-01").to(tz="utc")

rule = rrule(
    freq=MONTHLY,
    bymonthday=by_monthday,
    byhour=0,
    byminute=0,
    bysecond=0,
    dtstart=start.datetime,
)

for r in rule.between(start.datetime, end.datetime, inc=True):
    print(r)

Result --

2021-12-31 00:00:00+00:00
2022-01-31 00:00:00+00:00
2022-03-31 00:00:00+00:00
2022-05-31 00:00:00+00:00

rrule skips over the months that do not have the 31st which IMO is the expected behaviour of the library.

How can I handle the missing months in a clean way and set their default value to something like 2022-02-28 or 2022-04-30

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

澜川若宁 2025-01-17 22:51:30

我遇到了同样的问题,我发现的解决方案是创建一个新的虚拟日期,该日期从该月的第一天开始并将其用于 rrule 对象,然后如果您需要该月的最后一天,您可以使用日历模块

import datetime, calendar
from dateutil.rrule import MONTHLY, rrule

by_monthday = 1
start = datetime.datetime(2021, 12, 31)
end = datetime.datetime(2022, 6, 1)

rule = rrule(
    freq=MONTHLY,
    bymonthday=by_monthday,
    byhour=0,
    byminute=0,
    bysecond=0,
    dtstart=datetime.datetime(start.year, start.month, 1),
)

for r in rule.between(start, end, inc=True):
    print(datetime.datetime(r.year, r.month, calendar.monthrange(r.year, r.month)[1]))

I encounter your same problem and a solution that I found was to create a new dummy date that start on the first day of the month and use it on the rrule object, and then if you need the last day of the month you can use the calendar module

import datetime, calendar
from dateutil.rrule import MONTHLY, rrule

by_monthday = 1
start = datetime.datetime(2021, 12, 31)
end = datetime.datetime(2022, 6, 1)

rule = rrule(
    freq=MONTHLY,
    bymonthday=by_monthday,
    byhour=0,
    byminute=0,
    bysecond=0,
    dtstart=datetime.datetime(start.year, start.month, 1),
)

for r in rule.between(start, end, inc=True):
    print(datetime.datetime(r.year, r.month, calendar.monthrange(r.year, r.month)[1]))
同尘 2025-01-17 22:51:30

最好dateutil.rrule 中有一个名为 includeshortmonths 的标志,与 bymonthday 协同工作,

例如bymonthday=31 includeshortmonths=True 返回 Jan 31、Feb 28、Mar 31、Apr 30...

例如 bymonthday=30 includeshortmonths=True 返回 Jan 30、Feb 28、Mar 30、Apr 30...

例如 bymonthday=29 includeshortmonths=True 返回 Jan 29、Feb 28、Mar 29、Apr 29...

代替,只需使用适用于第一个示例的 bymonthday=-1 。另外两个仍然是需要定制的例外。

It would be nice to have a flag called includeshortmonths in dateutil.rrule that works in coordination with bymonthday,

e.g. bymonthday=31 includeshortmonths=True returns Jan 31, Feb 28, Mar 31, Apr 30...

e.g. bymonthday=30 includeshortmonths=True returns Jan 30, Feb 28, Mar 30, Apr 30...

e.g. bymonthday=29 includeshortmonths=True returns Jan 29, Feb 28, Mar 29, Apr 29...

In lieu, simply use bymonthday=-1 which works for the first example. The other two are still exceptions that require customization.

一城柳絮吹成雪 2025-01-17 22:51:30

我使用了这个解决方案

from datetime import datetime

from dateutil.relativedelta import relativedelta
from dateutil.rrule import (
    MONTHLY,
    rrule,
)

def monthly_range(
    start: datetime, 
    end: datetime, 
    month_day: int
) -> list[datetime]:
    first_day_offset = realtivedelta(day=1)
    rule = rrule(
        MONTHLY,
        dtstart=start + first_day_offset,
        until=end + first_day_offset,
        bymonthday=1,
    )
    
    month_day_offset = relativedelta(day=month_day)
    return [timestamp + month_day_offset for timestamp in runle]

I used this solution

from datetime import datetime

from dateutil.relativedelta import relativedelta
from dateutil.rrule import (
    MONTHLY,
    rrule,
)

def monthly_range(
    start: datetime, 
    end: datetime, 
    month_day: int
) -> list[datetime]:
    first_day_offset = realtivedelta(day=1)
    rule = rrule(
        MONTHLY,
        dtstart=start + first_day_offset,
        until=end + first_day_offset,
        bymonthday=1,
    )
    
    month_day_offset = relativedelta(day=month_day)
    return [timestamp + month_day_offset for timestamp in runle]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文