Introduction
============
.. contents::
croniter provides iteration for the datetime object with a cron like format.
::
_ _
___ _ __ ___ _ __ (_) |_ ___ _ __
/ __| '__/ _ \| '_ \| | __/ _ \ '__|
| (__| | | (_) | | | | | || __/ |
\___|_| \___/|_| |_|_|\__\___|_|
Website: https://github.com/kiorky/croniter
Build Badge
===========
.. image:: https://github.com/kiorky/croniter/actions/workflows/cicd.yml/badge.svg
:target: https://github.com/kiorky/croniter/actions/workflows/cicd.yml
Usage
============
A simple example::
>>> from croniter import croniter
>>> from datetime import datetime
>>> base = datetime(2010, 1, 25, 4, 46)
>>> iter = croniter('*/5 * * * *', base) # every 5 minutes
>>> print(iter.get_next(datetime)) # 2010-01-25 04:50:00
>>> print(iter.get_next(datetime)) # 2010-01-25 04:55:00
>>> print(iter.get_next(datetime)) # 2010-01-25 05:00:00
>>>
>>> iter = croniter('2 4 * * mon,fri', base) # 04:02 on every Monday and Friday
>>> print(iter.get_next(datetime)) # 2010-01-26 04:02:00
>>> print(iter.get_next(datetime)) # 2010-01-30 04:02:00
>>> print(iter.get_next(datetime)) # 2010-02-02 04:02:00
>>>
>>> iter = croniter('2 4 1 * wed', base) # 04:02 on every Wednesday OR on 1st day of month
>>> print(iter.get_next(datetime)) # 2010-01-27 04:02:00
>>> print(iter.get_next(datetime)) # 2010-02-01 04:02:00
>>> print(iter.get_next(datetime)) # 2010-02-03 04:02:00
>>>
>>> iter = croniter('2 4 1 * wed', base, day_or=False) # 04:02 on every 1st day of the month if it is a Wednesday
>>> print(iter.get_next(datetime)) # 2010-09-01 04:02:00
>>> print(iter.get_next(datetime)) # 2010-12-01 04:02:00
>>> print(iter.get_next(datetime)) # 2011-06-01 04:02:00
>>>
>>> iter = croniter('0 0 * * sat#1,sun#2', base) # 1st Saturday, and 2nd Sunday of the month
>>> print(iter.get_next(datetime)) # 2010-02-06 00:00:00
>>>
>>> iter = croniter('0 0 * * 5#3,L5', base) # 3rd and last Friday of the month
>>> print(iter.get_next(datetime)) # 2010-01-29 00:00:00
>>> print(iter.get_next(datetime)) # 2010-02-19 00:00:00
All you need to know is how to use the constructor and the ``get_next``
method, the signature of these methods are listed below::
>>> def __init__(self, cron_format, start_time=time.time(), day_or=True)
croniter iterates along with ``cron_format`` from ``start_time``.
``cron_format`` is **min hour day month day_of_week**, you can refer to
http://en.wikipedia.org/wiki/Cron for more details. The ``day_or``
switch is used to control how croniter handles **day** and **day_of_week**
entries. Default option is the cron behaviour, which connects those
values using **OR**. If the switch is set to False, the values are connected
using **AND**. This behaves like fcron and enables you to e.g. define a job that
executes each 2nd Friday of a month by setting the days of month and the
weekday.
::
>>> def get_next(self, ret_type=float)
get_next calculates the next value according to the cron expression and
returns an object of type ``ret_type``. ``ret_type`` should be a ``float`` or a
``datetime`` object.
Supported added for ``get_prev`` method. (>= 0.2.0)::
>>> base = datetime(2010, 8, 25)
>>> itr = croniter('0 0 1 * *', base)
>>> print(itr.get_prev(datetime)) # 2010-08-01 00:00:00
>>> print(itr.get_prev(datetime)) # 2010-07-01 00:00:00
>>> print(itr.get_prev(datetime)) # 2010-06-01 00:00:00
You can validate your crons using ``is_valid`` class method. (>= 0.3.18)::
>>> croniter.is_valid('0 0 1 * *') # True
>>> croniter.is_valid('0 wrong_value 1 * *') # False
About DST
=========
Be sure to init your croniter instance with a TZ aware datetime for this to work!
Example using pytz::
>>> import pytz
>>> tz = pytz.timezone("Europe/Paris")
>>> local_date = tz.localize(datetime(2017, 3, 26))
>>> val = croniter('0 0 * * *', local_date).get_next(datetime)
Example using python_dateutil::
>>> import dateutil.tz
>>> tz = dateutil.tz.gettz('Asia/Tokyo')
>>> local_date = datetime(2017, 3, 26, tzinfo=tz)
>>> val = croniter('0 0 * * *', local_date).get_next(datetime)
Example using python built in module::
>>> from datetime import datetime, timezone
>>> local_date = datetime(2017, 3, 26, tzinfo=timezone.utc)
>>> val = croniter('0 0 * * *', local_date).get_next(datetime)
About second repeats
=====================
Croniter is able to do second repetition crontabs form and by default seconds are the 6th field::
>>> base = datetime(2012, 4, 6, 13, 26, 10)
>>> itr = croniter('* * * * * 15,25', base)
>>> itr.get_next(datetime) # 4/6 13:26:15
>>> itr.get_next(datetime) # 4/6 13:26:25
>>> itr.get_next(datetime) # 4/6 13:27:15
You can also note that this expression will repeat every second from the start datetime.::
>>> croniter('* * * * * *', local_date).get_next(datetime)
You can also use seconds as first field::
>>> itr = croniter('15,25 * * * * *', base, second_at_beginning=True)
About year
===========
Croniter also support year field.
Year presents at the seventh field, which is after second repetition.
The range of year field is from 1970 to 2099.
To ignore second repetition, simply set second to ``0`` or any other const::
>>> base = datetime(2012, 4, 6, 2, 6, 59)
>>> itr = croniter('0 0 1 1 * 0 2020/2', base)
>>> itr.get_next(datetime) # 2020 1/1 0:0:0
>>> itr.get_next(datetime) # 2022 1/1 0:0:0
>>> itr.get_next(datetime) # 2024 1/1 0:0:0
Support for start_time shifts
==============================
See https://github.com/kiorky/croniter/pull/76,
You can set start_time=, then expand_from_start_time=True for your generations to be computed from start_time instead of calendar days::
>>> from pprint import pprint
>>> iter = croniter('0 0 */7 * *', start_time=datetime(2024, 7, 11), expand_from_start_time=True);pprint([iter.get_next(datetime) for a in range(10)])
[datetime.datetime(2024, 7, 18, 0, 0),
datetime.datetime(2024, 7, 25, 0, 0),
datetime.datetime(2024, 8, 4, 0, 0),
datetime.datetime(2024, 8, 11, 0, 0),
datetime.datetime(2024, 8, 18, 0, 0),
datetime.datetime(2024, 8, 25, 0, 0),
datetime.datetime(2024, 9, 4, 0, 0),
datetime.datetime(2024, 9, 11, 0, 0),
datetime.datetime(2024, 9, 18, 0, 0),
datetime.datetime(2024, 9, 25, 0, 0)]
>>> # INSTEAD OF THE DEFAULT BEHAVIOR:
>>> iter = croniter('0 0 */7 * *', start_time=datetime(2024, 7, 11), expand_from_start_time=False);pprint([iter.get_next(datetime) for a in range(10)])
[datetime.datetime(2024, 7, 15, 0, 0),
datetime.datetime(2024, 7, 22, 0, 0),
datetime.datetime(2024, 7, 29, 0, 0),
datetime.datetime(2024, 8, 1, 0, 0),
datetime.datetime(2024, 8, 8, 0, 0),
datetime.datetime(2024, 8, 15, 0, 0),
datetime.datetime(2024, 8, 22, 0, 0),
datetime.datetime(2024, 8, 29, 0, 0),
datetime.datetime(2024, 9, 1, 0, 0),
datetime.datetime(2024, 9, 8, 0, 0)]
Testing if a date matches a crontab
===================================
Test for a match with (>=0.3.32)::
>>> croniter.match("0 0 * * *", datetime(2019, 1, 14, 0, 0, 0, 0))
True
>>> croniter.match("0 0 * * *", datetime(2019, 1, 14, 0, 2, 0, 0))
False
>>>
>>> croniter.match("2 4 1 * wed", datetime(2019, 1, 1, 4, 2, 0, 0)) # 04:02 on every Wednesday OR on 1st day of month
True
>>> croniter.match("2 4 1 * wed", datetime(2019, 1, 1, 4, 2, 0, 0), day_or=False) # 04:02 on every 1st day of the month if it is a Wednesday
False
Testing if a crontab matches in datetime range
==============================================
Test for a match_range with (>=2.0.3)::
>>> croniter.match_range("0 0 * * *", datetime(2019, 1, 13, 0, 59, 0, 0), datetime(2019, 1, 14, 0, 1, 0, 0))
True
>>> croniter.match_range("0 0 * * *", datetime(2019, 1, 13, 0, 1, 0, 0), datetime(2019, 1, 13, 0, 59, 0, 0))
False
>>> croniter.match_range("2 4 1 * wed", datetime(2019, 1, 1, 3, 2, 0, 0), datetime(2019, 1, 1, 5, 1, 0, 0))
# 04:02 on every Wednesday OR on 1st day of month
True
>>> croniter.match_range("2 4 1 * wed", datetime(2019, 1, 1, 3, 2, 0, 0), datetime(2019, 1, 1, 5, 2, 0, 0), day_or=False)
# 04:02 on every 1st day of the month if it is a Wednesday
False
Gaps between date matches
=========================
For performance reasons, croniter limits the amount of CPU cycles spent attempting to find the next match.
Starting in v0.3.35, this behavior is configurable via the ``max_years_between_matches`` parameter, and the default window has been increased from 1 year to 50 years.
The defaults should be fine for many use cases.
Applications that evaluate multiple cron expressions or handle cron expressions from untrusted sources or end-users should use this parameter.
Iterating over sparse cron expressions can result in increased CPU consumption or a raised ``CroniterBadDateError`` exception which indicates that croniter has given up attempting to find the next (or previous) match.
Explicitly specifying ``max_years_between_matches`` provides a way to limit CPU utilization and simplifies the iterable interface by eliminating the need for ``CroniterBadDateError``.
The difference in the iterable interface is based on the reasoning that whenever ``max_years_between_matches`` is explicitly agreed upon, there is no need for croniter to signal that it has given up; simply stopping the iteration is preferable.
This example matches 4 AM Friday, January 1st.
Since January 1st isn't often a Friday, there may be a few years between each occurrence.
Setting the limit to 15 years ensures all matches::
>>> it = croniter("0 4 1 1 fri", datetime(2000,1,1), day_or=False, max_years_between_matches=15).all_next(datetime)
>>> for i in range(5):
... print(next(it))
...
2010-01-01 04:00:00
2016-01-01 04:00:00
2021-01-01 04:00:00
2027-01-01 04:00:00
2038-01-01 04:00:00
However, when only concerned with dates within the next 5 years, simply set ``max_years_between_matches=5`` in the above example.
This will result in no matches found, but no additional cycles will be wasted on unwanted matches far in the future.
Iterating over a range using cron
=================================
Find matches within a range using the ``croniter_range()`` function. This is much like the builtin ``range(start,stop,step)`` function, but for dates. The `step` argument is a cron expression.
Added in (>=0.3.34)
List the first Saturday of every month in 2019::
>>> from croniter import croniter_range
>>> for dt in croniter_range(datetime(2019, 1, 1), datetime(2019, 12, 31), "0 0 * * sat#1"):
>>> print(dt)
Hashed expressions
==================
croniter supports Jenkins-style hashed expressions, using the "H" definition keyword and the required hash_id keyword argument.
Hashed expressions remain consistent, given the same hash_id, but different hash_ids will evaluate completely different to each other.
This allows, for example, for an even distribution of differently-named jobs without needing to manually spread them out.
>>> itr = croniter("H H * * *", hash_id="hello")
>>> itr.get_next(datetime)
datetime.datetime(2021, 4, 10, 11, 10)
>>> itr.get_next(datetime)
datetime.datetime(2021, 4, 11, 11, 10)
>>> itr = croniter("H H * * *", hash_id="hello")
>>> itr.get_next(datetime)
datetime.datetime(2021, 4, 10, 11, 10)
>>> itr = croniter("H H * * *", hash_id="bonjour")
>>> itr.get_next(datetime)
datetime.datetime(2021, 4, 10, 20, 52)
Random expressions
==================
Random "R" definition keywords are supported, and remain consistent only within their croniter() instance.
>>> itr = croniter("R R * * *")
>>> itr.get_next(datetime)
datetime.datetime(2021, 4, 10, 22, 56)
>>> itr.get_next(datetime)
datetime.datetime(2021, 4, 11, 22, 56)
>>> itr = croniter("R R * * *")
>>> itr.get_next(datetime)
datetime.datetime(2021, 4, 11, 4, 19)
Note about Ranges
=================
Note that as a deviation from cron standard, croniter is somehow laxist with ranges and will allow ranges of ``Jan-Dec``, & ``Sun-Sat`` in reverse way and interpret them as following examples:
- ``Apr-Jan``: from April to january
- ``Sat-Sun``: Saturday, Sunday
- ``Wed-Sun``: Wednesday to Saturday, Sunday
Please note that if a /step is given, it will be respected.
Note about Sunday
=================
Note that as a deviation from cron standard, croniter like numerous cron implementations supports ``SUNDAY`` to be expressed as ``DAY7``, allowing such expressions:
- ``0 0 * * 7``
- ``0 0 * * 6-7``
- ``0 0 * * 6,7``
Keyword expressions
===================
Vixie cron-style "@" keyword expressions are supported.
What they evaluate to depends on whether you supply hash_id: no hash_id corresponds to Vixie cron definitions (exact times, minute resolution), while with hash_id corresponds to Jenkins definitions (hashed within the period, second resolution).
============ ============ ================
Keyword No hash_id With hash_id
============ ============ ================
@midnight 0 0 * * * H H(0-2) * * * H
@hourly 0 * * * * H * * * * H
@daily 0 0 * * * H H * * * H
@weekly 0 0 * * 0 H H * * H H
@monthly 0 0 1 * * H H H * * H
@yearly 0 0 1 1 * H H H H * H
@annually 0 0 1 1 * H H H H * H
============ ============ ================
Upgrading
==========
To 2.0.0
---------
- Install or upgrade pytz by using version specified requirements/base.txt if you have it installed `<=2021.1`.
Develop this package
====================
::
git clone https://github.com/kiorky/croniter.git
cd croniter
virtualenv --no-site-packages venv3
venv3/bin/pip install --upgrade -r requirements/test.txt -r requirements/lint.txt -r requirements/tox.txt
venv3/bin/tox --current-env -e lint,test
Testing under py2
==================
Install prerequisisites ::
# install py 2 with eg: apt install python2.7
mkdir venv2 && curl -sSL "https://github.com/pypa/get-virtualenv/blob/20.27.0/public/2.7/virtualenv.pyz?raw=true" > venv2/venv && python2 venv2/venv venv2
venv2/bin/python2 -m pip install -r ./requirements/test.txt
Run tests::
./venv2/bin/pytest src
Make a new release
====================
We use zest.fullreleaser, a great release infrastructure.
Do and follow these instructions
::
venv3/bin/pip install --upgrade -r requirements/release.txt
./release.sh
Contributors
===============
Thanks to all who have contributed to this project!
If you have contributed and your name is not listed below please let us know.
- Aarni Koskela (akx)
- mrmachine
- Hinnack
- shazow
- kiorky
- jlsandell
- mag009
- djmitche
- GreatCombinator
- chris-baynes
- ipartola
- yuzawa-san
- lowell80 (Kintyre)
- scop
- zed2015
- Ryan Finnie (rfinnie)
- salitaba
Changelog
==============
5.0.1 (2024-10-29)
------------------
- Community wanted: Reintroduce 7 as DayOfWeek in deviation from standard cron (#90). [kiorky]
4.0.0 (2024-10-28)
------------------
- Remove DayOfWeek alias 7 to DayOfWeek 0 to stick to standard cron (#90). [kiorky]
- Fix DOW ranges calculations when lastday is a Sunday. [kiorky]
3.0.4 (2024-10-25)
------------------
- Fix overflow on 32bits systems (#87) [kiorky]
- Fix python2 testing (related to #93) [kiorky]
- Modernize packaging. Special thanks to Aarni Koskela (akx) for all the inputs. [kiorky, akx]
3.0.3 (2024-07-26)
------------------
- fix lint [kiorky]
3.0.2 (2024-07-26)
------------------
- Fix start_time not respected in get_next/get_prev/all_next/all_prev (#86) [hesstobi, kiorky]
3.0.1 (2024-07-25)
------------------
- Add an `update_current` argument to get_next/get_prev/all_next/all_prev to facilitate writing of some downstream code, see #83. [kiorky]
3.0.0 (2024-07-23)
------------------
- Support for year field [zhouyizhen, kiorky]
- Better support for 6 fields (second), and 7 fields crons [zhouyizhen, kiorky]
- Better fix hashed expressions omitting some entries (#82, #42, #30) fix is retained over #42 initial fix [zhouyizhen, kiorky]
- Ensure match return false when not time available (#81) [zhouyizhen, kiorky]
2.0.7 (2024-07-16)
------------------
- fix doc
2.0.6 (2024-07-16)
------------------
- Implement second_at_beginning [zhouyizhen, kiorky]
- Support question mark as wildcard [zhouyizhen, kiorky]
- Support to start a cron from a reference start time [mghextreme, kiorky]
2.0.5 (2024-04-20)
------------------
- No changes, fix lint [kiorky]
2.0.4 (2024-04-20)
------------------
- Support hashid strings in is_valid [george-kuanli-peng, kiorky]
- Avoid over-optimization in crontab expansions [Cherie0125, liqirui <liqirui@baidu.com>, kiorky]
2.0.3 (2024-03-19)
------------------
- Add match_range function [salitaba]
2.0.2 (2024-02-29)
------------------
- fix leap year (29 days in February) [zed2015]
2.0.1 (2023-10-11)
------------------
- Fix release issue [kiorky]
2.0.0 (2023-10-10)
------------------
- Add Python 3.12 support [rafsaf]
- Make major release instructions [kiorky]
1.4.1 (2023-06-15)
------------------
- Make a retrocompatible version of 1.4.0 change about supporting VIXIECRON bug. (fix #47)
[kiorky]
1.4.0 (2023-06-15)
------------------
- Added "implement_cron_bug" flag to make the cron parser compatible with a bug in Vixie/ISC Cron
[kiorky, David White <dwhite2@cisco.com>]
*WARNING*: EXPAND METHOD CHANGES RETURN VALUE
1.3.15 (2023-05-25)
-------------------
- Fix hashed expressions omitting some entries
[@waltervos/Walter Vos <walter.vos@ns.nl>]
- Enhance .match() precision for 6 position expressions
[@szpol/szymon <szymon.polinkiewicz@gmail.com>]
1.3.14 (2023-04-12)
-------------------
- Lint
1.3.13 (2023-04-12)
-------------------
- Add check for range begin/end
1.3.12 (2023-04-12)
-------------------
- restore py2 compat
1.3.11 (2023-04-12)
-------------------
- Do not expose `i` into global namespace
1.3.10 (2023-04-07)
-------------------
- Fix DOW hash parsing [kiorky]
- better error handling on py3 [kiorky]
1.3.8 (2022-11-22)
------------------
- Add Python 3.11 support and move docs files to main folder [rafsaf]
1.3.7 (2022-09-06)
------------------
- fix tests
- Fix croniter_range infinite loop [Shachar Snapiri <ssnapiri@paloaltonetworks.com>]
1.3.5 (2022-05-14)
------------------
- Add Python 3.10 support [eelkevdbos]
1.3.4 (2022-02-18)
------------------
- Really fix compat for tests under py27
[kiorky]
1.3.3 (2022-02-18)
------------------
- Fix compat for tests under py27
[kiorky]
1.3.2 (2022-02-18)
------------------
- Fix #12: regressions with set_current
[kiorky, agateblue]
1.3.1 (2022-02-15)
------------------
- Restore compat with python2
[kiorky]
1.3.0 (2022-02-15)
------------------
- Add a way to make next() easier to use. This fixes #11
[kiorky]
1.2.0 (2022-01-14)
------------------
- Enforce validation for day=1. Before this release we used to support day=0 and it was silently glided to day=1 to support having both day in day in 4th field when it came to have 6fields cron forms (second repeat). It will now raises a CroniterBadDateError. See https://github.com/kiorky/croniter/issues/6
[kiorky]
1.1.0 (2021-12-03)
------------------
- Enforce validation for month=1. Before this release we used to support month=0 and it was silently glided to month=1 to support having both day in month in 4th field when it came to have 6fields cron forms (second repeat). It will now raises a CroniterBadDateError. See https://github.com/kiorky/croniter/issues/6
[kiorky]
1.0.15 (2021-06-25)
-------------------
- restore py2 [kiorky]
1.0.14 (2021-06-25)
-------------------
- better type checks [kiorky]
1.0.13 (2021-05-06)
-------------------
- Fix ZeroDivisionError with ``* * R/0 * *``
[cuu508]
1.0.12 (2021-04-13)
-------------------
- Add support for hashed/random/keyword expressions
Ryan Finnie (rfinnie)
- Review support support for hashed/random/keyword expression and add expanders reactor
[ kiorky ]
1.0.11 (2021-04-07)
-------------------
- fix bug: bad case:``0 6 30 3 *``
[zed2015(zhangchi)]
- Add support for ``L`` in the day_of_week component. This enable expressions like ``* * * * L4``, which means last Thursday of the month. This resolves #159.
[Kintyre]
- Create ``CroniterUnsupportedSyntaxError`` exception for situations where CRON syntax may be valid but some combinations of features is not supported.
Currently, this is used when the ``day_of_week`` component has a combination of literal values and nth/last syntax at the same time.
For example, ``0 0 * * 1,L6`` or ``0 0 * * 15,sat#1`` will both raise this exception because of mixing literal days of the week with nth-weekday or last-weekday syntax.
This *may* impact existing cron expressions in prior releases, because ``0 0 * * 15,sat#1`` was previously allowed but incorrectly handled.
[Kintyre]
- Update ``croniter_range()`` to allow an alternate ``croniter`` class to be used. Helpful when using a custom class derived from croniter.
[Kintyre]
1.0.10 (2021-03-25)
-------------------
- Remove external library ``natsort``.
Sorting of cron expression components now handled with ``sorted()`` with a custom ``key`` function.
[Kintyre]
1.0.9 (2021-03-23)
------------------
- Remove futures dependency
[kiorky]
1.0.8 (2021-03-06)
------------------
- Update `_expand` to lowercase each component of the expression.
This is in relation to #157. With this change, croniter accepts and correctly handles `* * 10-L * *`.
[cuu508]
1.0.7 (2021-03-02)
------------------
- Fix _expand to reject int literals with underscores
[cuu508]
- Remove a debug statement to make flake8 happy
[cuu508]
1.0.6 (2021-02-01)
------------------
- Fix combination of star and invalid expression bugs
[kiorky]
1.0.5 (2021-01-29)
------------------
- Security fix: fix overflow when using cron ranges
[kiorky]
1.0.4 (2021-01-29)
------------------
- Spelling fix release
1.0.3 (2021-01-29)
------------------
- Fix #155: raise CroniterBadCronError when error syntax
[kiorky]
1.0.2 (2021-01-19)
------------------
- Fix match when datetime has microseconds
[kiorky]
1.0.1 (2021-01-06)
------------------
- no changes, just to make sense with new semver2 (making croniter on a stable state)
[kiorky]
0.3.37 (2020-12-31)
-------------------
- Added Python 3.8 and 3.9 support
[eumiro]
0.3.36 (2020-11-02)
-------------------
- Updated docs section regarding ``max_years_between_matches`` to be more shorter and hopefully more relevant.
[Kintyre]
- Don't install tests
[scop]
0.3.35 (2020-10-11)
-------------------
- Handle L in ranges. This fixes #142.
[kiorky]
- Add a new initialization parameter ``max_years_between_matches`` to support finding the next/previous date beyond the default 1 year window, if so desired. Updated README to include additional notes and example of this usage. Fixes #145.
[Kintyre]
- The ``croniter_range()`` function was updated to automatically determines the appropriate ``max_years_between_matches`` value, this preventing handling of the ``CroniterBadDateError`` exception.
[Kintyre]
- Updated exception handling classes: ``CroniterBadDateError`` now only
applies during date finding operations (next/prev), and all parsing errors can now be caught using ``CroniterBadCronError``. The ``CroniterNotAlphaError`` exception is now a subclass of ``CroniterBadCronError``. A brief description of each exception class was added as an inline docstring.
[Kintyre]
- Updated iterable interfaces to replace the ``CroniterBadDateError`` with ``StopIteration`` if (and only if) the ``max_years_between_matches`` argument is provided. The rationale here is that if the user has specified the max tolerance between matches, then there's no need to further inform them of no additional matches. Just stop the iteration. This also keeps backwards compatibility.
[Kintyre]
- Minor docs update
[Kintyre]
0.3.34 (2020-06-19)
-------------------
- Feat ``croniter_range(start, stop, cron)``
[Kintyre]
- Optimization for poorly written cron expression
[Kintyre]
0.3.33 (2020-06-15)
-------------------
- Make dateutil tz support more official
[Kintyre]
- Feat/support for day or
[田口信元]
0.3.32 (2020-05-27)
-------------------
- document seconds repeats, fixes #122
[kiorky]
- Implement match method, fixes #54
[kiorky]
- Adding tests for #127 (test more DSTs and croniter behavior around)
[kiorky]
- Changed lag_hours comparison to absolute to manage dst boundary when getting previous
[Sokkka]
0.3.31 (2020-01-02)
-------------------
- Fix get_next() when start_time less then 1s before next instant
[AlexHill]
0.3.30 (2019-04-20)
-------------------
- credits
0.3.29 (2019-03-26)
-------------------
- credits
- history stripping (security)
- Handle -Sun notation, This fixes `#119 <https://github.com/taichino/croniter/issues/119>`_.
[kiorky]
- Handle invalid ranges correctly, This fixes `#114 <https://github.com/taichino/croniter/issues/114>`_.
[kiorky]
0.3.25 (2018-08-07)
-------------------
- Pypi hygiene
[hugovk]
0.3.24 (2018-06-20)
-------------------
- fix `#107 <https://github.com/taichino/croniter/issues/107>`_: microsecond threshold
[kiorky]
0.3.23 (2018-05-23)
-------------------
- fix ``get_next`` while preserving the fix of ``get_prev`` in 7661c2aaa
[Avikam Agur <avikam@pagaya-inv.com>]
0.3.22 (2018-05-16)
-------------------
- Don't count previous minute if now is dynamic
If the code is triggered from 5-asterisk based cron
``get_prev`` based on ``datetime.now()`` is expected to return
current cron iteration and not previous execution.
[Igor Khrol <igor.khrol@toptal.com>]
0.3.20 (2017-11-06)
-------------------
- More DST fixes
[Kevin Rose <kbrose@github>]
0.3.19 (2017-08-31)
-------------------
- fix #87: backward dst changes
[kiorky]
0.3.18 (2017-08-31)
-------------------
- Add is valid method, refactor errors
[otherpirate, Mauro Murari <mauro_murari@hotmail.com>]
0.3.17 (2017-05-22)
-------------------
- DOW occurrence sharp style support.
[kiorky, Kengo Seki <sekikn@apache.org>]
0.3.16 (2017-03-15)
-------------------
- Better test suite [mrcrilly@github]
- DST support [kiorky]
0.3.15 (2017-02-16)
-------------------
- fix bug around multiple conditions and range_val in
_get_prev_nearest_diff.
[abeja-yuki@github]
0.3.14 (2017-01-25)
-------------------
- issue #69: added day_or option to change behavior when day-of-month and
day-of-week is given
[Andreas Vogl <a.vogl@hackner-security.com>]
0.3.13 (2016-11-01)
-------------------
- `Real fix for #34 <https://github.com/taichino/croniter/pull/73>`_
[kiorky@github]
- `Modernize test infra <https://github.com/taichino/croniter/pull/72>`_
[kiorky@github]
- `Release as a universal wheel <https://github.com/kiorky/croniter/pull/16>`_
[adamchainz@github]
- `Raise ValueError on negative numbers <https://github.com/taichino/croniter/pull/63>`_
[josegonzalez@github]
- `Compare types using "issubclass" instead of exact match <https://github.com/taichino/croniter/pull/70>`_
[darkk@github]
- `Implement step cron with a variable base <https://github.com/taichino/croniter/pull/60>`_
[josegonzalez@github]
0.3.12 (2016-03-10)
-------------------
- support setting ret_type in __init__ [Brent Tubbs <brent.tubbs@gmail.com>]
0.3.11 (2016-01-13)
-------------------
- Bug fix: The get_prev API crashed when last day of month token was used. Some
essential logic was missing.
[Iddo Aviram <iddo.aviram@similarweb.com>]
0.3.10 (2015-11-29)
-------------------
- The functionality of 'l' as day of month was broken, since the month variable
was not properly updated
[Iddo Aviram <iddo.aviram@similarweb.com>]
0.3.9 (2015-11-19)
------------------
- Don't use datetime functions python 2.6 doesn't support
[petervtzand]
0.3.8 (2015-06-23)
------------------
- Truncate microseconds by setting to 0
[Corey Wright]
0.3.7 (2015-06-01)
------------------
- converting sun in range sun-thu transforms to int 0 which is
recognized as empty string; the solution was to convert sun to string "0"
0.3.6 (2015-05-29)
------------------
- Fix default behavior when no start_time given
Default value for ``start_time`` parameter is calculated at module init time rather than call time.
- Fix timezone support and stop depending on the system time zone
0.3.5 (2014-08-01)
------------------
- support for 'l' (last day of month)
0.3.4 (2014-01-30)
------------------
- Python 3 compat
- QA Release
0.3.3 (2012-09-29)
------------------
- proper packaging
Raw data
{
"_id": null,
"home_page": "http://github.com/kiorky/croniter",
"name": "croniter",
"maintainer": null,
"docs_url": null,
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.6",
"maintainer_email": null,
"keywords": "datetime, iterator, cron",
"author": "Matsumoto Taichi, kiorky",
"author_email": "taichino@gmail.com, kiorky@cryptelium.net",
"download_url": "https://files.pythonhosted.org/packages/a7/8c/0656200bfa5c1e90b26f4bb1cc8aecb4a7722f8386ee044bdc2d4efb589e/croniter-5.0.1.tar.gz",
"platform": null,
"description": "Introduction\n============\n\n.. contents::\n\n\ncroniter provides iteration for the datetime object with a cron like format.\n\n::\n\n _ _\n ___ _ __ ___ _ __ (_) |_ ___ _ __\n / __| '__/ _ \\| '_ \\| | __/ _ \\ '__|\n | (__| | | (_) | | | | | || __/ |\n \\___|_| \\___/|_| |_|_|\\__\\___|_|\n\n\nWebsite: https://github.com/kiorky/croniter\n\nBuild Badge\n===========\n.. image:: https://github.com/kiorky/croniter/actions/workflows/cicd.yml/badge.svg\n :target: https://github.com/kiorky/croniter/actions/workflows/cicd.yml\n\n\nUsage\n============\n\nA simple example::\n\n >>> from croniter import croniter\n >>> from datetime import datetime\n >>> base = datetime(2010, 1, 25, 4, 46)\n >>> iter = croniter('*/5 * * * *', base) # every 5 minutes\n >>> print(iter.get_next(datetime)) # 2010-01-25 04:50:00\n >>> print(iter.get_next(datetime)) # 2010-01-25 04:55:00\n >>> print(iter.get_next(datetime)) # 2010-01-25 05:00:00\n >>>\n >>> iter = croniter('2 4 * * mon,fri', base) # 04:02 on every Monday and Friday\n >>> print(iter.get_next(datetime)) # 2010-01-26 04:02:00\n >>> print(iter.get_next(datetime)) # 2010-01-30 04:02:00\n >>> print(iter.get_next(datetime)) # 2010-02-02 04:02:00\n >>>\n >>> iter = croniter('2 4 1 * wed', base) # 04:02 on every Wednesday OR on 1st day of month\n >>> print(iter.get_next(datetime)) # 2010-01-27 04:02:00\n >>> print(iter.get_next(datetime)) # 2010-02-01 04:02:00\n >>> print(iter.get_next(datetime)) # 2010-02-03 04:02:00\n >>>\n >>> iter = croniter('2 4 1 * wed', base, day_or=False) # 04:02 on every 1st day of the month if it is a Wednesday\n >>> print(iter.get_next(datetime)) # 2010-09-01 04:02:00\n >>> print(iter.get_next(datetime)) # 2010-12-01 04:02:00\n >>> print(iter.get_next(datetime)) # 2011-06-01 04:02:00\n >>>\n >>> iter = croniter('0 0 * * sat#1,sun#2', base) # 1st Saturday, and 2nd Sunday of the month\n >>> print(iter.get_next(datetime)) # 2010-02-06 00:00:00\n >>>\n >>> iter = croniter('0 0 * * 5#3,L5', base) # 3rd and last Friday of the month\n >>> print(iter.get_next(datetime)) # 2010-01-29 00:00:00\n >>> print(iter.get_next(datetime)) # 2010-02-19 00:00:00\n\n\nAll you need to know is how to use the constructor and the ``get_next``\nmethod, the signature of these methods are listed below::\n\n >>> def __init__(self, cron_format, start_time=time.time(), day_or=True)\n\ncroniter iterates along with ``cron_format`` from ``start_time``.\n``cron_format`` is **min hour day month day_of_week**, you can refer to\nhttp://en.wikipedia.org/wiki/Cron for more details. The ``day_or``\nswitch is used to control how croniter handles **day** and **day_of_week**\nentries. Default option is the cron behaviour, which connects those\nvalues using **OR**. If the switch is set to False, the values are connected\nusing **AND**. This behaves like fcron and enables you to e.g. define a job that\nexecutes each 2nd Friday of a month by setting the days of month and the\nweekday.\n::\n\n >>> def get_next(self, ret_type=float)\n\nget_next calculates the next value according to the cron expression and\nreturns an object of type ``ret_type``. ``ret_type`` should be a ``float`` or a\n``datetime`` object.\n\nSupported added for ``get_prev`` method. (>= 0.2.0)::\n\n >>> base = datetime(2010, 8, 25)\n >>> itr = croniter('0 0 1 * *', base)\n >>> print(itr.get_prev(datetime)) # 2010-08-01 00:00:00\n >>> print(itr.get_prev(datetime)) # 2010-07-01 00:00:00\n >>> print(itr.get_prev(datetime)) # 2010-06-01 00:00:00\n\nYou can validate your crons using ``is_valid`` class method. (>= 0.3.18)::\n\n >>> croniter.is_valid('0 0 1 * *') # True\n >>> croniter.is_valid('0 wrong_value 1 * *') # False\n\nAbout DST\n=========\nBe sure to init your croniter instance with a TZ aware datetime for this to work!\n\nExample using pytz::\n\n >>> import pytz\n >>> tz = pytz.timezone(\"Europe/Paris\")\n >>> local_date = tz.localize(datetime(2017, 3, 26))\n >>> val = croniter('0 0 * * *', local_date).get_next(datetime)\n\nExample using python_dateutil::\n\n >>> import dateutil.tz\n >>> tz = dateutil.tz.gettz('Asia/Tokyo')\n >>> local_date = datetime(2017, 3, 26, tzinfo=tz)\n >>> val = croniter('0 0 * * *', local_date).get_next(datetime)\n\nExample using python built in module::\n\n >>> from datetime import datetime, timezone\n >>> local_date = datetime(2017, 3, 26, tzinfo=timezone.utc)\n >>> val = croniter('0 0 * * *', local_date).get_next(datetime)\n\nAbout second repeats\n=====================\nCroniter is able to do second repetition crontabs form and by default seconds are the 6th field::\n\n >>> base = datetime(2012, 4, 6, 13, 26, 10)\n >>> itr = croniter('* * * * * 15,25', base)\n >>> itr.get_next(datetime) # 4/6 13:26:15\n >>> itr.get_next(datetime) # 4/6 13:26:25\n >>> itr.get_next(datetime) # 4/6 13:27:15\n\nYou can also note that this expression will repeat every second from the start datetime.::\n\n >>> croniter('* * * * * *', local_date).get_next(datetime)\n\nYou can also use seconds as first field::\n\n >>> itr = croniter('15,25 * * * * *', base, second_at_beginning=True)\n\n\nAbout year\n===========\nCroniter also support year field.\nYear presents at the seventh field, which is after second repetition.\nThe range of year field is from 1970 to 2099.\nTo ignore second repetition, simply set second to ``0`` or any other const::\n\n >>> base = datetime(2012, 4, 6, 2, 6, 59)\n >>> itr = croniter('0 0 1 1 * 0 2020/2', base)\n >>> itr.get_next(datetime) # 2020 1/1 0:0:0\n >>> itr.get_next(datetime) # 2022 1/1 0:0:0\n >>> itr.get_next(datetime) # 2024 1/1 0:0:0\n\nSupport for start_time shifts\n==============================\nSee https://github.com/kiorky/croniter/pull/76,\nYou can set start_time=, then expand_from_start_time=True for your generations to be computed from start_time instead of calendar days::\n\n >>> from pprint import pprint\n >>> iter = croniter('0 0 */7 * *', start_time=datetime(2024, 7, 11), expand_from_start_time=True);pprint([iter.get_next(datetime) for a in range(10)])\n [datetime.datetime(2024, 7, 18, 0, 0),\n datetime.datetime(2024, 7, 25, 0, 0),\n datetime.datetime(2024, 8, 4, 0, 0),\n datetime.datetime(2024, 8, 11, 0, 0),\n datetime.datetime(2024, 8, 18, 0, 0),\n datetime.datetime(2024, 8, 25, 0, 0),\n datetime.datetime(2024, 9, 4, 0, 0),\n datetime.datetime(2024, 9, 11, 0, 0),\n datetime.datetime(2024, 9, 18, 0, 0),\n datetime.datetime(2024, 9, 25, 0, 0)]\n >>> # INSTEAD OF THE DEFAULT BEHAVIOR:\n >>> iter = croniter('0 0 */7 * *', start_time=datetime(2024, 7, 11), expand_from_start_time=False);pprint([iter.get_next(datetime) for a in range(10)])\n [datetime.datetime(2024, 7, 15, 0, 0),\n datetime.datetime(2024, 7, 22, 0, 0),\n datetime.datetime(2024, 7, 29, 0, 0),\n datetime.datetime(2024, 8, 1, 0, 0),\n datetime.datetime(2024, 8, 8, 0, 0),\n datetime.datetime(2024, 8, 15, 0, 0),\n datetime.datetime(2024, 8, 22, 0, 0),\n datetime.datetime(2024, 8, 29, 0, 0),\n datetime.datetime(2024, 9, 1, 0, 0),\n datetime.datetime(2024, 9, 8, 0, 0)]\n\n\nTesting if a date matches a crontab\n===================================\nTest for a match with (>=0.3.32)::\n\n >>> croniter.match(\"0 0 * * *\", datetime(2019, 1, 14, 0, 0, 0, 0))\n True\n >>> croniter.match(\"0 0 * * *\", datetime(2019, 1, 14, 0, 2, 0, 0))\n False\n >>>\n >>> croniter.match(\"2 4 1 * wed\", datetime(2019, 1, 1, 4, 2, 0, 0)) # 04:02 on every Wednesday OR on 1st day of month\n True\n >>> croniter.match(\"2 4 1 * wed\", datetime(2019, 1, 1, 4, 2, 0, 0), day_or=False) # 04:02 on every 1st day of the month if it is a Wednesday\n False\n\nTesting if a crontab matches in datetime range\n==============================================\nTest for a match_range with (>=2.0.3)::\n\n >>> croniter.match_range(\"0 0 * * *\", datetime(2019, 1, 13, 0, 59, 0, 0), datetime(2019, 1, 14, 0, 1, 0, 0))\n True\n >>> croniter.match_range(\"0 0 * * *\", datetime(2019, 1, 13, 0, 1, 0, 0), datetime(2019, 1, 13, 0, 59, 0, 0))\n False\n >>> croniter.match_range(\"2 4 1 * wed\", datetime(2019, 1, 1, 3, 2, 0, 0), datetime(2019, 1, 1, 5, 1, 0, 0))\n # 04:02 on every Wednesday OR on 1st day of month\n True\n >>> croniter.match_range(\"2 4 1 * wed\", datetime(2019, 1, 1, 3, 2, 0, 0), datetime(2019, 1, 1, 5, 2, 0, 0), day_or=False)\n # 04:02 on every 1st day of the month if it is a Wednesday\n False\n\nGaps between date matches\n=========================\nFor performance reasons, croniter limits the amount of CPU cycles spent attempting to find the next match.\nStarting in v0.3.35, this behavior is configurable via the ``max_years_between_matches`` parameter, and the default window has been increased from 1 year to 50 years.\n\nThe defaults should be fine for many use cases.\nApplications that evaluate multiple cron expressions or handle cron expressions from untrusted sources or end-users should use this parameter.\nIterating over sparse cron expressions can result in increased CPU consumption or a raised ``CroniterBadDateError`` exception which indicates that croniter has given up attempting to find the next (or previous) match.\nExplicitly specifying ``max_years_between_matches`` provides a way to limit CPU utilization and simplifies the iterable interface by eliminating the need for ``CroniterBadDateError``.\nThe difference in the iterable interface is based on the reasoning that whenever ``max_years_between_matches`` is explicitly agreed upon, there is no need for croniter to signal that it has given up; simply stopping the iteration is preferable.\n\nThis example matches 4 AM Friday, January 1st.\nSince January 1st isn't often a Friday, there may be a few years between each occurrence.\nSetting the limit to 15 years ensures all matches::\n\n >>> it = croniter(\"0 4 1 1 fri\", datetime(2000,1,1), day_or=False, max_years_between_matches=15).all_next(datetime)\n >>> for i in range(5):\n ... print(next(it))\n ...\n 2010-01-01 04:00:00\n 2016-01-01 04:00:00\n 2021-01-01 04:00:00\n 2027-01-01 04:00:00\n 2038-01-01 04:00:00\n\nHowever, when only concerned with dates within the next 5 years, simply set ``max_years_between_matches=5`` in the above example.\nThis will result in no matches found, but no additional cycles will be wasted on unwanted matches far in the future.\n\nIterating over a range using cron\n=================================\nFind matches within a range using the ``croniter_range()`` function. This is much like the builtin ``range(start,stop,step)`` function, but for dates. The `step` argument is a cron expression.\nAdded in (>=0.3.34)\n\nList the first Saturday of every month in 2019::\n\n >>> from croniter import croniter_range\n >>> for dt in croniter_range(datetime(2019, 1, 1), datetime(2019, 12, 31), \"0 0 * * sat#1\"):\n >>> print(dt)\n\n\nHashed expressions\n==================\n\ncroniter supports Jenkins-style hashed expressions, using the \"H\" definition keyword and the required hash_id keyword argument.\nHashed expressions remain consistent, given the same hash_id, but different hash_ids will evaluate completely different to each other.\nThis allows, for example, for an even distribution of differently-named jobs without needing to manually spread them out.\n\n >>> itr = croniter(\"H H * * *\", hash_id=\"hello\")\n >>> itr.get_next(datetime)\n datetime.datetime(2021, 4, 10, 11, 10)\n >>> itr.get_next(datetime)\n datetime.datetime(2021, 4, 11, 11, 10)\n >>> itr = croniter(\"H H * * *\", hash_id=\"hello\")\n >>> itr.get_next(datetime)\n datetime.datetime(2021, 4, 10, 11, 10)\n >>> itr = croniter(\"H H * * *\", hash_id=\"bonjour\")\n >>> itr.get_next(datetime)\n datetime.datetime(2021, 4, 10, 20, 52)\n\n\nRandom expressions\n==================\n\nRandom \"R\" definition keywords are supported, and remain consistent only within their croniter() instance.\n\n >>> itr = croniter(\"R R * * *\")\n >>> itr.get_next(datetime)\n datetime.datetime(2021, 4, 10, 22, 56)\n >>> itr.get_next(datetime)\n datetime.datetime(2021, 4, 11, 22, 56)\n >>> itr = croniter(\"R R * * *\")\n >>> itr.get_next(datetime)\n datetime.datetime(2021, 4, 11, 4, 19)\n\n\nNote about Ranges\n=================\n\nNote that as a deviation from cron standard, croniter is somehow laxist with ranges and will allow ranges of ``Jan-Dec``, & ``Sun-Sat`` in reverse way and interpret them as following examples:\n\n - ``Apr-Jan``: from April to january\n - ``Sat-Sun``: Saturday, Sunday\n - ``Wed-Sun``: Wednesday to Saturday, Sunday\n\nPlease note that if a /step is given, it will be respected.\n\nNote about Sunday\n=================\n\nNote that as a deviation from cron standard, croniter like numerous cron implementations supports ``SUNDAY`` to be expressed as ``DAY7``, allowing such expressions:\n\n - ``0 0 * * 7``\n - ``0 0 * * 6-7``\n - ``0 0 * * 6,7``\n\n\nKeyword expressions\n===================\n\nVixie cron-style \"@\" keyword expressions are supported.\nWhat they evaluate to depends on whether you supply hash_id: no hash_id corresponds to Vixie cron definitions (exact times, minute resolution), while with hash_id corresponds to Jenkins definitions (hashed within the period, second resolution).\n\n ============ ============ ================\n Keyword No hash_id With hash_id\n ============ ============ ================\n @midnight 0 0 * * * H H(0-2) * * * H\n @hourly 0 * * * * H * * * * H\n @daily 0 0 * * * H H * * * H\n @weekly 0 0 * * 0 H H * * H H\n @monthly 0 0 1 * * H H H * * H\n @yearly 0 0 1 1 * H H H H * H\n @annually 0 0 1 1 * H H H H * H\n ============ ============ ================\n\nUpgrading\n==========\n\nTo 2.0.0\n---------\n\n- Install or upgrade pytz by using version specified requirements/base.txt if you have it installed `<=2021.1`.\n\nDevelop this package\n====================\n\n::\n\n git clone https://github.com/kiorky/croniter.git\n cd croniter\n virtualenv --no-site-packages venv3\n venv3/bin/pip install --upgrade -r requirements/test.txt -r requirements/lint.txt -r requirements/tox.txt\n venv3/bin/tox --current-env -e lint,test\n\n\nTesting under py2\n==================\n\nInstall prerequisisites ::\n\n # install py 2 with eg: apt install python2.7\n mkdir venv2 && curl -sSL \"https://github.com/pypa/get-virtualenv/blob/20.27.0/public/2.7/virtualenv.pyz?raw=true\" > venv2/venv && python2 venv2/venv venv2\n venv2/bin/python2 -m pip install -r ./requirements/test.txt\n\nRun tests::\n\n ./venv2/bin/pytest src\n\n\nMake a new release\n====================\nWe use zest.fullreleaser, a great release infrastructure.\n\nDo and follow these instructions\n::\n\n venv3/bin/pip install --upgrade -r requirements/release.txt\n ./release.sh\n\n\nContributors\n===============\nThanks to all who have contributed to this project!\nIf you have contributed and your name is not listed below please let us know.\n\n - Aarni Koskela (akx)\n - mrmachine\n - Hinnack\n - shazow\n - kiorky\n - jlsandell\n - mag009\n - djmitche\n - GreatCombinator\n - chris-baynes\n - ipartola\n - yuzawa-san\n - lowell80 (Kintyre)\n - scop\n - zed2015\n - Ryan Finnie (rfinnie)\n - salitaba\n\n\n\nChangelog\n==============\n\n5.0.1 (2024-10-29)\n------------------\n\n- Community wanted: Reintroduce 7 as DayOfWeek in deviation from standard cron (#90). [kiorky]\n\n4.0.0 (2024-10-28)\n------------------\n\n- Remove DayOfWeek alias 7 to DayOfWeek 0 to stick to standard cron (#90). [kiorky]\n- Fix DOW ranges calculations when lastday is a Sunday. [kiorky]\n\n3.0.4 (2024-10-25)\n------------------\n\n- Fix overflow on 32bits systems (#87) [kiorky]\n- Fix python2 testing (related to #93) [kiorky]\n- Modernize packaging. Special thanks to Aarni Koskela (akx) for all the inputs. [kiorky, akx]\n\n3.0.3 (2024-07-26)\n------------------\n\n- fix lint [kiorky]\n\n3.0.2 (2024-07-26)\n------------------\n\n- Fix start_time not respected in get_next/get_prev/all_next/all_prev (#86) [hesstobi, kiorky]\n\n3.0.1 (2024-07-25)\n------------------\n\n- Add an `update_current` argument to get_next/get_prev/all_next/all_prev to facilitate writing of some downstream code, see #83. [kiorky]\n\n3.0.0 (2024-07-23)\n------------------\n\n- Support for year field [zhouyizhen, kiorky]\n- Better support for 6 fields (second), and 7 fields crons [zhouyizhen, kiorky]\n- Better fix hashed expressions omitting some entries (#82, #42, #30) fix is retained over #42 initial fix [zhouyizhen, kiorky]\n- Ensure match return false when not time available (#81) [zhouyizhen, kiorky]\n\n\n2.0.7 (2024-07-16)\n------------------\n\n- fix doc\n\n\n2.0.6 (2024-07-16)\n------------------\n\n- Implement second_at_beginning [zhouyizhen, kiorky]\n- Support question mark as wildcard [zhouyizhen, kiorky]\n- Support to start a cron from a reference start time [mghextreme, kiorky]\n\n\n2.0.5 (2024-04-20)\n------------------\n\n- No changes, fix lint [kiorky]\n\n\n2.0.4 (2024-04-20)\n------------------\n\n- Support hashid strings in is_valid [george-kuanli-peng, kiorky]\n- Avoid over-optimization in crontab expansions [Cherie0125, liqirui <liqirui@baidu.com>, kiorky]\n\n\n2.0.3 (2024-03-19)\n------------------\n\n- Add match_range function [salitaba]\n\n\n2.0.2 (2024-02-29)\n------------------\n\n- fix leap year (29 days in February) [zed2015]\n\n\n2.0.1 (2023-10-11)\n------------------\n\n- Fix release issue [kiorky]\n\n\n2.0.0 (2023-10-10)\n------------------\n\n- Add Python 3.12 support [rafsaf]\n- Make major release instructions [kiorky]\n\n\n1.4.1 (2023-06-15)\n------------------\n\n- Make a retrocompatible version of 1.4.0 change about supporting VIXIECRON bug. (fix #47)\n [kiorky]\n\n\n1.4.0 (2023-06-15)\n------------------\n\n- Added \"implement_cron_bug\" flag to make the cron parser compatible with a bug in Vixie/ISC Cron\n [kiorky, David White <dwhite2@cisco.com>]\n *WARNING*: EXPAND METHOD CHANGES RETURN VALUE\n\n\n1.3.15 (2023-05-25)\n-------------------\n\n- Fix hashed expressions omitting some entries\n [@waltervos/Walter Vos <walter.vos@ns.nl>]\n- Enhance .match() precision for 6 position expressions\n [@szpol/szymon <szymon.polinkiewicz@gmail.com>]\n\n1.3.14 (2023-04-12)\n-------------------\n\n- Lint\n\n\n1.3.13 (2023-04-12)\n-------------------\n\n- Add check for range begin/end\n\n\n\n1.3.12 (2023-04-12)\n-------------------\n\n- restore py2 compat\n\n\n1.3.11 (2023-04-12)\n-------------------\n\n- Do not expose `i` into global namespace\n\n\n1.3.10 (2023-04-07)\n-------------------\n\n- Fix DOW hash parsing [kiorky]\n- better error handling on py3 [kiorky]\n\n1.3.8 (2022-11-22)\n------------------\n\n- Add Python 3.11 support and move docs files to main folder [rafsaf]\n\n\n1.3.7 (2022-09-06)\n------------------\n\n- fix tests\n- Fix croniter_range infinite loop [Shachar Snapiri <ssnapiri@paloaltonetworks.com>]\n\n\n1.3.5 (2022-05-14)\n------------------\n\n- Add Python 3.10 support [eelkevdbos]\n\n\n1.3.4 (2022-02-18)\n------------------\n\n- Really fix compat for tests under py27\n [kiorky]\n\n\n1.3.3 (2022-02-18)\n------------------\n\n- Fix compat for tests under py27\n [kiorky]\n\n\n1.3.2 (2022-02-18)\n------------------\n\n- Fix #12: regressions with set_current\n [kiorky, agateblue]\n\n\n1.3.1 (2022-02-15)\n------------------\n\n- Restore compat with python2\n [kiorky]\n\n\n1.3.0 (2022-02-15)\n------------------\n\n- Add a way to make next() easier to use. This fixes #11\n [kiorky]\n\n\n1.2.0 (2022-01-14)\n------------------\n\n- Enforce validation for day=1. Before this release we used to support day=0 and it was silently glided to day=1 to support having both day in day in 4th field when it came to have 6fields cron forms (second repeat). It will now raises a CroniterBadDateError. See https://github.com/kiorky/croniter/issues/6\n [kiorky]\n\n1.1.0 (2021-12-03)\n------------------\n\n- Enforce validation for month=1. Before this release we used to support month=0 and it was silently glided to month=1 to support having both day in month in 4th field when it came to have 6fields cron forms (second repeat). It will now raises a CroniterBadDateError. See https://github.com/kiorky/croniter/issues/6\n [kiorky]\n\n1.0.15 (2021-06-25)\n-------------------\n\n- restore py2 [kiorky]\n\n\n1.0.14 (2021-06-25)\n-------------------\n\n- better type checks [kiorky]\n\n\n1.0.13 (2021-05-06)\n-------------------\n\n- Fix ZeroDivisionError with ``* * R/0 * *``\n [cuu508]\n\n1.0.12 (2021-04-13)\n-------------------\n\n- Add support for hashed/random/keyword expressions\n Ryan Finnie (rfinnie)\n- Review support support for hashed/random/keyword expression and add expanders reactor\n [ kiorky ]\n\n\n1.0.11 (2021-04-07)\n-------------------\n\n- fix bug: bad case:``0 6 30 3 *``\n [zed2015(zhangchi)]\n- Add support for ``L`` in the day_of_week component. This enable expressions like ``* * * * L4``, which means last Thursday of the month. This resolves #159.\n [Kintyre]\n- Create ``CroniterUnsupportedSyntaxError`` exception for situations where CRON syntax may be valid but some combinations of features is not supported.\n Currently, this is used when the ``day_of_week`` component has a combination of literal values and nth/last syntax at the same time.\n For example, ``0 0 * * 1,L6`` or ``0 0 * * 15,sat#1`` will both raise this exception because of mixing literal days of the week with nth-weekday or last-weekday syntax.\n This *may* impact existing cron expressions in prior releases, because ``0 0 * * 15,sat#1`` was previously allowed but incorrectly handled.\n [Kintyre]\n\n- Update ``croniter_range()`` to allow an alternate ``croniter`` class to be used. Helpful when using a custom class derived from croniter.\n [Kintyre]\n\n\n1.0.10 (2021-03-25)\n-------------------\n\n- Remove external library ``natsort``.\n Sorting of cron expression components now handled with ``sorted()`` with a custom ``key`` function.\n [Kintyre]\n\n\n\n1.0.9 (2021-03-23)\n------------------\n\n- Remove futures dependency\n [kiorky]\n\n\n1.0.8 (2021-03-06)\n------------------\n\n- Update `_expand` to lowercase each component of the expression.\n This is in relation to #157. With this change, croniter accepts and correctly handles `* * 10-L * *`.\n [cuu508]\n\n\n1.0.7 (2021-03-02)\n------------------\n\n- Fix _expand to reject int literals with underscores\n [cuu508]\n- Remove a debug statement to make flake8 happy\n [cuu508]\n\n1.0.6 (2021-02-01)\n------------------\n\n- Fix combination of star and invalid expression bugs\n [kiorky]\n\n\n1.0.5 (2021-01-29)\n------------------\n\n- Security fix: fix overflow when using cron ranges\n [kiorky]\n\n1.0.4 (2021-01-29)\n------------------\n\n- Spelling fix release\n\n\n1.0.3 (2021-01-29)\n------------------\n\n- Fix #155: raise CroniterBadCronError when error syntax\n [kiorky]\n\n\n1.0.2 (2021-01-19)\n------------------\n\n- Fix match when datetime has microseconds\n [kiorky]\n\n1.0.1 (2021-01-06)\n------------------\n- no changes, just to make sense with new semver2 (making croniter on a stable state)\n [kiorky]\n\n\n0.3.37 (2020-12-31)\n-------------------\n\n- Added Python 3.8 and 3.9 support\n [eumiro]\n\n\n0.3.36 (2020-11-02)\n-------------------\n\n- Updated docs section regarding ``max_years_between_matches`` to be more shorter and hopefully more relevant.\n [Kintyre]\n- Don't install tests\n [scop]\n\n\n0.3.35 (2020-10-11)\n-------------------\n\n- Handle L in ranges. This fixes #142.\n [kiorky]\n- Add a new initialization parameter ``max_years_between_matches`` to support finding the next/previous date beyond the default 1 year window, if so desired. Updated README to include additional notes and example of this usage. Fixes #145.\n [Kintyre]\n- The ``croniter_range()`` function was updated to automatically determines the appropriate ``max_years_between_matches`` value, this preventing handling of the ``CroniterBadDateError`` exception.\n [Kintyre]\n- Updated exception handling classes: ``CroniterBadDateError`` now only\n applies during date finding operations (next/prev), and all parsing errors can now be caught using ``CroniterBadCronError``. The ``CroniterNotAlphaError`` exception is now a subclass of ``CroniterBadCronError``. A brief description of each exception class was added as an inline docstring.\n [Kintyre]\n- Updated iterable interfaces to replace the ``CroniterBadDateError`` with ``StopIteration`` if (and only if) the ``max_years_between_matches`` argument is provided. The rationale here is that if the user has specified the max tolerance between matches, then there's no need to further inform them of no additional matches. Just stop the iteration. This also keeps backwards compatibility.\n [Kintyre]\n- Minor docs update\n [Kintyre]\n\n\n0.3.34 (2020-06-19)\n-------------------\n\n- Feat ``croniter_range(start, stop, cron)``\n [Kintyre]\n- Optimization for poorly written cron expression\n [Kintyre]\n\n0.3.33 (2020-06-15)\n-------------------\n\n- Make dateutil tz support more official\n [Kintyre]\n- Feat/support for day or\n [\u7530\u53e3\u4fe1\u5143]\n\n0.3.32 (2020-05-27)\n-------------------\n\n- document seconds repeats, fixes #122\n [kiorky]\n- Implement match method, fixes #54\n [kiorky]\n- Adding tests for #127 (test more DSTs and croniter behavior around)\n [kiorky]\n- Changed lag_hours comparison to absolute to manage dst boundary when getting previous\n [Sokkka]\n\n0.3.31 (2020-01-02)\n-------------------\n\n- Fix get_next() when start_time less then 1s before next instant\n [AlexHill]\n\n\n0.3.30 (2019-04-20)\n-------------------\n\n- credits\n\n\n0.3.29 (2019-03-26)\n-------------------\n\n- credits\n- history stripping (security)\n- Handle -Sun notation, This fixes `#119 <https://github.com/taichino/croniter/issues/119>`_.\n [kiorky]\n- Handle invalid ranges correctly, This fixes `#114 <https://github.com/taichino/croniter/issues/114>`_.\n [kiorky]\n\n0.3.25 (2018-08-07)\n-------------------\n- Pypi hygiene\n [hugovk]\n\n\n0.3.24 (2018-06-20)\n-------------------\n- fix `#107 <https://github.com/taichino/croniter/issues/107>`_: microsecond threshold\n [kiorky]\n\n\n0.3.23 (2018-05-23)\n-------------------\n\n- fix ``get_next`` while preserving the fix of ``get_prev`` in 7661c2aaa\n [Avikam Agur <avikam@pagaya-inv.com>]\n\n\n0.3.22 (2018-05-16)\n-------------------\n- Don't count previous minute if now is dynamic\n If the code is triggered from 5-asterisk based cron\n ``get_prev`` based on ``datetime.now()`` is expected to return\n current cron iteration and not previous execution.\n [Igor Khrol <igor.khrol@toptal.com>]\n\n0.3.20 (2017-11-06)\n-------------------\n\n- More DST fixes\n [Kevin Rose <kbrose@github>]\n\n\n0.3.19 (2017-08-31)\n-------------------\n\n- fix #87: backward dst changes\n [kiorky]\n\n\n0.3.18 (2017-08-31)\n-------------------\n\n- Add is valid method, refactor errors\n [otherpirate, Mauro Murari <mauro_murari@hotmail.com>]\n\n\n0.3.17 (2017-05-22)\n-------------------\n- DOW occurrence sharp style support.\n [kiorky, Kengo Seki <sekikn@apache.org>]\n\n\n0.3.16 (2017-03-15)\n-------------------\n\n- Better test suite [mrcrilly@github]\n- DST support [kiorky]\n\n0.3.15 (2017-02-16)\n-------------------\n\n- fix bug around multiple conditions and range_val in\n _get_prev_nearest_diff.\n [abeja-yuki@github]\n\n0.3.14 (2017-01-25)\n-------------------\n\n- issue #69: added day_or option to change behavior when day-of-month and\n day-of-week is given\n [Andreas Vogl <a.vogl@hackner-security.com>]\n\n\n\n0.3.13 (2016-11-01)\n-------------------\n\n- `Real fix for #34 <https://github.com/taichino/croniter/pull/73>`_\n [kiorky@github]\n- `Modernize test infra <https://github.com/taichino/croniter/pull/72>`_\n [kiorky@github]\n- `Release as a universal wheel <https://github.com/kiorky/croniter/pull/16>`_\n [adamchainz@github]\n- `Raise ValueError on negative numbers <https://github.com/taichino/croniter/pull/63>`_\n [josegonzalez@github]\n- `Compare types using \"issubclass\" instead of exact match <https://github.com/taichino/croniter/pull/70>`_\n [darkk@github]\n- `Implement step cron with a variable base <https://github.com/taichino/croniter/pull/60>`_\n [josegonzalez@github]\n\n0.3.12 (2016-03-10)\n-------------------\n- support setting ret_type in __init__ [Brent Tubbs <brent.tubbs@gmail.com>]\n\n0.3.11 (2016-01-13)\n-------------------\n\n- Bug fix: The get_prev API crashed when last day of month token was used. Some\n essential logic was missing.\n [Iddo Aviram <iddo.aviram@similarweb.com>]\n\n\n0.3.10 (2015-11-29)\n-------------------\n\n- The functionality of 'l' as day of month was broken, since the month variable\n was not properly updated\n [Iddo Aviram <iddo.aviram@similarweb.com>]\n\n0.3.9 (2015-11-19)\n------------------\n\n- Don't use datetime functions python 2.6 doesn't support\n [petervtzand]\n\n0.3.8 (2015-06-23)\n------------------\n- Truncate microseconds by setting to 0\n [Corey Wright]\n\n\n0.3.7 (2015-06-01)\n------------------\n\n- converting sun in range sun-thu transforms to int 0 which is\n recognized as empty string; the solution was to convert sun to string \"0\"\n\n0.3.6 (2015-05-29)\n------------------\n\n- Fix default behavior when no start_time given\n Default value for ``start_time`` parameter is calculated at module init time rather than call time.\n- Fix timezone support and stop depending on the system time zone\n\n\n\n0.3.5 (2014-08-01)\n------------------\n\n- support for 'l' (last day of month)\n\n\n0.3.4 (2014-01-30)\n------------------\n\n- Python 3 compat\n- QA Release\n\n\n0.3.3 (2012-09-29)\n------------------\n- proper packaging\n\n",
"bugtrack_url": null,
"license": "MIT License",
"summary": "croniter provides iteration for datetime object with cron like format",
"version": "5.0.1",
"project_urls": {
"Homepage": "http://github.com/kiorky/croniter"
},
"split_keywords": [
"datetime",
" iterator",
" cron"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "3c6834c3d74d2af6ea98ff8a0b50d149cff26e88a3f09817121d1186e9185e97",
"md5": "7713313a9888e3547418db3fc2419253",
"sha256": "eb28439742291f6c10b181df1a5ecf421208b1fc62ef44501daec1780a0b09e9"
},
"downloads": -1,
"filename": "croniter-5.0.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "7713313a9888e3547418db3fc2419253",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.6",
"size": 24149,
"upload_time": "2024-10-29T16:30:29",
"upload_time_iso_8601": "2024-10-29T16:30:29.769758Z",
"url": "https://files.pythonhosted.org/packages/3c/68/34c3d74d2af6ea98ff8a0b50d149cff26e88a3f09817121d1186e9185e97/croniter-5.0.1-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "a78c0656200bfa5c1e90b26f4bb1cc8aecb4a7722f8386ee044bdc2d4efb589e",
"md5": "912d4da9688e00a4c12852eee6c63393",
"sha256": "7d9b1ef25b10eece48fdf29d8ac52f9b6252abff983ac614ade4f3276294019e"
},
"downloads": -1,
"filename": "croniter-5.0.1.tar.gz",
"has_sig": false,
"md5_digest": "912d4da9688e00a4c12852eee6c63393",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.6",
"size": 57084,
"upload_time": "2024-10-29T16:30:31",
"upload_time_iso_8601": "2024-10-29T16:30:31.556616Z",
"url": "https://files.pythonhosted.org/packages/a7/8c/0656200bfa5c1e90b26f4bb1cc8aecb4a7722f8386ee044bdc2d4efb589e/croniter-5.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-29 16:30:31",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "kiorky",
"github_project": "croniter",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "croniter"
}