Skip to main content

Introduction

croniter provides iteration for the datetime object with a cron like format.

                      _ _
  ___ _ __ ___  _ __ (_) |_ ___ _ __
 / __| '__/ _ \| '_ \| | __/ _ \ '__|
| (__| | | (_) | | | | | ||  __/ |
 \___|_|  \___/|_| |_|_|\__\___|_|

Website: https://github.com/pallets-eco/croniter

Build Badge

https://github.com/pallets-eco/croniter/actions/workflows/cicd.yml/badge.svg

Pallets Community Ecosystem

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

Strict validation

By default, is_valid and expand only check that each field is within its own range (e.g. day 1-31, month 1-12). They do not cross-validate fields, so expressions like 0 0 31 2 * (February 31st) are considered valid even though they can never match a real date.

Use strict=True to enable cross-field validation that rejects impossible day/month combinations:

>>> croniter.is_valid('0 0 31 2 *')  # True (default: syntax only)
>>> croniter.is_valid('0 0 31 2 *', strict=True)  # False (Feb has at most 29 days)
>>> croniter.is_valid('0 0 31 4 *', strict=True)  # False (Apr has 30 days)
>>> croniter.is_valid('0 0 30 1,2 *', strict=True)  # True (day 30 is valid in Jan)

When strict=True and the expression does not include a year field, February is assumed to have 29 days (since leap years exist):

>>> croniter.is_valid('0 0 29 2 *', strict=True)  # True (leap years exist)

If the expression includes a year field (7-field format), leap year status is determined from the specified years:

>>> croniter.is_valid('0 0 29 2 * 0 2024', strict=True)  # True (2024 is a leap year)
>>> croniter.is_valid('0 0 29 2 * 0 2023', strict=True)  # False (2023 is not)
>>> croniter.is_valid('0 0 29 2 * 0 2023-2025', strict=True)  # True (2024 in range is a leap year)

For 5- or 6-field expressions, you can pass strict_year to provide the year(s) for leap year checking without adding a year field to the expression:

>>> croniter.is_valid('0 0 29 2 *', strict=True, strict_year=2024)  # True (leap year)
>>> croniter.is_valid('0 0 29 2 *', strict=True, strict_year=2023)  # False (not a leap year)
>>> croniter.is_valid('0 0 29 2 *', strict=True, strict_year=[2023, 2024])  # True (2024 is a leap year)

The strict and strict_year parameters are also available on expand():

>>> croniter.expand('0 0 31 2 *', strict=True)  # raises CroniterBadCronError

Last day of month (l)

You can use l in the day-of-month field to mean “the last day of the month”. This automatically respects the number of days in each month, including February and leap years:

>>> croniter('0 0 l * *', datetime(2024, 1, 30)).get_next(datetime)
datetime.datetime(2024, 1, 31, 0, 0)
>>> croniter('0 0 l * *', datetime(2024, 2, 1)).get_next(datetime)  # leap year
datetime.datetime(2024, 2, 29, 0, 0)
>>> croniter('0 0 l * *', datetime(2023, 2, 1)).get_next(datetime)  # non-leap year
datetime.datetime(2023, 2, 28, 0, 0)

Nearest weekday (W)

The W character is supported in the day-of-month field to specify the nearest weekday (Monday-Friday) to the given day. Both nW and Wn formats are accepted:

>>> base = datetime(2024, 6, 1)
>>> itr = croniter('0 9 15W * *', base)
>>> itr.get_next(datetime)   # Jun 15 2024 is Saturday -> fires on Friday 14th
datetime.datetime(2024, 6, 14, 9, 0)

Rules:

  • If the specified day falls on a weekday, the trigger fires on that day.

  • If the specified day falls on Saturday, the trigger fires on the preceding Friday.

  • If the specified day falls on Sunday, the trigger fires on the following Monday.

  • The nearest weekday never crosses month boundaries. If the 1st is a Saturday, the trigger fires on Monday the 3rd. If the last day of the month is a Sunday, the trigger fires on the preceding Friday.

  • W can only be used with a single day value, not in a range or list.

Examples:

>>> croniter('0 9 1W * *', datetime(2024, 5, 31)).get_next(datetime)   # Jun 1 is Sat -> Mon 3rd
datetime.datetime(2024, 6, 3, 9, 0)
>>> croniter('0 9 W15 * *', datetime(2024, 1, 1)).get_next(datetime)   # Wn format also works
datetime.datetime(2024, 1, 15, 9, 0)

Day-of-month and day-of-week

When both the day-of-month and day-of-week fields are restricted (not *), the default POSIX cron behavior is to match when either field matches (OR). This is controlled by the day_or parameter:

>>> # OR (default): fires on every Wednesday OR on the 1st of the month
>>> iter = croniter('2 4 1 * wed', datetime(2010, 1, 25))
>>> print(iter.get_next(datetime))   # 2010-01-27 04:02:00 (Wed)
>>> print(iter.get_next(datetime))   # 2010-02-01 04:02:00 (1st)

>>> # AND: fires only on the 1st of the month IF it is a Wednesday
>>> iter = croniter('2 4 1 * wed', datetime(2010, 1, 25), day_or=False)
>>> print(iter.get_next(datetime))   # 2010-09-01 04:02:00 (Wed AND 1st)

This can be used to express patterns like “the first Tuesday of the month” by combining a day range with a weekday:

>>> # First Tuesday of each month: days 1-7 AND Tuesday
>>> iter = croniter('1 1 1-7 * 2', datetime(2024, 7, 12), day_or=False)
>>> print(iter.get_next(datetime))   # 2024-08-06 01:01:00
>>> print(iter.get_next(datetime))   # 2024-09-03 01:01:00
>>> print(iter.get_next(datetime))   # 2024-10-01 01:01:00
>>> print(iter.get_next(datetime))   # 2024-11-05 01:01:00

Vixie cron bug compatibility

Some vixie/ISC cron implementations have a known bug where expressions that start with * in the day-of-month or day-of-week field (e.g. */32,1-7) use AND logic instead of OR, even though those fields are technically restricted.

If you need to replicate this behavior (e.g. */32,1-7 as a hack for days 1-7), use implement_cron_bug=True:

>>> iter = croniter('1 1 */32,1-7 * 2', datetime(2024, 7, 12), implement_cron_bug=True)
>>> print(iter.get_next(datetime))   # 2024-08-06 01:01:00
>>> print(iter.get_next(datetime))   # 2024-09-03 01:01:00

About DST

Be sure to init your croniter instance with a TZ aware datetime for this to work!

Example using zoneinfo:

>>> import zoneinfo
>>> tz = zoneinfo.ZoneInfo("Europe/Berlin")
>>> local_date = datetime(2017, 3, 26, tzinfo=tz)
>>> val = croniter('0 0 * * *', local_date).get_next(datetime)

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/pallets-eco/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

By default, match uses a precision of 60 seconds for standard 5-field cron expressions and 1 second for 6-field expressions (with seconds). This means a datetime up to 60 (or 1) seconds after the scheduled time will still match.

You can override this with the precision_in_seconds parameter:

>>> # With default precision (60s), 59 seconds past midnight still matches
>>> croniter.match("0 0 * * *", datetime(2019, 1, 14, 0, 0, 59, 0))
True
>>> # With precision=1, only an exact match within 1 second works
>>> croniter.match("0 0 * * *", datetime(2019, 1, 14, 0, 0, 59, 0), precision_in_seconds=1)
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

The precision_in_seconds parameter is also available on match_range.

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 if you have it installed <=2021.1.

Develop this package

Clone the repository

git clone https://github.com/pallets-eco/croniter.git
cd croniter

Use uv to install the dependencies

uv sync --group dev --group lint --group format --group tox

Now you can run the formatters (fmt), linters (lint) and tests (test) using tox through uv.

uv run tox -e fmt,lint,test

Build the package

uv build

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)

  • ashb

  • bdrung

  • chris-baynes

  • djmitche

  • evanpurkhiser

  • GreatCombinator

  • Hinnack

  • ipartola

  • jlsandell

  • kiorky

  • lowell80 (Kintyre)

  • mag009

  • mrmachine

  • potiuk

  • Ryan Finnie (rfinnie)

  • salitaba

  • scop

  • shazow

  • yuzawa-san

  • zed2015

Release files for croniter 6.2.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for croniter 6.2.4
File Size Uploaded
croniter-6.2.4.tar.gz 166.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for croniter 6.2.4
File Interpreter ABI Platform
croniter-6.2.4-py3-none-any.whl Python 3 none any Details

Total release size: 212.9 kB

Release files / croniter-6.2.4.tar.gz

Download URL croniter-6.2.4.tar.gz
Size 166.3 kB
Tags Source
SHA-256 checksum
How to use checksums
fc124f751b1b04805c2a04b061898b436b45ab2320b045e1e052ea895de65189
BLAKE2b-256 checksum
How to use checksums
37572e2a65aee2a70483cb28e2b7e15a072d00a523207593b44400d4717bb100
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 10, 2026.

Transparency log

Release files / croniter-6.2.4-py3-none-any.whl

Download URL croniter-6.2.4-py3-none-any.whl
Size 46.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8ef3d544107a5c05a150a2d78f8bf5a8eb9c5c4d93405a736b824109574e3f4d
BLAKE2b-256 checksum
How to use checksums
cdbad678e5bd329646ca51d3c92addbc77804e86d21f4b6b6a027218e6abb010
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

6.2.4 This release

2 release files

6.2.3

2 release files

6.2.2

2 release files

6.2.1

2 release files

6.2.0

2 release files

6.1.0

2 release files

6.0.0

2 release files

5.0.1

2 release files

4.0.0

2 release files

3.0.4

2 release files

3.0.3

2 release files

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.0.7

2 release files

2.0.6

2 release files

2.0.5

2 release files

2.0.4

2 release files

2.0.3

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.15

2 release files

1.3.14

2 release files

1.3.13

2 release files

1.3.12

2 release files

1.3.11

2 release files

1.3.8

2 release files

1.3.7

2 release files

1.3.6

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.15

2 release files

1.0.14

2 release files

1.0.12

2 release files

1.0.10

2 release files

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

0.3.37

2 release files

0.3.35

2 release files

0.3.34

2 release files

0.3.33

2 release files

0.3.32

2 release files

0.3.30

2 release files

0.3.29

2 release files

0.3.28

2 release files

0.3.24

1 release file

0.3.23

1 release file

0.3.22

1 release file

0.3.20

1 release file

0.3.19

1 release file

0.3.18

1 release file

0.3.17

1 release file

0.3.16

1 release file

0.3.15

1 release file

0.3.14

1 release file

0.3.12

1 release file

0.3.11

1 release file

0.3.10

1 release file

0.3.9

1 release file

0.3.8

1 release file

0.3.7

1 release file

0.3.6

1 release file

0.3.5

1 release file

0.3.4

1 release file

0.3.3

1 release file

0.3.2

1 release file

0.3.1

1 release file

0.3.0

1 release file

0.2.7

2 release files

0.2.6

1 release file

0.2.5

1 release file

0.2.4

1 release file

0.2.2

1 release file

0.2.1

0.2.0

1 release file

0.1.6

1 release file

0.1.5

2 release files

0.1.4

3 release files

0.1.3

1 release file

0.1.2

1 release file

0.1.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page