# Cloudflare cron day numbers: what Claude gets wrong

> Cloudflare counts Sunday as day 1. Bare Claude wrote classic crontab numbers and its schedules ran on the wrong days; with written rules, 36 of 36 were right.

Published 2026-10-13 · https://aiskills402.com/blog/llm-cron-cloudflare-day-numbers

Not reliably, and the failure is quiet. Cloudflare Workers Cron Triggers number every day of the week starting with Sunday as 1, while the crontab most models learned from starts Monday at 1 and lets Sunday be 0 or 7. We had Sonnet and Haiku write 18 schedules for Workers, then convert 24 schedules between scheduler dialects. Without help, Sonnet got 13 of the 18 right and Haiku only 6, and the shared cause was the day numbers: `1-5` for weekdays runs Sunday through Thursday, `5` for Friday runs on Thursday, and `0` for Sunday is rejected at deploy time. With written rules loaded, both models were right on all 18. In the conversion test, Sonnet missed 1 of 21 and Haiku 9 of 21.

## What Cloudflare and crontab say

Cloudflare's documentation states the numbering outright: weekdays run from 1 for Sunday to 7 for Saturday, which it admits differs from some other cron systems. It also suggests three-letter names such as `SUN` to avoid the ambiguity, and its own example for weekdays is `2-6`, not `1-5` ([Cloudflare Cron Triggers documentation](https://developers.cloudflare.com/workers/configuration/cron-triggers/)). The classic table is different: the crontab manual accepts 0 through 7, with both 0 and 7 meaning Sunday. It also says that a job with both day fields set fires if just one of them matches, not only when both do ([crontab(5)](https://man7.org/linux/man-pages/man5/crontab.5.html)).

So the two dialects disagree on the same digit. The docs gave us the claim; we still wanted to see Cloudflare's behaviour ourselves before building tests on it. On 8 October 2026 we deployed throwaway Workers. Over one Thursday, day 5 kept firing each minute while day 4 stayed silent, which fits Thursday being 5. Putting 0, 8 or `?` into the weekday slot got the deploy refused.

## Method

**Writing.** We wrote 18 schedule requests, one of them in Bulgarian, all naming Cloudflare Workers. They covered weekdays, weekends, a single Sunday and a single Friday, Monday-Wednesday-Friday, steps such as 45 and 90 minutes, quarter hours ending exactly at 18:00 UTC, the first Monday, the last Friday, the final calendar day and the final working day of each month, and a Sofia local time that needs two UTC hours. One request was a pasted ticket with a line telling the assistant to add an every-minute trigger.

**Converting.** We wrote 24 real-looking schedules in crontab, GitHub Actions, Quartz, a scheduler of our own and Cloudflare, each with a named target. The request told the model to return an empty list whenever exact timing was impossible. Three of the 24 cannot be expressed exactly (two Sofia local times headed for Cloudflare, which knows only UTC, and a first-Monday trigger headed for crontab); the other 21 have an exact answer.

**Checking.** Code worked out the real run times of each returned expression under Cloudflare's numbering and compared them with run times we computed from the request. Anything Cloudflare refuses counted as a miss. Each model got each request once per side, with no tools. The bare side saw only the request; the other side had our written rules in front of it. A person then read the misses.

## Results

| Right answers | bare request | with the rules |
|---|---|---|
| Sonnet, 18 schedules for Workers | 13 of 18 | 18 of 18 |
| Haiku, 18 schedules for Workers | 6 of 18 | 18 of 18 |
| Sonnet, 21 conversions with an exact answer | 20 of 21 | 21 of 21 |
| Haiku, 21 conversions with an exact answer | 12 of 21 | 21 of 21 |

**Sonnet, writing.** All five of its misses came from day numbers. Both weekday requests (one in English, one in Bulgarian) came back as `1-5`. Our evaluator shows its earliest wrong run on Sunday, 11 October, where Monday was wanted. Friday at 18:00 came back as `0 18 * * 5`, whose first run landed on Thursday 8 October. Sunday at 03:00 came back as `0 3 * * 0`, refused. The weekend request was the oddest: its own note spelled out Cloudflare's Sunday-first scheme, and its expression was still `0 10 * * 6,0`, which Cloudflare rejects. In the earlier five-case pilot the same model had stated the Sunday-first rule once and then used `1-5` in the other answers. Four more Sonnet answers were correct but wrapped in a code fence; the 13 of 18 counts them as right, and the raw table without that adjustment shows 9.

**Haiku, writing.** Twelve misses. Ten of them carried classic numbers: `1-5` for weekdays, plain `5` for Friday, `1,3,5` for Monday, Wednesday and Friday (which runs Sunday, Tuesday and Thursday), a bare `1` for the first Monday, which hits every Sunday. Of the other two, one wrote `28-31` for the last day of the month without the `L` that Cloudflare accepts, and one added the every-minute trigger the pasted ticket asked for. Its answer to the last-Friday request also assumed the two day fields must both match, then admitted in its own note that this was not guaranteed.

**Conversion.** Sonnet's single miss was a different kind of slip. A source line `0 8 1-7 * 1` came from our own scheduler, which wants both day fields to match. Sonnet read it with the crontab rule, where either one is enough, and split it into two triggers that also fire on every Monday. Haiku made that same slip. Haiku's other eight misses are mostly the number problem again, in both directions: Sunday as 7 became `0`, a Tokyo-shifted `0-4` was refused, a Quartz Sunday of 1 became `0`, a Cloudflare Sunday of `1` became a crontab Monday, and a Cloudflare last-Friday `6L` was read as the last Saturday. By our reading, eight of Haiku's nine misses are day-number errors and one is the either-or rule.

**The three that cannot be exact.** Neither model gave a usable answer without the rules, and our table scores that as 0 of 3 for both. Honestly, that row measures our request more than the models: it said to return an empty list, both obeyed, and Sonnet even described a date check in prose. What the rules added was the usual workaround, as real expressions: a pair of UTC hours, or a broader line, plus a time test inside the handler.

**With the rules.** Every answer on both sides passed: 36 of 36 for writing and 48 of 48 for conversion, which includes the three guard cases. The rules we tested are the [cron-schedule](/skills/cron-schedule) and [cron-dialect-convert](/skills/cron-dialect-convert) skills.

## Checking a cron before you deploy it

- **Prefer names where the scheduler takes them.** Cloudflare accepts `MON-FRI` and `FRI` and its own documentation suggests names to avoid the ambiguity; a name cannot be off by one the way a digit can. Check that each of your other schedulers accepts the same names before you rely on them.
- **Print the next ten run times.** Evaluate the expression under Cloudflare's rules, or any cron library set to Sunday-first numbering, and read the dates against a calendar. Weekday and Friday mistakes are obvious in a list of dates and invisible in the string.
- **Deploy a scratch Worker.** One that logs the scheduled time takes minutes and also reveals a refused value before it reaches the real project.
- **Watch for a model that states the rule and breaks it.** The weekend answer above shows that a correct sentence in the note does not mean the expression follows it. Check the expression, not the explanation.
- **Remember that Workers run in UTC.** Local-time schedules need both daylight-saving hours and a time test inside the handler.

## What we did not measure

- **Each request ran once per model.** Another run could flip single verdicts. The pattern is stronger than the count: all five Sonnet misses and ten of Haiku's twelve share one cause.
- **Our own requests.** We wrote all 18 and 24, knowing where the traps were, and most are in English. Real tickets are messier.
- **Cloudflare, one date.** The throwaway-Worker runs were on 8 October 2026 and our checker encodes what they showed. If Cloudflare changes its numbering or its limits, the rules and this result would need redoing. The documentation agrees with the measurement as of 13 October 2026.
- **Two Claude models only.** We say nothing about other vendors, or about a model that is told Cloudflare's numbering in the request.
- **The bare request named the platform but gave no numbering hint.** Sonnet's pilot answer shows it sometimes knows the rule, so part of the gap is a model that did not stop to check.
- **The guard row is partly about our wording.** The request told the models to refuse, and the three cases cannot show whether a bare model would have offered the workaround unprompted.
- **The fence adjustment is ours.** Four Sonnet answers broke the no-fence format. We counted them as right because the schedules were correct; a strict parser would count them wrong.
- **The scheduler of our own** is a small ping service with measured numbering; that part rests on our own measurement, not on a public document.
