Fix
get_next/get_prevraisingCroniterBadDateErrorwhen day-of-month and day-of-week are both restricted and the day-of-month can never occur in the selected month (e.g.0 0 31 2 0). Under the Vixie OR semantics the unsatisfiable side now contributes no dates instead of aborting the whole expression, andmatch()andcroniter_range(), which swallow the error, no longer return silently wrong results for these expressions. [b245ab6, #243, @semx, @MildlyMeticulous]Fix hashed divisions (
H/{divisor},H({begin}-{end})/{divisor}) when the divisor is as wide as, or wider than, the range it is drawn from. Three symptoms, one cause: the offset was drawn from the first period without regard for where the range ends, and the result was emitted as{offset}-{end}/{divisor}even when the step could not reach a second value.H/60lost minute 59 and fired at minute 0 twice as often as any other minute (59 distinct schedules over 20,000 hash ids, not 60);H(50-59)/10fired six times an hour instead of once for the ids hashing to 59;H(50-52)/10fired outside its own range for 7 hash buckets in 10, andH/100raisedCroniterBadCronErrorfor roughly a third of hash ids. The offset is now clamped to the declared range and emitted as a single value when the step cannot reach a second one. Divisors that fit their range keep their existing hashes, so no schedule that was already correct moves. [#253, @potiuk]Fix
expand_from_start_timetaking the phase of a cycle from the UTC form of a timezone-awarestart_timerather than from the wall clock the caller wrote._get_low_from_current_date_numberread the start time back withtz=UTC, so0 */5 * * *from2025-01-01 05:00+13:00phased on UTC hour 16 and expanded to hours1,6,11,16,21instead of0,5,10,15,20. The hour, day-of-month, month and day-of-week fields were all affected, and at a date boundary the day and month could be a whole day or month out. The start time's owntzinfois now carried into the expansion. A naivestart_timeis converted to a timestamp as if it were UTC, so UTC round-trips it to the same wall clock and naive callers see no change at all — verified over 45,312 expansions with naive start times, none of which differ. [#256, @potiuk]Fix
expand_from_start_timeraisingValueError: Can't get current date number for index larger than 4for any range or step in the optional seconds or years field.croniter("* * * * * */15", start, expand_from_start_time=True)aborted, and because the exception came from outside theCroniterErrorhierarchy, code catchingCroniterErrornever saw it. Both fields now re-base like the five that already worked: seconds take their phase from the start time's second, and years — which do not start at zero — take theirs from the field minimum, as day-of-month and month already do. [#254, @potiuk]Fix
{start}/{step}expanding to the whole field whenstartis the field maximum.59/15 * * * *fired at:00/:15/:30/:45instead of:59, and the same held in every field at its own maximum:23/6hours,31/5day-of-month,DEC/3months,SAT/2day-of-week, and — not in the original report —59/15seconds and2099/5years.{start}/{step}normalizes to{start}-{max}/{step}; whenstartis the maximum the two bounds collide, making the token indistinguishable from an explicitly written equal range such asJan-Jan, which croniter deliberately expands to the whole cycle. The two forms are now distinguished. An explicitly written equal range is unchanged, and so is every start below the field maximum. The fix applies inexpand_from_start_timemode as well as by default. [#246, @earfman]Warning
This changes existing schedules, silently and substantially. Any
{max}/{step}expression that was firing across the whole field now fires once per cycle. The{max}/1forms are the sharpest case, because they read as "every":* * * * 6/1meant every day and now means Saturdays only;59/1 * * * *meant every minute and now means minute 59. The same applies to23/1hours,31/1day-of-month,12/1months, and to the optional seconds and years fields (59/1,2099/1). Use a wildcard (*or*/1) for "every". Before upgrading, audit for any field whose value begins with that field's maximum followed by a slash.One consequence is worth stating separately: a
{max}/{step}expression can now be unsatisfiable rather than merely different, because it no longer covers the whole field.0 0 1 1 * * 2099/1previously expanded the year field to every year and now means 2099 alone, so iterating from a later start time raisesCroniterBadDateErrorinstead of returning a date. The same happens without a year field wherever the remaining value cannot occur:0 0 31/5 2 *is now day 31 of February.
- Document what
expand_from_start_timeactually means — that it moves the phase of a cycle and not the values a field may take, that every field re-bases including the optional seconds and years, and that the phase is read in the start time's own timezone. Also document that a range with two equal bounds (Jan-Jan,Sun-Sun) means the whole cycle rather than the value it names, which was undocumented and is the rule behind several of the bugs fixed in this release. [#257, @potiuk]
- Bump pinned GitHub Actions via dependabot. [6e69553, #247; 3c6ce9b, #251]
- Bump pinned build dependencies (
hatchling) via dependabot. [70564f2, #245]
- Fix
expand_from_start_timeday-of-week low bound so Sunday wraps correctly for stepped day-of-week ranges (isoweekday() % 7instead ofweekday() + 1). [44e3090, #239, @alhudz]
- Bump pinned GitHub Actions via dependabot. [fd13d2e, #240; 5cd2e46, #238]
- Fix quadratic expansion of comma-separated range lists for a large speed-up on expressions with many ranges. [1d79fc6, #231, @alhudz]
- Reject a zero step (e.g.
5-5/0) in equal and reversed cron ranges instead of silently accepting it. [ab27767, #232, @alhudz] - Fix
expand_from_start_timemonth low-bound off-by-one so stepped month ranges start on the correct month. [f329964, #235, @alhudz]
- Fix zizmor-reported security findings in GitHub Actions workflows. [ed19f94, #230]
- Bump pinned build and CI dependencies via dependabot.
- Upgrade locked development and build dependencies (
uv lock --upgrade).
- Switch build backend from
flit_coretohatchlingwith pinned dependencies. [4cce160] - Add PEP 639 license expression (
license = "MIT") to project metadata. [4cce160] - Pin all GitHub Actions to SHA hashes across CI workflows. [4cce160]
- Add dependabot for automated dependency updates. [4cce160]
- Add zizmor workflow for CI security scanning. [4cce160]
- Fix
get_prevskipping Feb 29 on leap years for day-of-month expressions. [441a0b5, #203, @Souls-R]
- Add
License :: OSI Approved :: MIT Licenseclassifier. [270cbbc, @cdheiser]
- Add support for
W(nearest weekday) in the day-of-month field. [c79d88a] - Add
precision_in_secondsparameter tomatch()andmatch_range()to allow overriding the default matching precision. [7415d1e, #58] - Add
strictparameter tois_valid()andexpand()for cross-field validation of impossible day/month combinations. [0352d48, #199]
- Fix memory leak by removing
EXPRESSIONSglobal dict cache. [6ebfe56, @Souls-R]
- Document
day_orandimplement_cron_bugparameters for day-of-month/day-of-week interaction. [289bf73, #75] - Document
precision_in_secondsparameter and default precision behavior formatch(). [7415d1e, #58]
- Support for zoneinfo timezones. [b4e7295, Benjamin Drung (@bdrung)]
- Add type hints to croniter.__init__, timestamp_to_datetime, and property initializations. [b171ea9, 257741e, c4f5e44, Benjamin Drung (@bdrung)]
- Simplify code for max_years_between_matches. [e065efa, Benjamin Drung (@bdrung)]
- Extend type check for hash_id to empty str/bytes. [6fe1d43, Benjamin Drung (@bdrung)]
- Drop unused _get_next_nearest and _get_prev_nearest. [d7dab3a, Benjamin Drung (@bdrung)]
- Reduce line length to 99 and unfold long Python code lines. [ecea402, f38d5f4, Benjamin Drung (@bdrung)]
- Fix memory leak by removing TIMESTAMP_TO_DT_CACHE global dict cache. [1a9d3c0, Rafał Safin (@rafsaf)]
- Fix memory leak by removing EXPRESSIONS global dict cache. [1883e15, @Souls-R]
- Fix default value of second_at_beginning to a boolean. [dc63ed2, Benjamin Drung (@bdrung)]
- Fix always missing the timestamp to datetime cache. [18eb299, Benjamin Drung (@bdrung)]
- Fix skipping first of March. [de21a9d, Benjamin Drung (@bdrung)]
- Fix DST handling by rewriting the DST logic. [f48387b, Benjamin Drung (@bdrung)]
- Fix all flake8 complaints. [08c1f83, Benjamin Drung (@bdrung)]
- Test: fix date in DST test. [871e391, Benjamin Drung (@bdrung)]
- Test: document time jumps in DST test cases. [2d72258, Benjamin Drung (@bdrung)]
- Test: use isoformat() to compare dates with timezone information. [1dd3562, Benjamin Drung (@bdrung)]
- Announce back that croniter is maintained now as part of pallets-eco. [Jarek Potiuk (@jarekpotiuk)]
- Announce for now that croniter dev is ended (CRA).
- Rework timestamp_to_datetime to use whatever timezone [kiorky]
- Make datetime_to_timestamp & timestamp_to_datetime public [kiorky]
- Fix EPOCH calculation in case of non UTC & 32 bits based systems [kiorky]
- Apply isort formatter [kiorky]
- Reintegrate test_speed [kiorky]
- Apply black formatter [evanpurkhiser, kiorky]
- Code quality changes [evanpurkhiser, kiorky]
- Remove unused _get_caller_globals_and_locals [evanpurkhiser]
- Remove single-use bad_length [evanpurkhiser]
- Remove unused days in proc_month [evanpurkhiser]
- Use field_index over i for readability [evanpurkhiser]
- Always use """ for docstrings [evanpurkhiser]
- Make helper instance methods that do not use self static [evanpurkhiser]
- Remove unusd call to sys.exc_info [evanpurkhiser]
- Remove unused ALPHAS [evanpurkhiser]
- Improve croniter.expand documentation [evanpurkhiser]
- Community wanted: Reintroduce 7 as DayOfWeek in deviation from standard cron (#90). [kiorky]
- Remove DayOfWeek alias 7 to DayOfWeek 0 to stick to standard cron (#90). [kiorky]
- Fix DOW ranges calculations when lastday is a Sunday. [kiorky]
- 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]
- fix lint [kiorky]
- Fix start_time not respected in get_next/get_prev/all_next/all_prev (#86) [hesstobi, kiorky]
- Add an update_current argument to get_next/get_prev/all_next/all_prev to facilitate writing of some downstream code, see #83. [kiorky]
- 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]
- fix doc
- 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]
- No changes, fix lint [kiorky]
- Support hashid strings in is_valid [george-kuanli-peng, kiorky]
- Avoid over-optimization in crontab expansions [Cherie0125, liqirui <liqirui@baidu.com>, kiorky]
- Add match_range function [salitaba]
- fix leap year (29 days in February) [zed2015]
- Fix release issue [kiorky]
- Add Python 3.12 support [rafsaf]
- Make major release instructions [kiorky]
- Make a retrocompatible version of 1.4.0 change about supporting VIXIECRON bug. (fix #47) [kiorky]
- 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
- 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>]
- Lint
- Add check for range begin/end
- restore py2 compat
- Do not expose i into global namespace
- Fix DOW hash parsing [kiorky]
- better error handling on py3 [kiorky]
- Add Python 3.11 support and move docs files to main folder [rafsaf]
- fix tests
- Fix croniter_range infinite loop [Shachar Snapiri <ssnapiri@paloaltonetworks.com>]
- Add Python 3.10 support [eelkevdbos]
- Really fix compat for tests under py27 [kiorky]
- Fix compat for tests under py27 [kiorky]
- Fix #12: regressions with set_current [kiorky, agateblue]
- Restore compat with python2 [kiorky]
- Add a way to make next() easier to use. This fixes #11 [kiorky]
- 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 #6 [kiorky]
- 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 #6 [kiorky]
- restore py2 [kiorky]
- better type checks [kiorky]
- Fix ZeroDivisionError with
* * R/0 * *[cuu508]
- Add support for hashed/random/keyword expressions Ryan Finnie (rfinnie)
- Review support support for hashed/random/keyword expression and add expanders reactor [ kiorky ]
- fix bug: bad case:
0 6 30 3 *[zed2015(zhangchi)] - Add support for
Lin the day_of_week component. This enable expressions like* * * * L4, which means last Thursday of the month. This resolves #159. [Kintyre] - Create
CroniterUnsupportedSyntaxErrorexception for situations where CRON syntax may be valid but some combinations of features is not supported. Currently, this is used when theday_of_weekcomponent has a combination of literal values and nth/last syntax at the same time. For example,0 0 * * 1,L6or0 0 * * 15,sat#1will 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, because0 0 * * 15,sat#1was previously allowed but incorrectly handled. [Kintyre] - Update
croniter_range()to allow an alternatecroniterclass to be used. Helpful when using a custom class derived from croniter. [Kintyre]
- Remove external library
natsort. Sorting of cron expression components now handled withsorted()with a customkeyfunction. [Kintyre]
- Remove futures dependency [kiorky]
- 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]
- Fix _expand to reject int literals with underscores [cuu508]
- Remove a debug statement to make flake8 happy [cuu508]
- Fix combination of star and invalid expression bugs [kiorky]
- Security fix: fix overflow when using cron ranges [kiorky]
- Spelling fix release
- Fix #155: raise CroniterBadCronError when error syntax [kiorky]
- Fix match when datetime has microseconds [kiorky]
- no changes, just to make sense with new semver2 (making croniter on a stable state) [kiorky]
- Added Python 3.8 and 3.9 support [eumiro]
- Updated docs section regarding
max_years_between_matchesto be more shorter and hopefully more relevant. [Kintyre] - Don't install tests [scop]
- Handle L in ranges. This fixes #142. [kiorky]
- Add a new initialization parameter
max_years_between_matchesto 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 appropriatemax_years_between_matchesvalue, this preventing handling of theCroniterBadDateErrorexception. [Kintyre] - Updated exception handling classes:
CroniterBadDateErrornow only applies during date finding operations (next/prev), and all parsing errors can now be caught usingCroniterBadCronError. TheCroniterNotAlphaErrorexception is now a subclass ofCroniterBadCronError. A brief description of each exception class was added as an inline docstring. [Kintyre] - Updated iterable interfaces to replace the
CroniterBadDateErrorwithStopIterationif (and only if) themax_years_between_matchesargument 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]
- Feat
croniter_range(start, stop, cron)[Kintyre] - Optimization for poorly written cron expression [Kintyre]
- Make dateutil tz support more official [Kintyre]
- Feat/support for day or [田口信元]
- 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]
- Fix get_next() when start_time less then 1s before next instant [AlexHill]
- credits
- credits
- history stripping (security)
- Handle -Sun notation, This fixes #119. [kiorky]
- Handle invalid ranges correctly, This fixes #114. [kiorky]
- Pypi hygiene [hugovk]
- fix #107: microsecond threshold [kiorky]
- fix
get_nextwhile preserving the fix ofget_previn 7661c2aaa [Avikam Agur <avikam@pagaya-inv.com>]
- Don't count previous minute if now is dynamic
If the code is triggered from 5-asterisk based cron
get_prevbased ondatetime.now()is expected to return current cron iteration and not previous execution. [Igor Khrol <igor.khrol@toptal.com>]
- More DST fixes [Kevin Rose <kbrose@github>]
- fix #87: backward dst changes [kiorky]
- Add is valid method, refactor errors [otherpirate, Mauro Murari <mauro_murari@hotmail.com>]
- DOW occurrence sharp style support. [kiorky, Kengo Seki <sekikn@apache.org>]
- Better test suite [mrcrilly@github]
- DST support [kiorky]
- fix bug around multiple conditions and range_val in _get_prev_nearest_diff. [abeja-yuki@github]
- 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>]
- Real fix for #34 [kiorky@github]
- Modernize test infra [kiorky@github]
- Release as a universal wheel [adamchainz@github]
- Raise ValueError on negative numbers [josegonzalez@github]
- Compare types using "issubclass" instead of exact match [darkk@github]
- Implement step cron with a variable base [josegonzalez@github]
- support setting ret_type in __init__ [Brent Tubbs <brent.tubbs@gmail.com>]
- 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>]
- The functionality of 'l' as day of month was broken, since the month variable was not properly updated [Iddo Aviram <iddo.aviram@similarweb.com>]
- Don't use datetime functions python 2.6 doesn't support [petervtzand]
- Truncate microseconds by setting to 0 [Corey Wright]
- 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"
- Fix default behavior when no start_time given
Default value for
start_timeparameter is calculated at module init time rather than call time. - Fix timezone support and stop depending on the system time zone
- support for 'l' (last day of month)
- Python 3 compat
- QA Release
- proper packaging