Fast and tiny JavaScript/TypeScript cron parser with timezone support
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

substitute set in parser with array for performance gains

+412 -176
+3
.github/FUNDING.yml
··· 1 + # These are supported funding model platforms 2 + 3 + github: kbilkis
+83
CODE_OF_CONDUCT.md
··· 1 + # Contributor Covenant Code of Conduct 2 + 3 + ## Our Pledge 4 + 5 + We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation. 6 + 7 + We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 + 9 + ## Our Standards 10 + 11 + Examples of behavior that contributes to a positive environment for our community include: 12 + 13 + - Demonstrating empathy and kindness toward other people 14 + - Being respectful of differing opinions, viewpoints, and experiences 15 + - Giving and gracefully accepting constructive feedback 16 + - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 + - Focusing on what is best not just for us as individuals, but for the overall community 18 + 19 + Examples of unacceptable behavior include: 20 + 21 + - The use of sexualized language or imagery, and sexual attention or advances of any kind 22 + - Trolling, insulting or derogatory comments, and personal or political attacks 23 + - Public or private harassment 24 + - Publishing others' private information, such as a physical or email address, without their explicit permission 25 + - Other conduct which could reasonably be considered inappropriate in a professional setting 26 + 27 + ## Enforcement Responsibilities 28 + 29 + Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 30 + 31 + Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 32 + 33 + ## Scope 34 + 35 + This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 36 + 37 + ## Enforcement 38 + 39 + Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at kasparas@bilkis.lt. All complaints will be reviewed and investigated promptly and fairly. 40 + 41 + All community leaders are obligated to respect the privacy and security of the reporter of any incident. 42 + 43 + ## Enforcement Guidelines 44 + 45 + Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 46 + 47 + ### 1. Correction 48 + 49 + **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 50 + 51 + **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 52 + 53 + ### 2. Warning 54 + 55 + **Community Impact**: A violation through a single incident or series of actions. 56 + 57 + **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 58 + 59 + ### 3. Temporary Ban 60 + 61 + **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 62 + 63 + **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 64 + 65 + ### 4. Permanent Ban 66 + 67 + **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 68 + 69 + **Consequence**: A permanent ban from any sort of public interaction within the community. 70 + 71 + ## Attribution 72 + 73 + This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 74 + 75 + Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 76 + 77 + For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations]. 78 + 79 + [homepage]: https://www.contributor-covenant.org 80 + [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 81 + [Mozilla CoC]: https://github.com/mozilla/diversity 82 + [FAQ]: https://www.contributor-covenant.org/faq 83 + [translations]: https://www.contributor-covenant.org/translations
+143
CONTRIBUTING.md
··· 1 + # Contributing to cron-fast 2 + 3 + Thank you for your interest in contributing to cron-fast! This document provides guidelines and instructions for contributing. 4 + 5 + ## Table of Contents 6 + 7 + - [Code of Conduct](#code-of-conduct) 8 + - [Development Setup](#development-setup) 9 + - [How to Contribute](#how-to-contribute) 10 + - [Development Workflow](#development-workflow) 11 + - [Pull Request Guidelines](#pull-request-guidelines) 12 + - [Coding Standards](#coding-standards) 13 + 14 + ## Code of Conduct 15 + 16 + This project and everyone participating in it is governed by our [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. 17 + 18 + ## Development Setup 19 + 20 + ### Prerequisites 21 + 22 + - Node.js >= 20.0.0 23 + - pnpm (recommended) or npm 24 + 25 + ### Installation 26 + 27 + 1. Fork the repository 28 + 2. Clone your fork: 29 + ```bash 30 + git clone https://github.com/YOUR_USERNAME/cron-fast.git 31 + cd cron-fast 32 + ``` 33 + 3. Install dependencies: 34 + ```bash 35 + pnpm install 36 + ``` 37 + 38 + ## How to Contribute 39 + 40 + ### Reporting Bugs 41 + 42 + - Check if the bug has already been reported in [Issues](https://github.com/kbilkis/cron-fast/issues) 43 + - If not, create a new issue with: 44 + - Clear, descriptive title 45 + - Steps to reproduce 46 + - Expected vs actual behavior 47 + - Node.js/runtime version 48 + - Code sample if applicable 49 + 50 + ### Suggesting Features 51 + 52 + - Open an issue with the `enhancement` label 53 + - Describe the feature and its use case 54 + - Explain why it would benefit the project 55 + 56 + ### Submitting Changes 57 + 58 + 1. Create a feature branch from `main`: 59 + ```bash 60 + git checkout -b feature/my-feature 61 + ``` 62 + 2. Make your changes 63 + 3. Write/update tests 64 + 4. Ensure all tests pass 65 + 5. Submit a pull request 66 + 67 + ## Development Workflow 68 + 69 + ### Available Scripts 70 + 71 + ```bash 72 + # Run tests 73 + pnpm test 74 + 75 + # Run tests in watch mode 76 + pnpm test:watch 77 + 78 + # Run tests with coverage 79 + pnpm test:coverage 80 + 81 + # Lint code 82 + pnpm lint 83 + 84 + # Format code 85 + pnpm fmt 86 + 87 + # Type check 88 + pnpm typecheck 89 + 90 + # Build 91 + pnpm build 92 + 93 + # Run benchmarks 94 + pnpm benchmark 95 + ``` 96 + 97 + ### Testing 98 + 99 + - Write tests for all new features and bug fixes 100 + - Ensure all existing tests pass 101 + - Aim for high test coverage 102 + - Test across runtimes if making significant changes: 103 + - Node.js: `pnpm test` 104 + - Deno: `pnpm benchmark:deno` 105 + - Bun: `pnpm benchmark:bun` 106 + 107 + ## Pull Request Guidelines 108 + 109 + - Keep PRs focused and small 110 + - Write clear commit messages 111 + - Update documentation if needed 112 + - Add tests for new functionality 113 + - Ensure CI passes 114 + - Reference related issues 115 + 116 + ### PR Checklist 117 + 118 + - [ ] Tests pass locally 119 + - [ ] Linting passes 120 + - [ ] Type checking passes 121 + - [ ] Documentation updated (if needed) 122 + - [ ] Commit messages are clear 123 + 124 + ## Coding Standards 125 + 126 + - **TypeScript**: Strict mode enabled 127 + - **Style**: Formatted with `oxfmt` 128 + - **Linting**: Uses `oxlint` 129 + - **Commits**: Clear, descriptive messages 130 + 131 + ### Code Style 132 + 133 + - Use TypeScript for all source files 134 + - Prefer functional programming patterns 135 + - Keep functions small and focused 136 + - Add JSDoc comments for public APIs 137 + - Follow existing code patterns 138 + 139 + ## Questions? 140 + 141 + Feel free to open an issue for questions or discussions about contributing. 142 + 143 + Thank you for contributing! 🎉
+28 -19
README.md
··· 7 7 [![CI](https://github.com/kbilkis/cron-fast/actions/workflows/ci.yml/badge.svg)](https://github.com/kbilkis/cron-fast/actions/workflows/ci.yml) 8 8 [![codecov](https://codecov.io/github/kbilkis/cron-fast/graph/badge.svg)](https://codecov.io/github/kbilkis/cron-fast) 9 9 [![npm bundle size](https://img.shields.io/bundlejs/size/cron-fast?logo=esbuild)](https://bundlejs.com/?q=cron-fast) 10 + [![Snyk](https://snyk.io/test/npm/cron-fast/badge.svg)](https://snyk.io/test/npm/cron-fast) 10 11 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 11 12 12 13 **Fast and tiny JavaScript/TypeScript cron parser with timezone support.** Works everywhere: Node.js, Deno, Bun, Cloudflare Workers, and browsers. Zero dependencies. ··· 173 174 174 175 ## Bundle Size 175 176 176 - cron-fast is extremely lightweight and fully tree-shakeable. Here are the actual bundle sizes for different import scenarios (tested with v2.0.1): 177 + cron-fast is extremely lightweight and fully tree-shakeable. Here are the actual bundle sizes for different import scenarios (tested with v2.1.0): 177 178 178 179 | Import | Raw | Minified | Gzipped | 179 180 | ------------------------------------------------------ | -------- | -------- | ----------- | 180 - | `Full bundle (all exports) ` | 21.86 KB | 10.12 KB | **3.61 KB** | 181 - | `nextRun only ` | 13.12 KB | 6.03 KB | **2.21 KB** | 182 - | `previousRun only ` | 13.13 KB | 6.03 KB | **2.22 KB** | 183 - | `nextRuns only ` | 13.50 KB | 6.18 KB | **2.28 KB** | 184 - | `isValid only ` | 4.44 KB | 2.23 KB | **980 B** | 185 - | `parse only ` | 4.33 KB | 2.19 KB | **956 B** | 186 - | `describe only ` | 11.55 KB | 5.58 KB | **2.11 KB** | 187 - | `isMatch only ` | 6.35 KB | 3.15 KB | **1.33 KB** | 188 - | `Validation only (isValid + parse) ` | 4.45 KB | 2.24 KB | **981 B** | 189 - | `Scheduling only (nextRun + previousRun + nextRuns) ` | 13.91 KB | 6.36 KB | **2.30 KB** | 181 + | `Full bundle (all exports) ` | 21.84 KB | 10.12 KB | **3.61 KB** | 182 + | `nextRun only ` | 13.10 KB | 6.03 KB | **2.22 KB** | 183 + | `previousRun only ` | 13.11 KB | 6.03 KB | **2.22 KB** | 184 + | `nextRuns only ` | 13.49 KB | 6.18 KB | **2.28 KB** | 185 + | `isValid only ` | 4.42 KB | 2.23 KB | **986 B** | 186 + | `parse only ` | 4.31 KB | 2.18 KB | **963 B** | 187 + | `describe only ` | 11.53 KB | 5.58 KB | **2.11 KB** | 188 + | `isMatch only ` | 6.33 KB | 3.15 KB | **1.33 KB** | 189 + | `Validation only (isValid + parse) ` | 4.43 KB | 2.23 KB | **988 B** | 190 + | `Scheduling only (nextRun + previousRun + nextRuns) ` | 13.89 KB | 6.36 KB | **2.30 KB** | 190 191 191 192 Import only what you need: 192 193 193 194 ```typescript 194 - // Small bundle - only validation (~900 B gzipped) 195 + // Small bundle - only validation 195 196 import { isValid } from "cron-fast"; 196 197 197 - // Medium bundle - one function + dependencies (~2 KB gzipped) 198 + // Medium bundle - one function + dependencies 198 199 import { nextRun } from "cron-fast"; 199 200 200 - // Full bundle - everything (~2.3 KB gzipped) 201 + // Full bundle - everything 201 202 import * as cron from "cron-fast"; 202 203 ``` 203 204 ··· 283 284 284 285 cron-fast is designed for speed and efficiency. Here's how it compares to popular alternatives: 285 286 286 - > Tested with cron-fast v2.0.1, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0 on Node.js v22.18.0 287 + > Tested with cron-fast v2.1.0, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0 on Node.js v22.18.0 287 288 288 289 | Operation | cron-fast | croner | cron-parser | cron-schedule | 289 290 | ------------ | -------------- | --------- | ----------- | ------------- | 290 - | Next run | **367k ops/s** | 30k ops/s | 33k ops/s | 375k ops/s | 291 - | Previous run | **409k ops/s** | 31k ops/s | 37k ops/s | 386k ops/s | 292 - | Validation | **555k ops/s** | 32k ops/s | 94k ops/s | 436k ops/s | 293 - | Parsing | **543k ops/s** | 32k ops/s | 92k ops/s | 446k ops/s | 291 + | Next run | **459k ops/s** | 31k ops/s | 32k ops/s | 374k ops/s | 292 + | Previous run | **512k ops/s** | 31k ops/s | 37k ops/s | 388k ops/s | 293 + | Validation | **733k ops/s** | 34k ops/s | 92k ops/s | 453k ops/s | 294 + | Parsing | **735k ops/s** | 34k ops/s | 92k ops/s | 458k ops/s | 294 295 295 296 See [detailed benchmarks and feature comparison](docs/benchmark-comparison.md) (including Deno and Bun runtimes) for more information. 296 297 297 298 Run benchmarks yourself: `pnpm benchmark` 299 + 300 + ## Contributing 301 + 302 + Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests. 303 + 304 + ## Contributors 305 + 306 + - [Kasparas Bilkis](https://github.com/kbilkis) - Creator and maintainer 298 307 299 308 ## License 300 309
+47 -49
docs/benchmark-comparison-bun.md
··· 1 1 # Benchmark & Feature Comparison 2 2 3 - > Tested with bun v1.3.9, cron-fast v2.0.1, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0, cron-validate v1.5.3 3 + > Tested with bun v1.3.9, cron-fast v2.1.0, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0, cron-validate v1.5.3 4 4 > Tested on MacBook M1 pro 5 5 6 6 ## Performance Benchmarks ··· 11 11 12 12 | Library | Avg ops/sec | vs cron-fast | 13 13 | ------------- | ----------- | ------------ | 14 - | **cron-fast** | ~275k | baseline | 15 - | cron-schedule | ~296k | 1.1x slower | 16 - | croner | ~47k | 5.8x faster | 17 - | cron-parser | ~34k | 8.0x faster | 14 + | **cron-fast** | ~380k | baseline | 15 + | cron-schedule | ~313k | 1.2x faster | 16 + | croner | ~55k | 6.9x faster | 17 + | cron-parser | ~38k | 10.1x faster | 18 18 19 19 ### Previous Execution Time 20 20 21 21 | Library | Avg ops/sec | vs cron-fast | 22 22 | ------------- | ----------- | ------------ | 23 - | **cron-fast** | ~313k | baseline | 24 - | cron-schedule | ~323k | 1.0x slower | 25 - | croner | ~56k | 5.5x faster | 26 - | cron-parser | ~40k | 7.8x faster | 23 + | **cron-fast** | ~417k | baseline | 24 + | cron-schedule | ~326k | 1.3x faster | 25 + | croner | ~56k | 7.4x faster | 26 + | cron-parser | ~39k | 10.6x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 32 - | **cron-fast** | ~360k | baseline | 33 - | cron-validate | ~952k | 2.6x slower | 34 - | cron-schedule | ~357k | 1.0x faster | 35 - | cron-parser | ~120k | 3.0x faster | 36 - | croner | ~60k | 6.0x faster | 32 + | **cron-fast** | ~463k | baseline | 33 + | cron-validate | ~946k | 2.0x slower | 34 + | cron-schedule | ~334k | 1.4x faster | 35 + | cron-parser | ~116k | 4.0x faster | 36 + | croner | ~55k | 8.3x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 42 - | **cron-fast** | ~361k | baseline | 43 - | cron-validate | ~925k | 2.6x slower | 44 - | cron-schedule | ~341k | 1.1x faster | 45 - | cron-parser | ~118k | 3.1x faster | 46 - | croner | ~58k | 6.3x faster | 47 - 48 - **Note**: cron-validate is validation-only (no scheduling), which explains its speed advantage in parsing/validation. It only checks syntax without calculating dates or handling timezones, making it significantly faster for validation-only use cases. 42 + | **cron-fast** | ~494k | baseline | 43 + | cron-validate | ~944k | 1.9x slower | 44 + | cron-schedule | ~350k | 1.4x faster | 45 + | cron-parser | ~120k | 4.1x faster | 46 + | croner | ~61k | 8.1x faster | 49 47 50 48 Run benchmarks yourself: `pnpm benchmark:bun` 51 49 ··· 55 53 56 54 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 57 55 | --------------------------- | --------: | ------------: | -----: | ----------: | 58 - | Every minute | ~193k | ~142k ✓ | ~52k ✓ | ~35k ✓ | 59 - | Sparse: First of month | ~351k | ~391k ✗ | ~52k ✓ | ~21k ✓ | 60 - | Sparse: 31st (skips months) | ~340k | ~385k ✗ | ~31k ✓ | ~7k ✓ | 61 - | Step: Every 15 minutes | ~196k | ~189k | ~51k ✓ | ~56k ✓ | 62 - | Specific: 9 AM daily | ~261k | ~263k | ~52k ✓ | ~42k ✓ | 63 - | OR-mode: 15th OR Monday | ~343k | ~445k ✗ | ~46k ✓ | ~37k ✓ | 64 - | Weekdays: Mon-Fri 9 AM | ~238k | ~256k | ~44k ✓ | ~42k ✓ | 56 + | Every minute | ~426k | ~139k ✓ | ~53k ✓ | ~35k ✓ | 57 + | Sparse: First of month | ~382k | ~377k | ~51k ✓ | ~21k ✓ | 58 + | Sparse: 31st (skips months) | ~404k | ~409k | ~55k ✓ | ~9k ✓ | 59 + | Step: Every 15 minutes | ~390k | ~210k ✓ | ~59k ✓ | ~64k ✓ | 60 + | Specific: 9 AM daily | ~358k | ~299k ✓ | ~60k ✓ | ~46k ✓ | 61 + | OR-mode: 15th OR Monday | ~375k | ~484k ✗ | ~54k ✓ | ~42k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~324k | ~271k ✓ | ~52k ✓ | ~47k ✓ | 65 63 66 64 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 67 65 ··· 69 67 70 68 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 71 69 | --------------------------- | --------: | ------------: | -----: | ----------: | 72 - | Every minute | ~160k | ~155k | ~54k ✓ | ~37k ✓ | 73 - | Sparse: First of month | ~393k | ~423k | ~57k ✓ | ~11k ✓ | 74 - | Sparse: 31st (skips months) | ~380k | ~409k | ~57k ✓ | ~11k ✓ | 75 - | Step: Every 15 minutes | ~186k | ~217k ✗ | ~58k ✓ | ~61k ✓ | 76 - | Specific: 9 AM daily | ~287k | ~290k | ~59k ✓ | ~49k ✓ | 77 - | OR-mode: 15th OR Monday | ~500k | ~489k | ~58k ✓ | ~62k ✓ | 78 - | Weekdays: Mon-Fri 9 AM | ~283k | ~278k | ~53k ✓ | ~51k ✓ | 70 + | Every minute | ~405k | ~159k ✓ | ~58k ✓ | ~38k ✓ | 71 + | Sparse: First of month | ~461k | ~441k | ~58k ✓ | ~11k ✓ | 72 + | Sparse: 31st (skips months) | ~416k | ~424k | ~57k ✓ | ~10k ✓ | 73 + | Step: Every 15 minutes | ~395k | ~209k ✓ | ~59k ✓ | ~60k ✓ | 74 + | Specific: 9 AM daily | ~428k | ~274k ✓ | ~57k ✓ | ~47k ✓ | 75 + | OR-mode: 15th OR Monday | ~491k | ~496k | ~56k ✓ | ~60k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~321k | ~279k ✓ | ~48k ✓ | ~49k ✓ | 79 77 80 78 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 81 79 ··· 83 81 84 82 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 85 83 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 86 - | \* \* \* \* \* | ~148k | ~165k ✗ | ~52k ✓ | ~61k ✓ | ~943k ✗ | 87 - | 0 0 1 \* \* | ~512k | ~470k | ~159k ✓ | ~61k ✓ | ~981k ✗ | 88 - | 0 12 31 \* \* | ~467k | ~461k | ~155k ✓ | ~56k ✓ | ~931k ✗ | 89 - | _/15 _ \* \* \* | ~207k | ~228k | ~85k ✓ | ~60k ✓ | ~967k ✗ | 90 - | 0 9 \* \* \* | ~315k | ~305k | ~109k ✓ | ~62k ✓ | ~950k ✗ | 91 - | 0 9 15 \* 1 | ~610k | ~571k | ~177k ✓ | ~61k ✓ | ~936k ✗ | 92 - | 0 9 \* \* 1-5 | ~259k | ~297k ✗ | ~106k ✓ | ~57k ✓ | ~955k ✗ | 84 + | \* \* \* \* \* | ~380k | ~163k ✓ | ~53k ✓ | ~51k ✓ | ~977k ✗ | 85 + | 0 0 1 \* \* | ~494k | ~433k ✓ | ~136k ✓ | ~55k ✓ | ~962k ✗ | 86 + | 0 12 31 \* \* | ~566k | ~434k ✓ | ~161k ✓ | ~59k ✓ | ~926k ✗ | 87 + | _/15 _ \* \* \* | ~353k | ~210k ✓ | ~82k ✓ | ~56k ✓ | ~982k ✗ | 88 + | 0 9 \* \* \* | ~482k | ~309k ✓ | ~104k ✓ | ~59k ✓ | ~961k ✗ | 89 + | 0 9 15 \* 1 | ~619k | ~523k ✓ | ~167k ✓ | ~55k ✓ | ~932k ✗ | 90 + | 0 9 \* \* 1-5 | ~345k | ~266k ✓ | ~106k ✓ | ~52k ✓ | ~879k ✗ | 93 91 94 92 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 95 93 ··· 97 95 98 96 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 99 97 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 100 - | \* \* \* \* \* | ~195k | ~164k ✓ | ~52k ✓ | ~63k ✓ | ~945k ✗ | 101 - | 0 0 1 \* \* | ~490k | ~461k | ~160k ✓ | ~62k ✓ | ~966k ✗ | 102 - | 0 12 31 \* \* | ~474k | ~429k ✓ | ~156k ✓ | ~60k ✓ | ~901k ✗ | 103 - | _/15 _ \* \* \* | ~221k | ~207k | ~81k ✓ | ~54k ✓ | ~945k ✗ | 104 - | 0 9 \* \* \* | ~304k | ~287k | ~104k ✓ | ~56k ✓ | ~896k ✗ | 105 - | 0 9 15 \* 1 | ~570k | ~543k | ~166k ✓ | ~54k ✓ | ~901k ✗ | 106 - | 0 9 \* \* 1-5 | ~275k | ~295k | ~107k ✓ | ~54k ✓ | ~920k ✗ | 98 + | \* \* \* \* \* | ~420k | ~158k ✓ | ~52k ✓ | ~61k ✓ | ~917k ✗ | 99 + | 0 0 1 \* \* | ~592k | ~464k ✓ | ~148k ✓ | ~62k ✓ | ~986k ✗ | 100 + | 0 12 31 \* \* | ~552k | ~475k ✓ | ~160k ✓ | ~63k ✓ | ~958k ✗ | 101 + | _/15 _ \* \* \* | ~395k | ~226k ✓ | ~86k ✓ | ~64k ✓ | ~991k ✗ | 102 + | 0 9 \* \* \* | ~478k | ~301k ✓ | ~112k ✓ | ~61k ✓ | ~915k ✗ | 103 + | 0 9 15 \* 1 | ~659k | ~531k ✓ | ~178k ✓ | ~61k ✓ | ~900k ✗ | 104 + | 0 9 \* \* 1-5 | ~364k | ~295k ✓ | ~106k ✓ | ~58k ✓ | ~944k ✗ | 107 105 108 106 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower)
+47 -49
docs/benchmark-comparison-deno.md
··· 1 1 # Benchmark & Feature Comparison 2 2 3 - > Tested with deno v2.6.8, cron-fast v2.0.1, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0, cron-validate v1.5.3 3 + > Tested with deno v2.6.8, cron-fast v2.1.0, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0, cron-validate v1.5.3 4 4 > Tested on MacBook M1 pro 5 5 6 6 ## Performance Benchmarks ··· 11 11 12 12 | Library | Avg ops/sec | vs cron-fast | 13 13 | ------------- | ----------- | ------------ | 14 - | **cron-fast** | ~362k | baseline | 15 - | cron-schedule | ~399k | 1.1x slower | 16 - | croner | ~31k | 11.7x faster | 17 - | cron-parser | ~33k | 10.8x faster | 14 + | **cron-fast** | ~384k | baseline | 15 + | cron-schedule | ~341k | 1.1x faster | 16 + | croner | ~29k | 13.1x faster | 17 + | cron-parser | ~23k | 17.0x faster | 18 18 19 19 ### Previous Execution Time 20 20 21 21 | Library | Avg ops/sec | vs cron-fast | 22 22 | ------------- | ----------- | ------------ | 23 - | **cron-fast** | ~412k | baseline | 24 - | cron-schedule | ~419k | 1.0x slower | 25 - | croner | ~31k | 13.3x faster | 26 - | cron-parser | ~39k | 10.6x faster | 23 + | **cron-fast** | ~423k | baseline | 24 + | cron-schedule | ~350k | 1.2x faster | 25 + | croner | ~30k | 14.0x faster | 26 + | cron-parser | ~26k | 16.5x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 32 - | **cron-fast** | ~550k | baseline | 33 - | cron-validate | ~635k | 1.2x slower | 34 - | cron-schedule | ~478k | 1.2x faster | 35 - | cron-parser | ~98k | 5.6x faster | 36 - | croner | ~34k | 16.4x faster | 32 + | **cron-fast** | ~612k | baseline | 33 + | cron-validate | ~506k | 1.2x faster | 34 + | cron-schedule | ~395k | 1.5x faster | 35 + | cron-parser | ~63k | 9.8x faster | 36 + | croner | ~33k | 18.6x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 42 - | **cron-fast** | ~549k | baseline | 43 - | cron-validate | ~628k | 1.1x slower | 44 - | cron-schedule | ~472k | 1.2x faster | 45 - | cron-parser | ~97k | 5.7x faster | 46 - | croner | ~34k | 16.4x faster | 47 - 48 - **Note**: cron-validate is validation-only (no scheduling), which explains its speed advantage in parsing/validation. It only checks syntax without calculating dates or handling timezones, making it significantly faster for validation-only use cases. 42 + | **cron-fast** | ~611k | baseline | 43 + | cron-validate | ~506k | 1.2x faster | 44 + | cron-schedule | ~396k | 1.5x faster | 45 + | cron-parser | ~62k | 9.8x faster | 46 + | croner | ~33k | 18.6x faster | 49 47 50 48 Run benchmarks yourself: `pnpm benchmark:deno` 51 49 ··· 55 53 56 54 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 57 55 | --------------------------- | --------: | ------------: | -----: | ----------: | 58 - | Every minute | ~217k | ~145k ✓ | ~32k ✓ | ~31k ✓ | 59 - | Sparse: First of month | ~459k | ~522k ✗ | ~31k ✓ | ~18k ✓ | 60 - | Sparse: 31st (skips months) | ~434k | ~539k ✗ | ~30k ✓ | ~7k ✓ | 61 - | Step: Every 15 minutes | ~289k | ~277k | ~33k ✓ | ~56k ✓ | 62 - | Specific: 9 AM daily | ~366k | ~361k | ~33k ✓ | ~42k ✓ | 63 - | OR-mode: 15th OR Monday | ~403k | ~590k ✗ | ~29k ✓ | ~37k ✓ | 64 - | Weekdays: Mon-Fri 9 AM | ~368k | ~355k | ~28k ✓ | ~44k ✓ | 56 + | Every minute | ~333k | ~126k ✓ | ~29k ✓ | ~20k ✓ | 57 + | Sparse: First of month | ~434k | ~445k | ~29k ✓ | ~13k ✓ | 58 + | Sparse: 31st (skips months) | ~420k | ~448k | ~28k ✓ | ~5k ✓ | 59 + | Step: Every 15 minutes | ~352k | ~250k ✓ | ~30k ✓ | ~35k ✓ | 60 + | Specific: 9 AM daily | ~421k | ~325k ✓ | ~31k ✓ | ~29k ✓ | 61 + | OR-mode: 15th OR Monday | ~333k | ~487k ✗ | ~29k ✓ | ~26k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~394k | ~306k ✓ | ~28k ✓ | ~30k ✓ | 65 63 66 64 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 67 65 ··· 69 67 70 68 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 71 69 | --------------------------- | --------: | ------------: | -----: | ----------: | 72 - | Every minute | ~221k | ~187k ✓ | ~31k ✓ | ~35k ✓ | 73 - | Sparse: First of month | ~513k | ~562k | ~31k ✓ | ~9k ✓ | 74 - | Sparse: 31st (skips months) | ~438k | ~510k ✗ | ~31k ✓ | ~8k ✓ | 75 - | Step: Every 15 minutes | ~288k | ~271k | ~32k ✓ | ~57k ✓ | 76 - | Specific: 9 AM daily | ~379k | ~381k | ~31k ✓ | ~50k ✓ | 77 - | OR-mode: 15th OR Monday | ~664k | ~656k | ~31k ✓ | ~63k ✓ | 78 - | Weekdays: Mon-Fri 9 AM | ~382k | ~366k | ~30k ✓ | ~49k ✓ | 70 + | Every minute | ~343k | ~179k ✓ | ~30k ✓ | ~23k ✓ | 71 + | Sparse: First of month | ~475k | ~452k | ~30k ✓ | ~6k ✓ | 72 + | Sparse: 31st (skips months) | ~410k | ~408k | ~30k ✓ | ~6k ✓ | 73 + | Step: Every 15 minutes | ~346k | ~243k ✓ | ~31k ✓ | ~34k ✓ | 74 + | Specific: 9 AM daily | ~413k | ~332k ✓ | ~31k ✓ | ~33k ✓ | 75 + | OR-mode: 15th OR Monday | ~571k | ~517k ✓ | ~31k ✓ | ~44k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~404k | ~321k ✓ | ~29k ✓ | ~34k ✓ | 79 77 80 78 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 81 79 ··· 83 81 84 82 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 85 83 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 86 - | \* \* \* \* \* | ~237k | ~197k ✓ | ~45k ✓ | ~34k ✓ | ~600k ✗ | 87 - | 0 0 1 \* \* | ~717k | ~633k ✓ | ~126k ✓ | ~34k ✓ | ~642k ✓ | 88 - | 0 12 31 \* \* | ~671k | ~628k | ~126k ✓ | ~34k ✓ | ~641k | 89 - | _/15 _ \* \* \* | ~336k | ~290k ✓ | ~68k ✓ | ~34k ✓ | ~678k ✗ | 90 - | 0 9 \* \* \* | ~474k | ~407k ✓ | ~88k ✓ | ~34k ✓ | ~616k ✗ | 91 - | 0 9 15 \* 1 | ~956k | ~780k ✓ | ~140k ✓ | ~32k ✓ | ~654k ✓ | 92 - | 0 9 \* \* 1-5 | ~460k | ~410k ✓ | ~90k ✓ | ~32k ✓ | ~616k ✗ | 84 + | \* \* \* \* \* | ~371k | ~189k ✓ | ~28k ✓ | ~33k ✓ | ~484k ✗ | 85 + | 0 0 1 \* \* | ~738k | ~498k ✓ | ~82k ✓ | ~33k ✓ | ~513k ✓ | 86 + | 0 12 31 \* \* | ~688k | ~510k ✓ | ~82k ✓ | ~33k ✓ | ~515k ✓ | 87 + | _/15 _ \* \* \* | ~474k | ~266k ✓ | ~42k ✓ | ~33k ✓ | ~523k | 88 + | 0 9 \* \* \* | ~602k | ~355k ✓ | ~55k ✓ | ~33k ✓ | ~500k ✓ | 89 + | 0 9 15 \* 1 | ~865k | ~602k ✓ | ~93k ✓ | ~33k ✓ | ~522k ✓ | 90 + | 0 9 \* \* 1-5 | ~546k | ~348k ✓ | ~57k ✓ | ~32k ✓ | ~488k ✓ | 93 91 94 92 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 95 93 ··· 97 95 98 96 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 99 97 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 100 - | \* \* \* \* \* | ~236k | ~200k ✓ | ~46k ✓ | ~34k ✓ | ~598k ✗ | 101 - | 0 0 1 \* \* | ~723k | ~626k ✓ | ~127k ✓ | ~34k ✓ | ~642k ✓ | 102 - | 0 12 31 \* \* | ~664k | ~614k | ~122k ✓ | ~33k ✓ | ~628k | 103 - | _/15 _ \* \* \* | ~328k | ~288k ✓ | ~68k ✓ | ~34k ✓ | ~655k ✗ | 104 - | 0 9 \* \* \* | ~470k | ~407k ✓ | ~88k ✓ | ~34k ✓ | ~615k ✗ | 105 - | 0 9 15 \* 1 | ~968k | ~761k ✓ | ~138k ✓ | ~32k ✓ | ~651k ✓ | 106 - | 0 9 \* \* 1-5 | ~457k | ~405k ✓ | ~88k ✓ | ~33k ✓ | ~609k ✗ | 98 + | \* \* \* \* \* | ~387k | ~188k ✓ | ~28k ✓ | ~33k ✓ | ~480k ✗ | 99 + | 0 0 1 \* \* | ~710k | ~508k ✓ | ~81k ✓ | ~33k ✓ | ~515k ✓ | 100 + | 0 12 31 \* \* | ~682k | ~510k ✓ | ~81k ✓ | ~32k ✓ | ~510k ✓ | 101 + | _/15 _ \* \* \* | ~475k | ~267k ✓ | ~42k ✓ | ~33k ✓ | ~526k | 102 + | 0 9 \* \* \* | ~598k | ~353k ✓ | ~55k ✓ | ~33k ✓ | ~501k ✓ | 103 + | 0 9 15 \* 1 | ~876k | ~601k ✓ | ~93k ✓ | ~33k ✓ | ~524k ✓ | 104 + | 0 9 \* \* 1-5 | ~548k | ~343k ✓ | ~56k ✓ | ~32k ✓ | ~490k ✓ | 107 105 108 106 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower)
+47 -49
docs/benchmark-comparison-node.md
··· 1 1 # Benchmark & Feature Comparison 2 2 3 - > Tested with node v22.18.0, cron-fast v2.0.1, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0, cron-validate v1.5.3 3 + > Tested with node v22.18.0, cron-fast v2.1.0, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0, cron-validate v1.5.3 4 4 > Tested on MacBook M1 pro 5 5 6 6 ## Performance Benchmarks ··· 11 11 12 12 | Library | Avg ops/sec | vs cron-fast | 13 13 | ------------- | ----------- | ------------ | 14 - | **cron-fast** | ~367k | baseline | 15 - | cron-schedule | ~375k | 1.0x slower | 16 - | croner | ~30k | 12.2x faster | 17 - | cron-parser | ~33k | 11.1x faster | 14 + | **cron-fast** | ~459k | baseline | 15 + | cron-schedule | ~374k | 1.2x faster | 16 + | croner | ~31k | 15.0x faster | 17 + | cron-parser | ~32k | 14.2x faster | 18 18 19 19 ### Previous Execution Time 20 20 21 21 | Library | Avg ops/sec | vs cron-fast | 22 22 | ------------- | ----------- | ------------ | 23 - | **cron-fast** | ~409k | baseline | 24 - | cron-schedule | ~386k | 1.1x faster | 25 - | croner | ~31k | 13.4x faster | 26 - | cron-parser | ~37k | 11.0x faster | 23 + | **cron-fast** | ~512k | baseline | 24 + | cron-schedule | ~388k | 1.3x faster | 25 + | croner | ~31k | 16.7x faster | 26 + | cron-parser | ~37k | 14.0x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 32 - | **cron-fast** | ~555k | baseline | 33 - | cron-validate | ~664k | 1.2x slower | 34 - | cron-schedule | ~436k | 1.3x faster | 35 - | cron-parser | ~94k | 5.9x faster | 36 - | croner | ~32k | 17.2x faster | 32 + | **cron-fast** | ~733k | baseline | 33 + | cron-validate | ~643k | 1.1x faster | 34 + | cron-schedule | ~453k | 1.6x faster | 35 + | cron-parser | ~92k | 7.9x faster | 36 + | croner | ~34k | 21.4x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 42 - | **cron-fast** | ~543k | baseline | 43 - | cron-validate | ~659k | 1.2x slower | 44 - | cron-schedule | ~446k | 1.2x faster | 45 - | cron-parser | ~92k | 5.9x faster | 46 - | croner | ~32k | 17.2x faster | 47 - 48 - **Note**: cron-validate is validation-only (no scheduling), which explains its speed advantage in parsing/validation. It only checks syntax without calculating dates or handling timezones, making it significantly faster for validation-only use cases. 42 + | **cron-fast** | ~735k | baseline | 43 + | cron-validate | ~655k | 1.1x faster | 44 + | cron-schedule | ~458k | 1.6x faster | 45 + | cron-parser | ~92k | 8.0x faster | 46 + | croner | ~34k | 21.5x faster | 49 47 50 48 Run benchmarks yourself: `pnpm benchmark` 51 49 ··· 55 53 56 54 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 57 55 | --------------------------- | --------: | ------------: | -----: | ----------: | 58 - | Every minute | ~210k | ~172k ✓ | ~30k ✓ | ~30k ✓ | 59 - | Sparse: First of month | ~463k | ~482k | ~29k ✓ | ~18k ✓ | 60 - | Sparse: 31st (skips months) | ~461k | ~509k | ~28k ✓ | ~7k ✓ | 61 - | Step: Every 15 minutes | ~284k | ~262k | ~33k ✓ | ~55k ✓ | 62 - | Specific: 9 AM daily | ~378k | ~343k ✓ | ~33k ✓ | ~43k ✓ | 63 - | OR-mode: 15th OR Monday | ~402k | ~536k ✗ | ~29k ✓ | ~36k ✓ | 64 - | Weekdays: Mon-Fri 9 AM | ~369k | ~321k ✓ | ~29k ✓ | ~43k ✓ | 56 + | Every minute | ~361k | ~177k ✓ | ~32k ✓ | ~30k ✓ | 57 + | Sparse: First of month | ~554k | ~509k | ~32k ✓ | ~18k ✓ | 58 + | Sparse: 31st (skips months) | ~502k | ~477k | ~27k ✓ | ~7k ✓ | 59 + | Step: Every 15 minutes | ~416k | ~247k ✓ | ~31k ✓ | ~51k ✓ | 60 + | Specific: 9 AM daily | ~499k | ~333k ✓ | ~34k ✓ | ~42k ✓ | 61 + | OR-mode: 15th OR Monday | ~420k | ~546k ✗ | ~30k ✓ | ~36k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~461k | ~327k ✓ | ~28k ✓ | ~42k ✓ | 65 63 66 64 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 67 65 ··· 69 67 70 68 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 71 69 | --------------------------- | --------: | ------------: | -----: | ----------: | 72 - | Every minute | ~216k | ~175k ✓ | ~31k ✓ | ~34k ✓ | 73 - | Sparse: First of month | ~522k | ~519k | ~30k ✓ | ~8k ✓ | 74 - | Sparse: 31st (skips months) | ~448k | ~470k | ~30k ✓ | ~8k ✓ | 75 - | Step: Every 15 minutes | ~286k | ~260k | ~32k ✓ | ~55k ✓ | 76 - | Specific: 9 AM daily | ~356k | ~347k | ~31k ✓ | ~48k ✓ | 77 - | OR-mode: 15th OR Monday | ~663k | ~592k ✓ | ~30k ✓ | ~59k ✓ | 78 - | Weekdays: Mon-Fri 9 AM | ~371k | ~339k | ~30k ✓ | ~48k ✓ | 70 + | Every minute | ~372k | ~184k ✓ | ~32k ✓ | ~34k ✓ | 71 + | Sparse: First of month | ~593k | ~533k ✓ | ~31k ✓ | ~8k ✓ | 72 + | Sparse: 31st (skips months) | ~508k | ~436k ✓ | ~31k ✓ | ~8k ✓ | 73 + | Step: Every 15 minutes | ~404k | ~265k ✓ | ~29k ✓ | ~53k ✓ | 74 + | Specific: 9 AM daily | ~493k | ~348k ✓ | ~31k ✓ | ~45k ✓ | 75 + | OR-mode: 15th OR Monday | ~723k | ~597k ✓ | ~31k ✓ | ~59k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~494k | ~351k ✓ | ~31k ✓ | ~48k ✓ | 79 77 80 78 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 81 79 ··· 83 81 84 82 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 85 83 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 86 - | \* \* \* \* \* | ~233k | ~190k ✓ | ~45k ✓ | ~33k ✓ | ~648k ✗ | 87 - | 0 0 1 \* \* | ~759k | ~588k ✓ | ~124k ✓ | ~33k ✓ | ~674k ✓ | 88 - | 0 12 31 \* \* | ~689k | ~582k ✓ | ~124k ✓ | ~33k ✓ | ~659k | 89 - | _/15 _ \* \* \* | ~316k | ~265k ✓ | ~65k ✓ | ~32k ✓ | ~700k ✗ | 90 - | 0 9 \* \* \* | ~461k | ~369k ✓ | ~84k ✓ | ~32k ✓ | ~635k ✗ | 91 - | 0 9 15 \* 1 | ~979k | ~690k ✓ | ~131k ✓ | ~32k ✓ | ~686k ✓ | 92 - | 0 9 \* \* 1-5 | ~445k | ~368k ✓ | ~84k ✓ | ~30k ✓ | ~649k ✗ | 84 + | \* \* \* \* \* | ~425k | ~186k ✓ | ~44k ✓ | ~34k ✓ | ~625k ✗ | 85 + | 0 0 1 \* \* | ~879k | ~590k ✓ | ~116k ✓ | ~34k ✓ | ~633k ✓ | 86 + | 0 12 31 \* \* | ~832k | ~620k ✓ | ~119k ✓ | ~34k ✓ | ~658k ✓ | 87 + | _/15 _ \* \* \* | ~533k | ~281k ✓ | ~65k ✓ | ~34k ✓ | ~693k ✗ | 88 + | 0 9 \* \* \* | ~684k | ~377k ✓ | ~84k ✓ | ~34k ✓ | ~624k | 89 + | 0 9 15 \* 1 | ~1128k | ~751k ✓ | ~133k ✓ | ~34k ✓ | ~682k ✓ | 90 + | 0 9 \* \* 1-5 | ~649k | ~366k ✓ | ~86k ✓ | ~34k ✓ | ~584k ✓ | 93 91 94 92 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 95 93 ··· 97 95 98 96 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 99 97 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 100 - | \* \* \* \* \* | ~223k | ~184k ✓ | ~45k ✓ | ~32k ✓ | ~606k ✗ | 101 - | 0 0 1 \* \* | ~721k | ~586k ✓ | ~119k ✓ | ~32k ✓ | ~673k | 102 - | 0 12 31 \* \* | ~668k | ~592k ✓ | ~118k ✓ | ~31k ✓ | ~655k | 103 - | _/15 _ \* \* \* | ~319k | ~271k ✓ | ~65k ✓ | ~32k ✓ | ~708k ✗ | 104 - | 0 9 \* \* \* | ~458k | ~372k ✓ | ~83k ✓ | ~32k ✓ | ~639k ✗ | 105 - | 0 9 15 \* 1 | ~966k | ~748k ✓ | ~126k ✓ | ~31k ✓ | ~694k ✓ | 106 - | 0 9 \* \* 1-5 | ~448k | ~371k ✓ | ~86k ✓ | ~31k ✓ | ~637k ✗ | 98 + | \* \* \* \* \* | ~416k | ~192k ✓ | ~44k ✓ | ~34k ✓ | ~627k ✗ | 99 + | 0 0 1 \* \* | ~897k | ~604k ✓ | ~117k ✓ | ~34k ✓ | ~663k ✓ | 100 + | 0 12 31 \* \* | ~839k | ~602k ✓ | ~118k ✓ | ~34k ✓ | ~667k ✓ | 101 + | _/15 _ \* \* \* | ~523k | ~275k ✓ | ~64k ✓ | ~33k ✓ | ~694k ✗ | 102 + | 0 9 \* \* \* | ~696k | ~391k ✓ | ~84k ✓ | ~35k ✓ | ~638k | 103 + | 0 9 15 \* 1 | ~1132k | ~756k ✓ | ~133k ✓ | ~35k ✓ | ~665k ✓ | 104 + | 0 9 \* \* 1-5 | ~641k | ~387k ✓ | ~85k ✓ | ~34k ✓ | ~629k | 107 105 108 106 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower)
+1 -1
jsr.json
··· 1 1 { 2 2 "name": "@kbilkis/cron-fast", 3 - "version": "2.0.1", 3 + "version": "2.1.0", 4 4 "description": "Fast and tiny JavaScript/TypeScript cron parser with timezone support - works in Node.js, Deno, Bun, Cloudflare Workers, and browsers. Zero dependencies.", 5 5 "keywords": [ 6 6 "javascript",
+5 -1
package.json
··· 1 1 { 2 2 "name": "cron-fast", 3 - "version": "2.0.1", 3 + "version": "2.1.0", 4 4 "description": "Fast and tiny JavaScript/TypeScript cron parser with timezone support - works in Node.js, Deno, Bun, Cloudflare Workers, and browsers. Zero dependencies.", 5 5 "keywords": [ 6 6 "browser", ··· 30 30 "repository": { 31 31 "type": "git", 32 32 "url": "https://github.com/kbilkis/cron-fast.git" 33 + }, 34 + "funding": { 35 + "type": "github", 36 + "url": "https://github.com/sponsors/kbilkis" 33 37 }, 34 38 "files": [ 35 39 "dist",
+8 -8
src/parser.ts
··· 124 124 max: number, 125 125 names?: Record<string, number>, 126 126 ): number[] | null { 127 - const values = new Set<number>(); 127 + const values: number[] = []; 128 128 129 129 if (field === "*") { 130 - for (let i = min; i <= max; i++) values.add(i); 131 - return Array.from(values).sort((a, b) => a - b); 130 + for (let i = min; i <= max; i++) values.push(i); 131 + return values.slice().sort((a, b) => a - b); 132 132 } 133 133 134 134 const parts = field.split(","); ··· 160 160 } 161 161 162 162 for (let i = start; i <= end; i += step) { 163 - if (i >= min && i <= max) values.add(i); 163 + if (i >= min && i <= max) values.push(i); 164 164 } 165 165 } 166 166 // Handle ranges (e.g., 1-5) ··· 174 174 if (start > end) return null; 175 175 176 176 for (let i = start; i <= end; i++) { 177 - if (i >= min && i <= max) values.add(i); 177 + if (i >= min && i <= max) values.push(i); 178 178 } 179 179 } 180 180 // Handle single values ··· 182 182 const value = parseValue(part, names); 183 183 if (value === null) return null; 184 184 if (value < min || value > max) return null; 185 - values.add(value); 185 + values.push(value); 186 186 } 187 187 } 188 188 189 - if (values.size === 0) return null; 190 - return Array.from(values).sort((a, b) => a - b); 189 + if (values.length === 0) return null; 190 + return [...new Set(values)].sort((a, b) => a - b); 191 191 } 192 192 193 193 /**