Python 中的年度重复日期提醒
用户可以为生日设置一个提前提醒的日期。(我们不关心出生年份)
用户还可以选择在生日的前0、1、2或7天收到提醒(也就是“提前量”)。用户可以设置自己的时区。
我希望服务器在生日当天的早上8点发送提醒,具体时间要根据用户的时区和提前量来调整。
举个例子:
如果设置的是6月12日,并选择“提前3天提醒”,那么提醒的日期就是6月9日。
我的想法是,在“重复事件”对象中增加一个叫trigger_datetime的字段。这样的话,我的服务器上可以有一个定时任务,每小时运行一次,检查所有与当前时间的小时、日期和月份匹配的事件,并发送提醒。
但问题是,从一年到下一年,提醒的日期可能会变化!
比如,如果提醒设置在3月1日,提前一天的话,可能是2月28日或29日……
也许我不应该使用这个trigger_date的方式,而是应该考虑其他的方案。
欢迎大家提出各种想法。
5 个回答
试试这样做。
从警报中选择所有内容,条件是日期在以下几种情况中:今天的日期、今天加2天、今天加5天、今天加7天。
并且日期减去今天的日期等于警报的频率。
把这些查询结果中的警报发送出去。
假设你不能使用一个现有的库来实现你需要的功能,那么Python自带的datetime
模块,特别是timedelta
对象,可以帮助你实现所需的基本功能:
from datetime import timedelta, date
start_date = date(2010, 6, 12)
notification_date = start_date + timedelta(days=365) - timedelta(days=3)
print notification_date
这段代码会输出:2011-06-09
,而且timedelta
也可以接受小时数,所以你需要的精确度是可以实现的。你可以像我上面展示的那样,定期运行这段代码来检查你的所有事件,看看是否有需要发送提醒的事件。在你的情况下,由于你希望这个事件能够重复发生,你只需在提醒触发时更新开始年份,这样就可以为明年做好准备。
虽然使用普通的 datetime
模块你可以实现所有需要的功能,但如果你需要处理重复事件的话,有一个更强大的扩展库叫做 python-dateutil,它会更方便。下面的代码可以给你一些实现目标的思路:
from datetime import *
from dateutil.rrule import rrule, YEARLY
# GLOBAL CONFIG
td_8am = timedelta(seconds=3600*8)
td_jobfrequency = timedelta(seconds=3600) # hourly job
# USER DATA::
# birthday: assumed to be retrieved from some data source
bday = date(1960, 5, 12)
# reminder delta: number of days before the b-day
td_delta = timedelta(days=6)
# difference between the user TZ and the server TZ
tz_diff = timedelta(seconds=3600*5) # example: +5h
# from current time minus the job periodicity and the delta
sday = date.today()
# occr will return the first birthday from today on
occr = rrule(YEARLY, bymonth=bday.month, bymonthday=bday.day, dtstart=sday, count=1)[0]
# adjust: subtract the reminder delta, fixed 8h (8am) and tz difference
occr -= (td_delta + td_8am + tz_diff)
# send the reminder when the adjusted occurance is within this job time period
if datetime.now() - td_jobfrequency < occr < datetime.now():
print occr, '@todo: send the reminder'
else:
print occr, 'no reminder'
我建议你不要存储明年的提醒日期,因为 delta
(时间间隔)可能会变化,或者 timezone
(时区)可能会变化,甚至 birthday
(生日)本身也可能会变,所以你需要重新计算。上面的方法基本上是实时计算提醒日期(和时间)。
还有一点我可以建议的是,记录上一次发送提醒的日期(包括年份),这样如果系统出现故障,你就不会错过提醒,而是可以发送所有未发送的提醒。你需要调整代码,增加这个检查和更新的功能。