@@ -63,7 +63,7 @@ def adjust(self, minimum: timedelta) -> None:
6363class DurationPeriod (BaseMessagePeriod ):
6464 """
6565 Base for duration-like message periods.
66- """
66+ """
6767 @abstractmethod
6868 def _get_period (self ) -> timedelta :
6969 "Get's the calculated relative period (offset) from previous scheduled time."
@@ -146,14 +146,14 @@ def __init__(
146146 self .maximum = maximum
147147 super ().__init__ (next_send_time )
148148
149-
149+
150150 def _get_period (self ):
151151 return timedelta (seconds = randrange (self .minimum .total_seconds (), self .maximum .total_seconds ()))
152152
153153 def adjust (self , minimum : timedelta ) -> None :
154154 if self .minimum >= minimum :
155155 return
156-
156+
157157 self .maximum = minimum + (self .maximum - self .minimum ) # Preserve the period band's width
158158 self .minimum = minimum
159159
@@ -194,7 +194,7 @@ def __init__(
194194 ) -> None :
195195 if not days :
196196 raise ValueError (f"'days' parameter must be a list of day literals { DaysOfWeekPeriod .WEEK_DAYS } ." )
197-
197+
198198 if time .tzinfo is None :
199199 time = time .replace (tzinfo = datetime .now ().astimezone ().tzinfo )
200200
@@ -243,7 +243,7 @@ def calculate(self):
243243 def adjust (self , minimum : timedelta ) -> None :
244244 # The minium between sends will always be 24 hours.
245245 # Slow-mode minimum is maximum 6 hours, thus this is not needed.
246- raise NotImplementedError ( "Setting minimal period would break the definition of class." )
246+ pass
247247
248248
249249@doc_category ("Message period" )
@@ -295,7 +295,7 @@ class NamedDayOfYearPeriod(EveryXPeriod):
295295 Use ``datetime`` to specify the exact date and time at which the message should start being sent.
296296 Use ``timedelta`` to specify how soon (after creation of the object) the message
297297 should start being sent.
298-
298+
299299 Example
300300 ----------
301301 .. code-block:: python
@@ -332,7 +332,6 @@ def calculate(self) -> datetime:
332332 now = max (datetime .now ().astimezone (), self .next_send_time )
333333 self_time = self .time
334334 next = now .replace (
335- day = 1 ,
336335 month = self .month ,
337336 hour = self_time .hour ,
338337 minute = self_time .minute ,
@@ -341,23 +340,18 @@ def calculate(self) -> datetime:
341340 )
342341
343342 while True :
344- isoday = next .isoweekday ()
345- if isoday > self .isoday :
346- next = next .replace (day = 1 + 7 + self .isoday - isoday )
347- else :
348- next = next .replace (day = 1 + self .isoday - isoday )
349-
350- next += timedelta (days = 7 * (self .week - 1 ))
351-
343+ next += timedelta (self .isoday - next .isoweekday ()) # Move to the named day (e.g., Monday)
344+ next += timedelta (days = 7 * (self .week - next .day // 7 - 1 )) # Move to the specified weekday (e.g., 2nd (Monday))
352345 if next >= now :
353346 break
354347
355348 next = next .replace (year = next .year + 1 )
356349
350+ self .next_send_time = next
357351 return next
358352
359353 def adjust (self , minimum : timedelta ) -> None :
360- raise NotImplementedError ( "Setting minimal period would break the definition of class." )
354+ pass # Slow-mode can't be one year long
361355
362356
363357@doc_category ("Message period" )
@@ -382,7 +376,7 @@ class NamedDayOfMonthPeriod(EveryXPeriod):
382376 Use ``datetime`` to specify the exact date and time at which the message should start being sent.
383377 Use ``timedelta`` to specify how soon (after creation of the object) the message
384378 should start being sent.
385-
379+
386380 Example
387381 ----------
388382 .. code-block:: python
@@ -401,39 +395,41 @@ def __init__(
401395 self ,
402396 time : time ,
403397 day : NamedDayOfMonthPeriod .DAYS ,
404- week : int ,
398+ week : Literal [ 1 , 2 , 3 , 4 , 5 ] ,
405399 next_send_time : Union [datetime , timedelta ] = None
406400 ) -> None :
407401 self .time = time
408402 self .day = day
409403 self .week = week
410404 self .isoday = NamedDayOfMonthPeriod ._DAYS_ARGS .index (day ) + 1
411405 super ().__init__ (next_send_time )
412-
406+
413407 def calculate (self ) -> datetime :
414408 now = max (datetime .now ().astimezone (), self .next_send_time )
415409 self_time = self .time
416410 next = now .replace (
417- day = 1 ,
418411 hour = self_time .hour ,
419412 minute = self_time .minute ,
420413 second = self_time .second ,
421414 microsecond = self_time .microsecond
422415 )
423416
424417 while True :
425- isoday = next .isoweekday ()
426- if isoday > self .isoday :
427- next = next .replace (day = 1 + 7 + self .isoday - isoday )
428- else :
429- next = next .replace (day = 1 + self .isoday - isoday )
430-
431- next += timedelta (days = 7 * (self .week - 1 ))
432-
418+ next += timedelta (self .isoday - next .isoweekday ()) # Move to the named day (e.g., Monday)
419+ next += timedelta (days = 7 * (self .week - next .day // 7 - 1 )) # Move to the specified weekday (e.g., 2nd (Monday))
433420 if next >= now :
434421 break
435422
436- next = next .replace (month = next .month + 1 )
423+ next_month = next .month + 1
424+ next_year = next .year
425+ if next_month > 12 :
426+ next_year += 1
427+ next_month = 1
428+
429+ next = next .replace (year = next_year , month = next_month )
430+
431+ self .next_send_time = next
432+ return next
437433
438434 def adjust (self , minimum : timedelta ) -> None :
439- raise NotImplementedError ( "Setting minimal period would break the definition of class." )
435+ pass # Slow-mode can't be one month long
0 commit comments