hat.util.cron

  1import datetime
  2import typing
  3
  4
  5class AllSubExpr(typing.NamedTuple):
  6    pass
  7
  8
  9class ValueSubExpr(typing.NamedTuple):
 10    value: int
 11
 12
 13class RangeSubExpr(typing.NamedTuple):
 14    from_: ValueSubExpr
 15    to: ValueSubExpr
 16
 17
 18class ListSubExpr(typing.NamedTuple):
 19    subexprs: list[ValueSubExpr]
 20
 21
 22SubExpr: typing.TypeAlias = (AllSubExpr |
 23                             ValueSubExpr |
 24                             RangeSubExpr |
 25                             ListSubExpr)
 26
 27
 28class Expr(typing.NamedTuple):
 29    minute: SubExpr
 30    hour: SubExpr
 31    day: SubExpr
 32    month: SubExpr
 33    day_of_week: SubExpr
 34
 35
 36def parse(expr_str: str) -> Expr:
 37    return Expr(*(_parse_subexpr(i) for i in expr_str.split(' ')))
 38
 39
 40def next(expr: Expr,
 41         t: datetime.datetime
 42         ) -> datetime.datetime:
 43    t = t.replace(second=0, microsecond=0)
 44
 45    while True:
 46        t = t + datetime.timedelta(minutes=1)
 47
 48        if match(expr, t):
 49            return t
 50
 51
 52def match(expr: Expr,
 53          t: datetime.datetime
 54          ) -> bool:
 55    if t.second or t.microsecond:
 56        return False
 57
 58    if not _match_subexpr(expr.minute, t.minute):
 59        return False
 60
 61    if not _match_subexpr(expr.hour, t.hour):
 62        return False
 63
 64    if not _match_subexpr(expr.day, t.day):
 65        return False
 66
 67    if not _match_subexpr(expr.month, t.month):
 68        return False
 69
 70    if not _match_subexpr(expr.day_of_week, t.isoweekday() % 7):
 71        return False
 72
 73    return True
 74
 75
 76def _parse_subexpr(subexpr_str):
 77    if subexpr_str == '*':
 78        return AllSubExpr()
 79
 80    if '-' in subexpr_str:
 81        from_str, to_str = subexpr_str.split('-')
 82        return RangeSubExpr(int(from_str), int(to_str))
 83
 84    if ',' in subexpr_str:
 85        return ListSubExpr([int(i) for i in subexpr_str.split(',')])
 86
 87    return ValueSubExpr(int(subexpr_str))
 88
 89
 90def _match_subexpr(subexpr, value):
 91    if isinstance(subexpr, AllSubExpr):
 92        return True
 93
 94    if isinstance(subexpr, ValueSubExpr):
 95        return value == subexpr.value
 96
 97    if isinstance(subexpr, RangeSubExpr):
 98        return subexpr.from_ <= value <= subexpr.to
 99
100    if isinstance(subexpr, ListSubExpr):
101        return value in subexpr.subexprs
102
103    raise ValueError('unsupported subexpression')
class AllSubExpr(typing.NamedTuple):
6class AllSubExpr(typing.NamedTuple):
7    pass

AllSubExpr()

AllSubExpr()

Create new instance of AllSubExpr()

Inherited Members
builtins.tuple
index
count
class ValueSubExpr(typing.NamedTuple):
10class ValueSubExpr(typing.NamedTuple):
11    value: int

ValueSubExpr(value,)

ValueSubExpr(value: int)

Create new instance of ValueSubExpr(value,)

value: int

Alias for field number 0

Inherited Members
builtins.tuple
index
count
class RangeSubExpr(typing.NamedTuple):
14class RangeSubExpr(typing.NamedTuple):
15    from_: ValueSubExpr
16    to: ValueSubExpr

RangeSubExpr(from_, to)

RangeSubExpr(from_: ValueSubExpr, to: ValueSubExpr)

Create new instance of RangeSubExpr(from_, to)

from_: ValueSubExpr

Alias for field number 0

Alias for field number 1

Inherited Members
builtins.tuple
index
count
class ListSubExpr(typing.NamedTuple):
19class ListSubExpr(typing.NamedTuple):
20    subexprs: list[ValueSubExpr]

ListSubExpr(subexprs,)

ListSubExpr(subexprs: list[ValueSubExpr])

Create new instance of ListSubExpr(subexprs,)

subexprs: list[ValueSubExpr]

Alias for field number 0

Inherited Members
builtins.tuple
index
count
SubExpr: TypeAlias = AllSubExpr | ValueSubExpr | RangeSubExpr | ListSubExpr
class Expr(typing.NamedTuple):
29class Expr(typing.NamedTuple):
30    minute: SubExpr
31    hour: SubExpr
32    day: SubExpr
33    month: SubExpr
34    day_of_week: SubExpr

Expr(minute, hour, day, month, day_of_week)

Create new instance of Expr(minute, hour, day, month, day_of_week)

Alias for field number 0

Alias for field number 1

Alias for field number 2

Alias for field number 3

Alias for field number 4

Inherited Members
builtins.tuple
index
count
def parse(expr_str: str) -> Expr:
37def parse(expr_str: str) -> Expr:
38    return Expr(*(_parse_subexpr(i) for i in expr_str.split(' ')))
def next(expr: Expr, t: datetime.datetime) -> datetime.datetime:
41def next(expr: Expr,
42         t: datetime.datetime
43         ) -> datetime.datetime:
44    t = t.replace(second=0, microsecond=0)
45
46    while True:
47        t = t + datetime.timedelta(minutes=1)
48
49        if match(expr, t):
50            return t
def match(expr: Expr, t: datetime.datetime) -> bool:
53def match(expr: Expr,
54          t: datetime.datetime
55          ) -> bool:
56    if t.second or t.microsecond:
57        return False
58
59    if not _match_subexpr(expr.minute, t.minute):
60        return False
61
62    if not _match_subexpr(expr.hour, t.hour):
63        return False
64
65    if not _match_subexpr(expr.day, t.day):
66        return False
67
68    if not _match_subexpr(expr.month, t.month):
69        return False
70
71    if not _match_subexpr(expr.day_of_week, t.isoweekday() % 7):
72        return False
73
74    return True