The project details and documentation can be found here; It also provides a online API for demonstration with rust implementation;
ChronoX is a project to extend functionalities of the conventional cron utility;
It provides more expressive power (day of year, month of week, span of time) by using expressions (let's call it cronx) similar to the conventional one;
Besides conventional next/prev 1 search, ChronoX provides convinient functions to directly and efficiently compute the next/prev n
occurence of desired pattern,
where n
could be extremely large! Most conventional implementations require loops which leads to a O(n) time complexity, whereas ChronoX utilizes a optimized algorithm to reduce the time complexity down to somewhere between constant and log, here's a casual benchmark between ChronoX rust implementation and a popular rust crate implement the conventional cron;
If you needs to express the following patterns:
- day of year
- week of month
- time span between start and end
or, you need to compute time N
leaps away,
ChronoX would be your right choice!
This is a python implementation of ChronoX. It requires using cronx, a cron like expression, see cronx guide below or detail explaination in docs.
pip install chronox-python
python >= 3.8
This package provides two main classes: ChronoX
for time point and ChronoXSpan
for time span.
The input and output should be encapsulated by datetime
class from datetime
module;
from datetime import datetime
ChronoX
class provides 3 main functions: prev
and next
to calculate previous and next time point respectively and contains
to check if the passed in datetime can be represented by the pattern.
from chronox import ChronoX
from datetime import datetime
cron = ChronoX("* * * 1,3,5 * * ; c")
assert cron.prev(datetime(2003, 11, 10, 6, 0, 6)) == datetime(2003, 11, 10, 5, 59, 59)
assert cron.prev(datetime(2003, 11, 10, 6, 0, 6), 1) == datetime(2003, 11, 10, 5, 59, 59)
assert cron.prev(datetime(2003, 11, 10, 6, 0, 6), leap=1) == datetime(2003, 11, 10, 5, 59, 59)
assert cron.prev(datetime(2003, 11, 10, 0, 0, 6), leap=10) == datetime(2003, 11, 9, 5, 59, 50)
assert cron.next(datetime(2003, 11, 10, 5, 59, 59)) == datetime(2003, 11, 11, 1, 0, 0)
# If current datetime represented by the cron
cron.contains(datetime.now())
# is equivalent to
datetime.now() in cron
parameter for datetime is optional and default to datetime.now()
ChronoXSpan
provides a main function contains
. It also provide start
and end
properties which reference decoded ChronoX instances for start and end pattern, you can utilize these two properties for calculation releted start or end pattern seperately.
from chronox import ChronoXSpan
from datetime import datetime
period = ChronoXSpan("* 1,3,5 * 3..5 0 0 0; m")
assert period.contains(datetime(2003, 5, 16, 0, 0, 0))
# is equivalent to
datetime(2003, 5, 16, 0, 0, 0).now() in period
# calculate next start time
period.start.next()
# calculate next end time
period.end.next()
This guide explains cronx expression by comparing it with the conventional cron; if you are unfamiliar with cron, please read about cron first, or go to cronx detail section in doc.
Beyond all the time pattern a conventional cron support, cronx also support the following pattern:
day of year
week of month
span of time
fromstart time pattern
toend time pattern
All week date definition follows ISO standard, see details
In order to support these extra time patterns, cronx introduces the following main differences from the conventional cron:
- explicit indicator for calendar mode
- different order of unit
- new character set
..
for span of time
Let's go through them one by one.
full # of units includes three clock units:
hour
,minute
,second
Token | Description | Calendar Combination | full # of units |
---|---|---|---|
d |
day of year | year , day of year |
5 |
w |
week of year | year , week of year , day of week |
6 |
m |
special month composed by week | year , month , week of month , day of week |
7 |
c |
common mode | year , month , day of month |
6 |
All indicator tokens should be at the end of expression string, and using ;
to seperate from the main expression.
Generally, cronx takes a reverse order compared to cron, that is from bigger unit to smaller one
mode | Order |
---|---|
d |
year day of year hour minute second |
w |
year week of year day of week hour minute second |
m |
year month week of month day of week hour minute second |
c |
year month day of month hour minute second |
This is a new concept, it represents a span or a block of time from a start to an end expressed by cronx, start and end occur in pairs, and connected by new character set ..
in cronx.
cronx | meaning |
---|---|
* * * * 0..15; d |
every minute (of every hour in every day in every year), from 0 second to 15 second |
* 10 1..5 8..10 .. 0; w |
from 8:00:00 in 10th Monday to 10:59:00 in 10th Friday in every year |
* 10 1 5 8.. .. ..; m |
first Friday in every October, from 8:00:00 to 23:59:59 |
* 10 * 8..10 .. ..; c |
every day in every October, from 8:00:00 to 10:59:59 |
This pattern has several rules as following:
- At least one explicit
..
unit pattern should appear; - Only
..
or single number unit pattern should appear after a..
; - If a single number pattern
s
occurs after..
, it will be expanded tos..s
automatically in this context; - You can omit integer on each side of
..
, default are0
andL1
, respectively; i.e.,..
equals0..L1
or1..L1
;
L1 means the last one;
L is a useful indicator to represent ordinal in reverse order. Since correct pattern range is essential for correct and fast computation, explicity is much desired; It is highly recommended to use L indicator when you need to represent numbers close to end of a unit range, especially for calendar units that the range could vary on different circumstances;
When use forms like a-b/c
, you may encouter with a situation that b
does not comply with the sequence which a
and c
define; in this context, the last number a-b/c
represents is max(a+c*i)
where i > 0 and i is integer;
i.e. 1-9/5 represents 1, 6
Second
and year
is not required, and to indicate year
, second
must be explicit first;
If U
is the full number of units for each mode (see table), U - 1
means year is implicit, U - 2
means year and second are implicit.
year
is always default to *
;
second
is default 0
in normal context, and 0..59
in span context;
expression | meaning |
---|---|
* 1,L1 8 *; d |
every minute of 8 clock in first and last day of every year |
2000 1 1 * */3 0 0; m |
every 3 hour of every day in first week of Jan. 2000 |
2000 L1 1,3,5 10 0 0; w |
every 10 O'Clock of Monday, Wednesday, Friday in last week of 2000 |
10 10 * *; c |
every minute (0 sec) in Oct. 10th, every year |
10 10 * * *; c |
every seconds in Oct. 10th, every year |
* * * * 0..15; d |
every minute (of every hour in every day in every year), from 0 second to 15 second |