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.

removed redundant sorting for perf gains

+164 -164
+18 -18
README.md
··· 180 180 181 181 ## Bundle Size 182 182 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): 183 + cron-fast is extremely lightweight and fully tree-shakeable. Here are the actual bundle sizes for different import scenarios (tested with v2.3.0): 184 184 185 185 | Import | Raw | Minified | Gzipped | 186 186 | ------------------------------------------------------ | -------- | -------- | ----------- | 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** | 187 + | `Full bundle (all exports) ` | 21.86 KB | 10.11 KB | **3.61 KB** | 188 + | `nextRun only ` | 13.11 KB | 6.02 KB | **2.22 KB** | 189 + | `previousRun only ` | 13.12 KB | 6.02 KB | **2.22 KB** | 190 + | `nextRuns only ` | 13.50 KB | 6.17 KB | **2.28 KB** | 191 + | `isValid only ` | 4.44 KB | 2.22 KB | **984 B** | 192 + | `parse only ` | 4.32 KB | 2.18 KB | **961 B** | 193 + | `describe only ` | 11.54 KB | 5.57 KB | **2.11 KB** | 194 + | `isMatch only ` | 6.34 KB | 3.14 KB | **1.33 KB** | 195 + | `Validation only (isValid + parse) ` | 4.45 KB | 2.23 KB | **986 B** | 196 + | `Scheduling only (nextRun + previousRun + nextRuns) ` | 13.90 KB | 6.35 KB | **2.30 KB** | 197 197 198 198 Import only what you need: 199 199 ··· 290 290 291 291 cron-fast is designed for speed and efficiency. Here's how it compares to popular alternatives: 292 292 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 293 + > Tested with cron-fast v2.3.0, croner v10.0.1, cron-parser v5.5.0, cron-schedule v6.0.0 on Node.js v22.18.0 294 294 295 - | Operation | cron-fast | croner | cron-parser | cron-schedule | 296 - | ------------ | -------------- | --------- | ----------- | ------------- | 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 + | Operation | cron-fast | croner | cron-parser | cron-schedule | 296 + | ------------ | --------------- | --------- | ----------- | ------------- | 297 + | Next run | **913k ops/s** | 31k ops/s | 33k ops/s | 381k ops/s | 298 + | Previous run | **991k ops/s** | 30k ops/s | 38k ops/s | 391k ops/s | 299 + | Validation | **1899k ops/s** | 33k ops/s | 94k ops/s | 452k ops/s | 300 + | Parsing | **1913k ops/s** | 33k ops/s | 95k ops/s | 447k ops/s | 301 301 302 302 See [detailed benchmarks and feature comparison](docs/benchmark-comparison.md) (including Deno and Bun runtimes) for more information. 303 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.2.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.3.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** | ~390k | baseline | 15 - | cron-schedule | ~310k | 1.3x faster | 16 - | croner | ~54k | 7.2x faster | 17 - | cron-parser | ~37k | 10.7x faster | 14 + | **cron-fast** | ~656k | baseline | 15 + | cron-schedule | ~318k | 2.1x faster | 16 + | croner | ~56k | 11.6x faster | 17 + | cron-parser | ~37k | 17.6x faster | 18 18 19 19 ### Previous Execution Time 20 20 21 21 | Library | Avg ops/sec | vs cron-fast | 22 22 | ------------- | ----------- | ------------ | 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 | 23 + | **cron-fast** | ~604k | baseline | 24 + | cron-schedule | ~319k | 1.9x faster | 25 + | croner | ~57k | 10.5x faster | 26 + | cron-parser | ~40k | 15.3x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 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 | 32 + | **cron-fast** | ~827k | baseline | 33 + | cron-validate | ~961k | 1.2x slower | 34 + | cron-schedule | ~354k | 2.3x faster | 35 + | cron-parser | ~121k | 6.8x faster | 36 + | croner | ~61k | 13.4x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 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 | 42 + | **cron-fast** | ~858k | baseline | 43 + | cron-validate | ~955k | 1.1x slower | 44 + | cron-schedule | ~352k | 2.4x faster | 45 + | cron-parser | ~121k | 7.1x faster | 46 + | croner | ~60k | 14.4x 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 | ~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 ✓ | 56 + | Every minute | ~1180k | ~158k ✓ | ~60k ✓ | ~37k ✓ | 57 + | Sparse: First of month | ~606k | ~414k ✓ | ~59k ✓ | ~22k ✓ | 58 + | Sparse: 31st (skips months) | ~585k | ~415k ✓ | ~53k ✓ | ~9k ✓ | 59 + | Step: Every 15 minutes | ~569k | ~216k ✓ | ~59k ✓ | ~63k ✓ | 60 + | Specific: 9 AM daily | ~704k | ~289k ✓ | ~61k ✓ | ~45k ✓ | 61 + | OR-mode: 15th OR Monday | ~458k | ~467k | ~52k ✓ | ~41k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~490k | ~269k ✓ | ~50k ✓ | ~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 | ~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 ✓ | 70 + | Every minute | ~528k | ~158k ✓ | ~58k ✓ | ~37k ✓ | 71 + | Sparse: First of month | ~735k | ~430k ✓ | ~57k ✓ | ~11k ✓ | 72 + | Sparse: 31st (skips months) | ~547k | ~412k ✓ | ~56k ✓ | ~10k ✓ | 73 + | Step: Every 15 minutes | ~547k | ~215k ✓ | ~60k ✓ | ~60k ✓ | 74 + | Specific: 9 AM daily | ~645k | ~286k ✓ | ~59k ✓ | ~49k ✓ | 75 + | OR-mode: 15th OR Monday | ~691k | ~462k ✓ | ~57k ✓ | ~59k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~532k | ~268k ✓ | ~54k ✓ | ~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 - | \* \* \* \* \* | ~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 ✗ | 84 + | \* \* \* \* \* | ~464k | ~157k ✓ | ~50k ✓ | ~64k ✓ | ~952k ✗ | 85 + | 0 0 1 \* \* | ~1128k | ~463k ✓ | ~156k ✓ | ~62k ✓ | ~989k ✓ | 86 + | 0 12 31 \* \* | ~977k | ~463k ✓ | ~159k ✓ | ~60k ✓ | ~946k | 87 + | _/15 _ \* \* \* | ~642k | ~218k ✓ | ~85k ✓ | ~62k ✓ | ~990k ✗ | 88 + | 0 9 \* \* \* | ~940k | ~305k ✓ | ~109k ✓ | ~61k ✓ | ~954k | 89 + | 0 9 15 \* 1 | ~874k | ~574k ✓ | ~178k ✓ | ~61k ✓ | ~966k | 90 + | 0 9 \* \* 1-5 | ~764k | ~294k ✓ | ~108k ✓ | ~61k ✓ | ~931k ✗ | 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 - | \* \* \* \* \* | ~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 ✗ | 98 + | \* \* \* \* \* | ~962k | ~163k ✓ | ~52k ✓ | ~58k ✓ | ~946k | 99 + | 0 0 1 \* \* | ~842k | ~456k ✓ | ~159k ✓ | ~58k ✓ | ~958k ✗ | 100 + | 0 12 31 \* \* | ~964k | ~450k ✓ | ~161k ✓ | ~62k ✓ | ~941k | 101 + | _/15 _ \* \* \* | ~648k | ~227k ✓ | ~86k ✓ | ~59k ✓ | ~989k ✗ | 102 + | 0 9 \* \* \* | ~949k | ~307k ✓ | ~109k ✓ | ~63k ✓ | ~937k | 103 + | 0 9 15 \* 1 | ~877k | ~570k ✓ | ~171k ✓ | ~57k ✓ | ~956k | 104 + | 0 9 \* \* 1-5 | ~766k | ~295k ✓ | ~109k ✓ | ~61k ✓ | ~956k ✗ | 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.2.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.3.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** | ~478k | baseline | 15 - | cron-schedule | ~405k | 1.2x faster | 16 - | croner | ~32k | 15.1x faster | 17 - | cron-parser | ~34k | 14.1x faster | 14 + | **cron-fast** | ~894k | baseline | 15 + | cron-schedule | ~400k | 2.2x faster | 16 + | croner | ~31k | 28.5x faster | 17 + | cron-parser | ~35k | 25.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** | ~545k | baseline | 24 - | cron-schedule | ~426k | 1.3x faster | 25 - | croner | ~32k | 17.0x faster | 26 - | cron-parser | ~39k | 13.8x faster | 23 + | **cron-fast** | ~1014k | baseline | 24 + | cron-schedule | ~427k | 2.4x faster | 25 + | croner | ~32k | 32.0x faster | 26 + | cron-parser | ~41k | 24.7x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 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 | 32 + | **cron-fast** | ~1923k | baseline | 33 + | cron-validate | ~660k | 2.9x faster | 34 + | cron-schedule | ~463k | 4.2x faster | 35 + | cron-parser | ~101k | 19.1x faster | 36 + | croner | ~34k | 57.3x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 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 | 42 + | **cron-fast** | ~1931k | baseline | 43 + | cron-validate | ~668k | 2.9x faster | 44 + | cron-schedule | ~482k | 4.0x faster | 45 + | cron-parser | ~103k | 18.7x faster | 46 + | croner | ~34k | 57.4x 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 | ~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 ✓ | 56 + | Every minute | ~1381k | ~149k ✓ | ~32k ✓ | ~33k ✓ | 57 + | Sparse: First of month | ~829k | ~543k ✓ | ~32k ✓ | ~19k ✓ | 58 + | Sparse: 31st (skips months) | ~744k | ~531k ✓ | ~30k ✓ | ~7k ✓ | 59 + | Step: Every 15 minutes | ~932k | ~267k ✓ | ~33k ✓ | ~57k ✓ | 60 + | Specific: 9 AM daily | ~1044k | ~370k ✓ | ~32k ✓ | ~45k ✓ | 61 + | OR-mode: 15th OR Monday | ~523k | ~581k | ~30k ✓ | ~40k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~809k | ~362k ✓ | ~30k ✓ | ~46k ✓ | 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 | ~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 ✓ | 70 + | Every minute | ~1430k | ~192k ✓ | ~33k ✓ | ~37k ✓ | 71 + | Sparse: First of month | ~1023k | ~583k ✓ | ~32k ✓ | ~9k ✓ | 72 + | Sparse: 31st (skips months) | ~757k | ~520k ✓ | ~32k ✓ | ~9k ✓ | 73 + | Step: Every 15 minutes | ~931k | ~277k ✓ | ~32k ✓ | ~59k ✓ | 74 + | Specific: 9 AM daily | ~1074k | ~386k ✓ | ~33k ✓ | ~53k ✓ | 75 + | OR-mode: 15th OR Monday | ~1016k | ~658k ✓ | ~31k ✓ | ~67k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~867k | ~378k ✓ | ~31k ✓ | ~54k ✓ | 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 - | \* \* \* \* \* | ~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 | 84 + | \* \* \* \* \* | ~2216k | ~200k ✓ | ~47k ✓ | ~34k ✓ | ~620k ✓ | 85 + | 0 0 1 \* \* | ~2222k | ~540k ✓ | ~132k ✓ | ~34k ✓ | ~669k ✓ | 86 + | 0 12 31 \* \* | ~1806k | ~625k ✓ | ~133k ✓ | ~34k ✓ | ~648k ✓ | 87 + | _/15 _ \* \* \* | ~1653k | ~292k ✓ | ~68k ✓ | ~33k ✓ | ~714k ✓ | 88 + | 0 9 \* \* \* | ~2278k | ~407k ✓ | ~93k ✓ | ~35k ✓ | ~645k ✓ | 89 + | 0 9 15 \* 1 | ~1859k | ~769k ✓ | ~137k ✓ | ~31k ✓ | ~682k ✓ | 90 + | 0 9 \* \* 1-5 | ~1423k | ~409k ✓ | ~95k ✓ | ~34k ✓ | ~645k ✓ | 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 - | \* \* \* \* \* | ~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 | 98 + | \* \* \* \* \* | ~2214k | ~199k ✓ | ~47k ✓ | ~32k ✓ | ~622k ✓ | 99 + | 0 0 1 \* \* | ~2182k | ~637k ✓ | ~133k ✓ | ~34k ✓ | ~678k ✓ | 100 + | 0 12 31 \* \* | ~1835k | ~624k ✓ | ~132k ✓ | ~33k ✓ | ~669k ✓ | 101 + | _/15 _ \* \* \* | ~1675k | ~291k ✓ | ~72k ✓ | ~33k ✓ | ~717k ✓ | 102 + | 0 9 \* \* \* | ~2307k | ~404k ✓ | ~93k ✓ | ~35k ✓ | ~648k ✓ | 103 + | 0 9 15 \* 1 | ~1868k | ~805k ✓ | ~149k ✓ | ~35k ✓ | ~693k ✓ | 104 + | 0 9 \* \* 1-5 | ~1437k | ~413k ✓ | ~96k ✓ | ~34k ✓ | ~651k ✓ | 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.2.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.3.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** | ~484k | baseline | 15 - | cron-schedule | ~380k | 1.3x faster | 16 - | croner | ~30k | 15.9x faster | 17 - | cron-parser | ~33k | 14.5x faster | 14 + | **cron-fast** | ~913k | baseline | 15 + | cron-schedule | ~381k | 2.4x faster | 16 + | croner | ~31k | 29.1x faster | 17 + | cron-parser | ~33k | 27.4x faster | 18 18 19 19 ### Previous Execution Time 20 20 21 21 | Library | Avg ops/sec | vs cron-fast | 22 22 | ------------- | ----------- | ------------ | 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 | 23 + | **cron-fast** | ~991k | baseline | 24 + | cron-schedule | ~391k | 2.5x faster | 25 + | croner | ~30k | 32.6x faster | 26 + | cron-parser | ~38k | 26.4x faster | 27 27 28 28 ### Validation 29 29 30 30 | Library | Avg ops/sec | vs cron-fast | 31 31 | ------------- | ----------- | ------------ | 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 | 32 + | **cron-fast** | ~1899k | baseline | 33 + | cron-validate | ~656k | 2.9x faster | 34 + | cron-schedule | ~452k | 4.2x faster | 35 + | cron-parser | ~94k | 20.2x faster | 36 + | croner | ~33k | 57.9x faster | 37 37 38 38 ### Parsing 39 39 40 40 | Library | Avg ops/sec | vs cron-fast | 41 41 | ------------- | ----------- | ------------ | 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 | 42 + | **cron-fast** | ~1913k | baseline | 43 + | cron-validate | ~665k | 2.9x faster | 44 + | cron-schedule | ~447k | 4.3x faster | 45 + | cron-parser | ~95k | 20.2x faster | 46 + | croner | ~33k | 58.1x 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 | ~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 ✓ | 56 + | Every minute | ~1445k | ~178k ✓ | ~32k ✓ | ~32k ✓ | 57 + | Sparse: First of month | ~877k | ~509k ✓ | ~32k ✓ | ~19k ✓ | 58 + | Sparse: 31st (skips months) | ~813k | ~512k ✓ | ~30k ✓ | ~8k ✓ | 59 + | Step: Every 15 minutes | ~939k | ~270k ✓ | ~34k ✓ | ~56k ✓ | 60 + | Specific: 9 AM daily | ~1032k | ~360k ✓ | ~34k ✓ | ~43k ✓ | 61 + | OR-mode: 15th OR Monday | ~520k | ~515k | ~31k ✓ | ~34k ✓ | 62 + | Weekdays: Mon-Fri 9 AM | ~764k | ~323k ✓ | ~27k ✓ | ~41k ✓ | 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 | ~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 ✓ | 70 + | Every minute | ~1410k | ~181k ✓ | ~32k ✓ | ~34k ✓ | 71 + | Sparse: First of month | ~1012k | ~541k ✓ | ~29k ✓ | ~8k ✓ | 72 + | Sparse: 31st (skips months) | ~783k | ~479k ✓ | ~31k ✓ | ~8k ✓ | 73 + | Step: Every 15 minutes | ~868k | ~263k ✓ | ~31k ✓ | ~56k ✓ | 74 + | Specific: 9 AM daily | ~1043k | ~361k ✓ | ~30k ✓ | ~48k ✓ | 75 + | OR-mode: 15th OR Monday | ~979k | ~585k ✓ | ~31k ✓ | ~62k ✓ | 76 + | Weekdays: Mon-Fri 9 AM | ~844k | ~329k ✓ | ~29k ✓ | ~47k ✓ | 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 - | \* \* \* \* \* | ~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 | 84 + | \* \* \* \* \* | ~2187k | ~179k ✓ | ~44k ✓ | ~33k ✓ | ~593k ✓ | 85 + | 0 0 1 \* \* | ~2194k | ~601k ✓ | ~120k ✓ | ~32k ✓ | ~656k ✓ | 86 + | 0 12 31 \* \* | ~1754k | ~590k ✓ | ~117k ✓ | ~31k ✓ | ~650k ✓ | 87 + | _/15 _ \* \* \* | ~1632k | ~277k ✓ | ~66k ✓ | ~33k ✓ | ~696k ✓ | 88 + | 0 9 \* \* \* | ~2251k | ~374k ✓ | ~84k ✓ | ~33k ✓ | ~657k ✓ | 89 + | 0 9 15 \* 1 | ~1826k | ~748k ✓ | ~138k ✓ | ~33k ✓ | ~692k ✓ | 90 + | 0 9 \* \* 1-5 | ~1447k | ~396k ✓ | ~88k ✓ | ~33k ✓ | ~650k ✓ | 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 - | \* \* \* \* \* | ~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 ✓ | 98 + | \* \* \* \* \* | ~2220k | ~190k ✓ | ~45k ✓ | ~34k ✓ | ~657k ✓ | 99 + | 0 0 1 \* \* | ~2217k | ~615k ✓ | ~123k ✓ | ~34k ✓ | ~676k ✓ | 100 + | 0 12 31 \* \* | ~1841k | ~625k ✓ | ~121k ✓ | ~33k ✓ | ~664k ✓ | 101 + | _/15 _ \* \* \* | ~1629k | ~281k ✓ | ~66k ✓ | ~32k ✓ | ~717k ✓ | 102 + | 0 9 \* \* \* | ~2259k | ~333k ✓ | ~82k ✓ | ~31k ✓ | ~636k ✓ | 103 + | 0 9 15 \* 1 | ~1800k | ~713k ✓ | ~138k ✓ | ~33k ✓ | ~673k ✓ | 104 + | 0 9 \* \* 1-5 | ~1424k | ~375k ✓ | ~87k ✓ | ~33k ✓ | ~634k ✓ | 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.2.0", 3 + "version": "2.3.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.2.0", 3 + "version": "2.3.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",
+3 -3
src/parser.ts
··· 85 85 hour, 86 86 day, 87 87 month: month.map((m) => m - 1), 88 - weekday: weekdays.sort((a, b) => a - b), 88 + weekday: weekdays, 89 89 dayIsWildcard: dayStr.trim() === "*", 90 90 weekdayIsWildcard: weekdayStr.trim() === "*", 91 91 }; ··· 130 130 131 131 if (field === "*") { 132 132 for (let i = min; i <= max; i++) values.push(i); 133 - return values.slice().sort((a, b) => a - b); 133 + return values; 134 134 } 135 135 136 136 const parts = field.split(","); ··· 189 189 } 190 190 191 191 if (values.length === 0) return null; 192 - return [...new Set(values)].sort((a, b) => a - b); 192 + return values.sort((a, b) => a - b).filter((v, i, arr) => i === 0 || arr[i - 1] !== v); 193 193 } 194 194 195 195 /**