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.

improve deduplication perf in parser

+173 -165
+26 -20
README.md
··· 14 14 15 15 ## Features 16 16 17 + - **Parse & validate** - Convert cron expressions to structured data and check validity 18 + - **Get execution times** - Calculate next, previous, or multiple scheduled runs 19 + - **Match dates** - Check if a date matches a cron expression 20 + - **Describe** - Convert cron expressions to human-readable text (e.g., "Every 5 minutes") 21 + - **Timezone support** - Full IANA timezone support using native `Intl` API 22 + 23 + ## Why cron-fast? 24 + 17 25 - **Universal** - Works in Node.js, Deno, Bun, Cloudflare Workers, and browsers 18 - - **Lightweight** - Zero dependencies 19 - - **Fast** - Optimal field increment algorithm 26 + - **Zero dependencies** - Lightweight and secure 27 + - **Fast** - Optimal field increment algorithm with 10x+ performance vs alternatives 20 28 - **Tree-shakeable** - Import only what you need 21 - - **Timezone support** - Built-in timezone handling using native `Intl` 22 29 - **Modern** - ESM + CJS, TypeScript-first 23 30 - **Fully tested** - Comprehensive test coverage across all runtimes 24 - - **Simple API** - Clean, intuitive interface 25 31 - **ISO 8601 compatible** - Works with all standard date formats 26 32 27 33 ## Installation ··· 174 180 175 181 ## Bundle Size 176 182 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): 183 + cron-fast is extremely lightweight and fully tree-shakeable. Here are the actual bundle sizes for different import scenarios (tested with v2.2.0): 178 184 179 185 | Import | Raw | Minified | Gzipped | 180 186 | ------------------------------------------------------ | -------- | -------- | ----------- | 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** | 187 + | `Full bundle (all exports) ` | 21.87 KB | 10.13 KB | **3.62 KB** | 188 + | `nextRun only ` | 13.13 KB | 6.04 KB | **2.23 KB** | 189 + | `previousRun only ` | 13.14 KB | 6.04 KB | **2.23 KB** | 190 + | `nextRuns only ` | 13.51 KB | 6.19 KB | **2.29 KB** | 191 + | `isValid only ` | 4.45 KB | 2.24 KB | **992 B** | 192 + | `parse only ` | 4.34 KB | 2.20 KB | **969 B** | 193 + | `describe only ` | 11.56 KB | 5.59 KB | **2.11 KB** | 194 + | `isMatch only ` | 6.36 KB | 3.16 KB | **1.34 KB** | 195 + | `Validation only (isValid + parse) ` | 4.46 KB | 2.25 KB | **993 B** | 196 + | `Scheduling only (nextRun + previousRun + nextRuns) ` | 13.92 KB | 6.37 KB | **2.31 KB** | 191 197 192 198 Import only what you need: 193 199 ··· 284 290 285 291 cron-fast is designed for speed and efficiency. Here's how it compares to popular alternatives: 286 292 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 293 + > Tested with cron-fast v2.2.0, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0 on Node.js v22.18.0 288 294 289 295 | Operation | cron-fast | croner | cron-parser | cron-schedule | 290 296 | ------------ | -------------- | --------- | ----------- | ------------- | 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 | 297 + | Next run | **484k ops/s** | 30k ops/s | 33k ops/s | 380k ops/s | 298 + | Previous run | **551k ops/s** | 31k ops/s | 38k ops/s | 393k ops/s | 299 + | Validation | **651k ops/s** | 28k ops/s | 78k ops/s | 372k ops/s | 300 + | Parsing | **718k ops/s** | 29k ops/s | 86k ops/s | 430k ops/s | 295 301 296 302 See [detailed benchmarks and feature comparison](docs/benchmark-comparison.md) (including Deno and Bun runtimes) for more information. 297 303
+47 -47
docs/benchmark-comparison-bun.md
··· 1 1 # Benchmark & Feature Comparison 2 2 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 3 + > Tested with bun v1.3.9, cron-fast v2.2.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** | ~380k | baseline | 15 - | cron-schedule | ~313k | 1.2x faster | 16 - | croner | ~55k | 6.9x faster | 17 - | cron-parser | ~38k | 10.1x faster | 14 + | **cron-fast** | ~390k | baseline | 15 + | cron-schedule | ~310k | 1.3x faster | 16 + | croner | ~54k | 7.2x faster | 17 + | cron-parser | ~37k | 10.7x faster | 18 18 19 19 ### Previous Execution Time 20 20 21 21 | Library | Avg ops/sec | vs cron-fast | 22 22 | ------------- | ----------- | ------------ | 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 | 23 + | **cron-fast** | ~435k | baseline | 24 + | cron-schedule | ~313k | 1.4x faster | 25 + | croner | ~55k | 7.9x faster | 26 + | cron-parser | ~40k | 10.9x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 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 | 32 + | **cron-fast** | ~526k | baseline | 33 + | cron-validate | ~945k | 1.8x slower | 34 + | cron-schedule | ~346k | 1.5x faster | 35 + | cron-parser | ~120k | 4.4x faster | 36 + | croner | ~59k | 9.0x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 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 | 42 + | **cron-fast** | ~527k | baseline | 43 + | cron-validate | ~960k | 1.8x slower | 44 + | cron-schedule | ~350k | 1.5x faster | 45 + | cron-parser | ~124k | 4.2x faster | 46 + | croner | ~62k | 8.5x faster | 47 47 48 48 Run benchmarks yourself: `pnpm benchmark:bun` 49 49 ··· 53 53 54 54 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 55 55 | --------------------------- | --------: | ------------: | -----: | ----------: | 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 ✓ | 56 + | Every minute | ~457k | ~147k ✓ | ~54k ✓ | ~34k ✓ | 57 + | Sparse: First of month | ~431k | ~408k | ~56k ✓ | ~22k ✓ | 58 + | Sparse: 31st (skips months) | ~435k | ~411k | ~55k ✓ | ~9k ✓ | 59 + | Step: Every 15 minutes | ~405k | ~213k ✓ | ~59k ✓ | ~63k ✓ | 60 + | Specific: 9 AM daily | ~344k | ~283k ✓ | ~60k ✓ | ~45k ✓ | 61 + | OR-mode: 15th OR Monday | ~347k | ~443k ✗ | ~46k ✓ | ~39k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~309k | ~263k ✓ | ~48k ✓ | ~44k ✓ | 63 63 64 64 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 65 65 ··· 67 67 68 68 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 69 69 | --------------------------- | --------: | ------------: | -----: | ----------: | 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 ✓ | 70 + | Every minute | ~424k | ~152k ✓ | ~58k ✓ | ~36k ✓ | 71 + | Sparse: First of month | ~489k | ~415k ✓ | ~57k ✓ | ~10k ✓ | 72 + | Sparse: 31st (skips months) | ~424k | ~401k | ~51k ✓ | ~10k ✓ | 73 + | Step: Every 15 minutes | ~383k | ~180k ✓ | ~55k ✓ | ~61k ✓ | 74 + | Specific: 9 AM daily | ~446k | ~280k ✓ | ~56k ✓ | ~49k ✓ | 75 + | OR-mode: 15th OR Monday | ~518k | ~489k | ~58k ✓ | ~61k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~359k | ~274k ✓ | ~53k ✓ | ~51k ✓ | 77 77 78 78 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 79 79 ··· 81 81 82 82 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 83 83 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 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 ✗ | 84 + | \* \* \* \* \* | ~464k | ~155k ✓ | ~51k ✓ | ~61k ✓ | ~901k ✗ | 85 + | 0 0 1 \* \* | ~564k | ~447k ✓ | ~155k ✓ | ~60k ✓ | ~954k ✗ | 86 + | 0 12 31 \* \* | ~644k | ~454k ✓ | ~160k ✓ | ~57k ✓ | ~922k ✗ | 87 + | _/15 _ \* \* \* | ~445k | ~221k ✓ | ~85k ✓ | ~60k ✓ | ~998k ✗ | 88 + | 0 9 \* \* \* | ~495k | ~296k ✓ | ~109k ✓ | ~61k ✓ | ~952k ✗ | 89 + | 0 9 15 \* 1 | ~622k | ~574k | ~172k ✓ | ~53k ✓ | ~954k ✗ | 90 + | 0 9 \* \* 1-5 | ~450k | ~276k ✓ | ~106k ✓ | ~58k ✓ | ~932k ✗ | 91 91 92 92 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 93 93 ··· 95 95 96 96 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 97 97 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 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 ✗ | 98 + | \* \* \* \* \* | ~457k | ~157k ✓ | ~51k ✓ | ~60k ✓ | ~940k ✗ | 99 + | 0 0 1 \* \* | ~583k | ~424k ✓ | ~159k ✓ | ~62k ✓ | ~943k ✗ | 100 + | 0 12 31 \* \* | ~611k | ~447k ✓ | ~164k ✓ | ~63k ✓ | ~955k ✗ | 101 + | _/15 _ \* \* \* | ~430k | ~225k ✓ | ~87k ✓ | ~61k ✓ | ~1008k ✗ | 102 + | 0 9 \* \* \* | ~510k | ~310k ✓ | ~112k ✓ | ~62k ✓ | ~948k ✗ | 103 + | 0 9 15 \* 1 | ~679k | ~581k ✓ | ~182k ✓ | ~63k ✓ | ~959k ✗ | 104 + | 0 9 \* \* 1-5 | ~419k | ~304k ✓ | ~113k ✓ | ~62k ✓ | ~967k ✗ | 105 105 106 106 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower)
+47 -47
docs/benchmark-comparison-deno.md
··· 1 1 # Benchmark & Feature Comparison 2 2 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 3 + > Tested with deno v2.6.8, cron-fast v2.2.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** | ~384k | baseline | 15 - | cron-schedule | ~341k | 1.1x faster | 16 - | croner | ~29k | 13.1x faster | 17 - | cron-parser | ~23k | 17.0x faster | 14 + | **cron-fast** | ~478k | baseline | 15 + | cron-schedule | ~405k | 1.2x faster | 16 + | croner | ~32k | 15.1x faster | 17 + | cron-parser | ~34k | 14.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** | ~423k | baseline | 24 - | cron-schedule | ~350k | 1.2x faster | 25 - | croner | ~30k | 14.0x faster | 26 - | cron-parser | ~26k | 16.5x faster | 23 + | **cron-fast** | ~545k | baseline | 24 + | cron-schedule | ~426k | 1.3x faster | 25 + | croner | ~32k | 17.0x faster | 26 + | cron-parser | ~39k | 13.8x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 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 | 32 + | **cron-fast** | ~775k | baseline | 33 + | cron-validate | ~638k | 1.2x faster | 34 + | cron-schedule | ~479k | 1.6x faster | 35 + | cron-parser | ~99k | 7.9x faster | 36 + | croner | ~34k | 22.8x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 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 | 42 + | **cron-fast** | ~776k | baseline | 43 + | cron-validate | ~629k | 1.2x faster | 44 + | cron-schedule | ~473k | 1.6x faster | 45 + | cron-parser | ~97k | 8.0x faster | 46 + | croner | ~34k | 23.0x faster | 47 47 48 48 Run benchmarks yourself: `pnpm benchmark:deno` 49 49 ··· 53 53 54 54 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 55 55 | --------------------------- | --------: | ------------: | -----: | ----------: | 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 ✓ | 56 + | Every minute | ~355k | ~149k ✓ | ~33k ✓ | ~31k ✓ | 57 + | Sparse: First of month | ~576k | ~540k | ~33k ✓ | ~18k ✓ | 58 + | Sparse: 31st (skips months) | ~517k | ~542k | ~30k ✓ | ~7k ✓ | 59 + | Step: Every 15 minutes | ~444k | ~275k ✓ | ~33k ✓ | ~55k ✓ | 60 + | Specific: 9 AM daily | ~521k | ~375k ✓ | ~32k ✓ | ~44k ✓ | 61 + | OR-mode: 15th OR Monday | ~444k | ~593k ✗ | ~30k ✓ | ~37k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~488k | ~358k ✓ | ~30k ✓ | ~45k ✓ | 63 63 64 64 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 65 65 ··· 67 67 68 68 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 69 69 | --------------------------- | --------: | ------------: | -----: | ----------: | 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 ✓ | 70 + | Every minute | ~394k | ~190k ✓ | ~33k ✓ | ~36k ✓ | 71 + | Sparse: First of month | ~641k | ~577k ✓ | ~31k ✓ | ~9k ✓ | 72 + | Sparse: 31st (skips months) | ~524k | ~519k | ~32k ✓ | ~8k ✓ | 73 + | Step: Every 15 minutes | ~441k | ~278k ✓ | ~33k ✓ | ~57k ✓ | 74 + | Specific: 9 AM daily | ~541k | ~382k ✓ | ~32k ✓ | ~50k ✓ | 75 + | OR-mode: 15th OR Monday | ~764k | ~668k ✓ | ~32k ✓ | ~66k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~511k | ~372k ✓ | ~31k ✓ | ~50k ✓ | 77 77 78 78 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 79 79 ··· 81 81 82 82 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 83 83 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 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 ✓ | 84 + | \* \* \* \* \* | ~440k | ~202k ✓ | ~46k ✓ | ~34k ✓ | ~606k ✗ | 85 + | 0 0 1 \* \* | ~977k | ~630k ✓ | ~128k ✓ | ~34k ✓ | ~649k ✓ | 86 + | 0 12 31 \* \* | ~897k | ~627k ✓ | ~127k ✓ | ~34k ✓ | ~643k ✓ | 87 + | _/15 _ \* \* \* | ~546k | ~291k ✓ | ~69k ✓ | ~34k ✓ | ~676k ✗ | 88 + | 0 9 \* \* \* | ~725k | ~403k ✓ | ~89k ✓ | ~34k ✓ | ~618k ✓ | 89 + | 0 9 15 \* 1 | ~1168k | ~785k ✓ | ~140k ✓ | ~33k ✓ | ~655k ✓ | 90 + | 0 9 \* \* 1-5 | ~669k | ~412k ✓ | ~91k ✓ | ~34k ✓ | ~620k | 91 91 92 92 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 93 93 ··· 95 95 96 96 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 97 97 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 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 ✓ | 98 + | \* \* \* \* \* | ~437k | ~201k ✓ | ~46k ✓ | ~35k ✓ | ~598k ✗ | 99 + | 0 0 1 \* \* | ~982k | ~631k ✓ | ~126k ✓ | ~33k ✓ | ~649k ✓ | 100 + | 0 12 31 \* \* | ~895k | ~633k ✓ | ~127k ✓ | ~34k ✓ | ~636k ✓ | 101 + | _/15 _ \* \* \* | ~550k | ~294k ✓ | ~68k ✓ | ~33k ✓ | ~678k ✗ | 102 + | 0 9 \* \* \* | ~748k | ~367k ✓ | ~88k ✓ | ~35k ✓ | ~615k ✓ | 103 + | 0 9 15 \* 1 | ~1175k | ~793k ✓ | ~140k ✓ | ~34k ✓ | ~625k ✓ | 104 + | 0 9 \* \* 1-5 | ~642k | ~395k ✓ | ~84k ✓ | ~32k ✓ | ~598k | 105 105 106 106 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower)
+47 -47
docs/benchmark-comparison-node.md
··· 1 1 # Benchmark & Feature Comparison 2 2 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 3 + > Tested with node v22.18.0, cron-fast v2.2.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** | ~459k | baseline | 15 - | cron-schedule | ~374k | 1.2x faster | 16 - | croner | ~31k | 15.0x faster | 17 - | cron-parser | ~32k | 14.2x faster | 14 + | **cron-fast** | ~484k | baseline | 15 + | cron-schedule | ~380k | 1.3x faster | 16 + | croner | ~30k | 15.9x faster | 17 + | cron-parser | ~33k | 14.5x faster | 18 18 19 19 ### Previous Execution Time 20 20 21 21 | Library | Avg ops/sec | vs cron-fast | 22 22 | ------------- | ----------- | ------------ | 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 | 23 + | **cron-fast** | ~551k | baseline | 24 + | cron-schedule | ~393k | 1.4x faster | 25 + | croner | ~31k | 17.6x faster | 26 + | cron-parser | ~38k | 14.6x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 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 | 32 + | **cron-fast** | ~651k | baseline | 33 + | cron-validate | ~579k | 1.1x faster | 34 + | cron-schedule | ~372k | 1.8x faster | 35 + | cron-parser | ~78k | 8.4x faster | 36 + | croner | ~28k | 23.5x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 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 | 42 + | **cron-fast** | ~718k | baseline | 43 + | cron-validate | ~620k | 1.2x faster | 44 + | cron-schedule | ~430k | 1.7x faster | 45 + | cron-parser | ~86k | 8.3x faster | 46 + | croner | ~29k | 24.4x faster | 47 47 48 48 Run benchmarks yourself: `pnpm benchmark` 49 49 ··· 53 53 54 54 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 55 55 | --------------------------- | --------: | ------------: | -----: | ----------: | 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 ✓ | 56 + | Every minute | ~357k | ~172k ✓ | ~31k ✓ | ~31k ✓ | 57 + | Sparse: First of month | ~586k | ~511k ✓ | ~31k ✓ | ~18k ✓ | 58 + | Sparse: 31st (skips months) | ~563k | ~513k | ~29k ✓ | ~7k ✓ | 59 + | Step: Every 15 minutes | ~446k | ~253k ✓ | ~34k ✓ | ~55k ✓ | 60 + | Specific: 9 AM daily | ~504k | ~339k ✓ | ~31k ✓ | ~41k ✓ | 61 + | OR-mode: 15th OR Monday | ~431k | ~546k ✗ | ~28k ✓ | ~36k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~499k | ~326k ✓ | ~29k ✓ | ~43k ✓ | 63 63 64 64 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 65 65 ··· 67 67 68 68 | Test Case | cron-fast | cron-schedule | croner | cron-parser | 69 69 | --------------------------- | --------: | ------------: | -----: | ----------: | 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 ✓ | 70 + | Every minute | ~395k | ~182k ✓ | ~32k ✓ | ~35k ✓ | 71 + | Sparse: First of month | ~660k | ~535k ✓ | ~31k ✓ | ~9k ✓ | 72 + | Sparse: 31st (skips months) | ~544k | ~482k ✓ | ~30k ✓ | ~8k ✓ | 73 + | Step: Every 15 minutes | ~442k | ~259k ✓ | ~33k ✓ | ~56k ✓ | 74 + | Specific: 9 AM daily | ~536k | ~362k ✓ | ~31k ✓ | ~49k ✓ | 75 + | OR-mode: 15th OR Monday | ~765k | ~582k ✓ | ~32k ✓ | ~59k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~517k | ~351k ✓ | ~31k ✓ | ~49k ✓ | 77 77 78 78 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 79 79 ··· 81 81 82 82 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 83 83 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 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 ✓ | 84 + | \* \* \* \* \* | ~444k | ~196k ✓ | ~47k ✓ | ~35k ✓ | ~647k ✗ | 85 + | 0 0 1 \* \* | ~711k | ~417k ✓ | ~90k ✓ | ~24k ✓ | ~568k ✓ | 86 + | 0 12 31 \* \* | ~702k | ~511k ✓ | ~99k ✓ | ~25k ✓ | ~565k ✓ | 87 + | _/15 _ \* \* \* | ~455k | ~218k ✓ | ~54k ✓ | ~28k ✓ | ~598k ✗ | 88 + | 0 9 \* \* \* | ~678k | ~323k ✓ | ~71k ✓ | ~27k ✓ | ~556k ✓ | 89 + | 0 9 15 \* 1 | ~1004k | ~620k ✓ | ~111k ✓ | ~28k ✓ | ~586k ✓ | 90 + | 0 9 \* \* 1-5 | ~562k | ~316k ✓ | ~72k ✓ | ~27k ✓ | ~533k | 91 91 92 92 ✓ = cron-fast is faster (≥10% faster) | ✗ = cron-fast is slower (≥10% slower) 93 93 ··· 95 95 96 96 | Test Case | cron-fast | cron-schedule | cron-parser | croner | cron-validate | 97 97 | --------------- | --------: | ------------: | ----------: | -----: | ------------: | 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 | 98 + | \* \* \* \* \* | ~353k | ~152k ✓ | ~37k ✓ | ~27k ✓ | ~524k ✗ | 99 + | 0 0 1 \* \* | ~811k | ~501k ✓ | ~98k ✓ | ~25k ✓ | ~553k ✓ | 100 + | 0 12 31 \* \* | ~753k | ~511k ✓ | ~99k ✓ | ~27k ✓ | ~546k ✓ | 101 + | _/15 _ \* \* \* | ~431k | ~284k ✓ | ~55k ✓ | ~24k ✓ | ~727k ✗ | 102 + | 0 9 \* \* \* | ~767k | ~398k ✓ | ~87k ✓ | ~34k ✓ | ~670k ✓ | 103 + | 0 9 15 \* 1 | ~1225k | ~766k ✓ | ~138k ✓ | ~34k ✓ | ~701k ✓ | 104 + | 0 9 \* \* 1-5 | ~688k | ~399k ✓ | ~89k ✓ | ~34k ✓ | ~617k ✓ | 105 105 106 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.1.0", 3 + "version": "2.2.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",
+1 -1
package.json
··· 1 1 { 2 2 "name": "cron-fast", 3 - "version": "2.1.0", 3 + "version": "2.2.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",
+4 -2
src/parser.ts
··· 76 76 `Invalid cron expression: "${expression}" - invalid weekday field "${weekdayStr}"`, 77 77 ); 78 78 79 - const weekdays = weekdayRaw.map((d) => (d === 7 ? 0 : d)); 79 + const weekdays = weekdayRaw 80 + .filter((d) => d !== 7 || !weekdayRaw.includes(0)) 81 + .map((d) => (d === 7 ? 0 : d)); 80 82 81 83 const parsed: ParsedCron = { 82 84 minute, 83 85 hour, 84 86 day, 85 87 month: month.map((m) => m - 1), 86 - weekday: Array.from(new Set(weekdays)).sort((a, b) => a - b), 88 + weekday: weekdays.sort((a, b) => a - b), 87 89 dayIsWildcard: dayStr.trim() === "*", 88 90 weekdayIsWildcard: weekdayStr.trim() === "*", 89 91 };