Cron Expression Parser

Parse cron expressions, see next execution times, and get human-readable explanations. Perfect for developers working with scheduled tasks and automation.

Cron Expression Parser

Cron Field Reference

FieldRangeSpecial Characters
Minute
Minute of the hour
0-59* , - /
Hour
Hour of the day
0-23* , - /
Day of Month
Day of the month
1-31* , - / L W ?
Month
Month of the year
1-12 or JAN-DEC* , - /
Day of Week
Day of the week
0-7 or SUN-SAT* , - / L # ?

Common Examples

Every minute
* * * * *
Run every minute
Every hour
0 * * * *
Run at the start of every hour
Daily at midnight
0 0 * * *
Run daily at 12:00 AM
Daily at 9 AM
0 9 * * *
Run daily at 9:00 AM
Weekly on Sunday
0 0 * * 0
Run every Sunday at midnight
Monthly on 1st
0 0 1 * *
Run on the 1st day of every month
Weekdays only
0 9 * * 1-5
Run Monday to Friday at 9 AM
Every 15 minutes
*/15 * * * *
Run every 15 minutes
Every 6 hours
0 */6 * * *
Run every 6 hours
Twice daily
0 9,21 * * *
Run at 9 AM and 9 PM

Special Characters

* - Any value
, - Value list separator
- - Range of values
/ - Step values
? - No specific value
L - Last day of month/week

Examples:

  • */15 - Every 15 units
  • 0-5 - Range from 0 to 5
  • 1,15,30 - On the 1st, 15th, and 30th

What are Cron Expressions?

Cron expressions are strings that represent a schedule in Unix-like operating systems. They consist of five fields representing minute, hour, day of month, month, and day of week.

Originally developed for the Unix cron daemon, cron expressions are now used in many applications, cloud platforms, and task schedulers to define when automated tasks should run.

Common Use Cases

  • Database Backups: Schedule regular data backups
  • Report Generation: Automated daily/weekly reports
  • Data Processing: Batch processing jobs
  • System Maintenance: Cleanup and monitoring tasks
  • Cloud Functions: Serverless function scheduling

Cron Expression Examples

Basic Patterns

0 0 * * *Daily at midnight
0 */6 * * *Every 6 hours
*/15 * * * *Every 15 minutes
0 9 * * 1-59 AM on weekdays

Advanced Patterns

0 0 1 */3 *Quarterly (1st of every 3rd month)
0 9,17 * * *9 AM and 5 PM daily
0 2 * * 02 AM every Sunday
30 23 * * *11:30 PM daily

Field Format

┌───────── minute (0-59)
│ ┌─────── hour (0-23)
│ │ ┌───── day of month (1-31)
│ │ │ ┌─── month (1-12)
│ │ │ │ ┌─ day of week (0-6)
* * * * *

Special Characters

* - Any value
, - Value list
- - Range
/ - Step values
? - No specific value

Tips

• Sunday can be 0 or 7
• Use month names (JAN-DEC)
• Use day names (SUN-SAT)
• Test expressions before deployment
• Consider timezone differences

How Cron Expressions Work

The Structure and Syntax of Cron Expressions

Cron expressions consist of five or six fields separated by spaces: minute (0-59), hour (0-23), day of month (1-31), month (1-12), and day of week (0-6, where 0 is Sunday). Some systems add a sixth field for seconds. Each field accepts specific values, ranges, lists, steps, and wildcards. For example, "*/15 * * * *" means "every 15 minutes" where */15 is a step value in the minute field and * (wildcard) in all other fields.

The asterisk (*) means "any value" or "every." In the minute field, * means every minute. The comma creates value lists: 1,15,30 in the minute field triggers at minutes 1, 15, and 30. The hyphen creates ranges: 9-17 in the hour field means hours 9 through 17. The slash specifies steps: */10 means "every 10th value," and 5-20/3 means "every 3rd value between 5 and 20" (5, 8, 11, 14, 17, 20).

Day fields (day of month and day of week) have special interaction. When both are specified (neither is *), the condition is OR, not AND. The expression "0 0 13 * 5" triggers on the 13th of every month OR every Friday, not just Fridays that fall on the 13th. This surprising behavior catches many developers off guard. To trigger only when both conditions match requires more complex logic than a single cron expression can provide.

Some implementations support special strings as shortcuts: @yearly or @annually (0 0 1 1 *), @monthly (0 0 1 * *), @weekly (0 0 * * 0), @daily or @midnight (0 0 * * *), @hourly (0 * * * *), and @reboot (run at startup). These improve readability for common schedules. The ? character in some systems (like Quartz) means "no specific value" and is used in day fields to explicitly indicate the other day field should determine scheduling.

Parsing cron expressions requires careful handling of edge cases. February has 28 or 29 days, so "0 0 31 2 *" never triggers. "0 0 30 2 *" only triggers in years with 29 days. Month and day names (JAN-DEC, SUN-SAT) are case-insensitive and provide better readability than numbers. Implementations should validate expressions before accepting them—detecting impossible dates (like February 30) and warning users about expressions that may not trigger as intended.

Historical Origins and Evolution

Cron was created by Ken Thompson in 1975 for Version 7 Unix. The name comes from the Greek word "chronos" (time). Early cron only supported simple time specifications—individual values or asterisks. Users would write shell scripts that ran at fixed times to perform system maintenance like log rotation, backups, and cleanup. This automated tasks that previously required manual intervention at specific times.

Paul Vixie released Vixie Cron in 1987, which became the de facto standard Unix cron. It introduced ranges (1-5), lists (1,3,5), and steps (*/5), making expressions more flexible. Vixie Cron also added per-user crontabs, allowing regular users to schedule their own jobs, not just root. The crontab command (cron table) became the interface for editing scheduled jobs, with each line specifying a schedule and command.

The Quartz Scheduler (2001) extended cron syntax for Java applications, adding a seconds field and the ? character for day fields. This became popular in enterprise applications. Spring Framework adopted similar cron support. Cloud platforms introduced their variations: AWS CloudWatch Events uses a similar but slightly different syntax with year support. Google Cloud Scheduler uses App Engine Cron format with descriptive phrases like "every 5 minutes."

Modern interpretations have diverged. Some systems use 6-field cron (adding seconds), others stick to 5-field. Some support macros like @hourly, others don't. Some allow ? in day fields, others don't recognize it. This fragmentation means a cron expression working in one system might fail in another. Developers must consult documentation for their specific platform—there's no universal cron standard, just variations on the original Unix implementation.

Containerization and orchestration systems like Kubernetes introduced CronJobs, adapting the familiar cron syntax for cloud-native environments. These systems handle distribution, retries, and failure scenarios that traditional Unix cron couldn't address. The basic cron syntax remains, but the infrastructure ensures jobs run reliably across distributed systems, resuming after failures and preventing concurrent executions when jobs overlap.

Parsing Algorithms and Next Execution Calculation

Parsing a cron expression involves tokenizing each field and converting it to a set of valid values. For "*/15" in minutes, the parser generates the set {0, 15, 30, 45}. For "9-17" in hours, it generates {9, 10, 11, 12, 13, 14, 15, 16, 17}. For "1,15" it generates {1, 15}. The parser validates each value against the field's range—rejecting minute values > 59, hour values > 23, etc. This produces five sets of valid values for each field.

Calculating the next execution time starts with the current time plus one minute (cron runs at minute boundaries). The algorithm checks if the current minute matches the minute set. If not, advance to the next minute in the set, potentially wrapping to the next hour. Then check hours, advancing if needed. Then day of month OR day of week (remember the OR logic). Then month. If any field advances, reset all smaller fields to their minimum values and check again.

The algorithm must handle month boundaries carefully. When advancing from January 31 to February, day 31 doesn't exist. The algorithm skips to March 31 (or the first valid day in the day set). Leap years add complexity—February 29 exists only in leap years. The algorithm must check if the current year is a leap year when the day is 29 and month is February. This involves the leap year calculation: divisible by 4, except years divisible by 100, unless also divisible by 400.

Efficient implementations use bit sets or binary search. Instead of iterating through all possible values, store valid values in a bit array where bit position represents the value. Checking if minute 37 is valid becomes a O(1) bit check. Finding the next valid minute uses bit scanning operations (find first set bit after position). This optimization matters for high-frequency cron parsing in systems that evaluate thousands of cron expressions per second.

Timezone handling adds another layer of complexity. Cron traditionally runs in server local time. Cloud systems support timezone-specific cron: the same expression "0 9 * * *" triggers at different UTC times for different timezones. During daylight saving time transitions, jobs scheduled at 2:30 AM might skip (when clocks spring forward) or run twice (when clocks fall back). Some systems let users specify which behavior they want—skip, run once, or run twice during DST transitions.

Common Patterns and Real-World Usage

Database backups typically run during low-traffic hours. "0 2 * * *" (2 AM daily) is classic for full backups. "0 */4 * * *" (every 4 hours) for incremental backups. Monthly backups on the first day: "0 3 1 * *". These schedules avoid peak hours while ensuring backups complete before business hours resume. Production systems often stagger backups across instances: server1 at 2 AM, server2 at 2:30 AM, to avoid overwhelming storage systems.

Report generation follows business schedules. Weekly sales reports: "0 8 * * 1" (Monday 8 AM). Monthly financial reports: "0 9 1 * *" (first of month, 9 AM). Quarterly reports: "0 10 1 1,4,7,10 *" (Jan, Apr, Jul, Oct at 10 AM). End-of-business-day reports: "30 17 * * 1-5" (5:30 PM weekdays). These schedules align automated reporting with business processes, delivering information when stakeholders need it.

System maintenance windows use cron for cleanup tasks. Log rotation: "0 0 * * *" (daily at midnight). Temporary file cleanup: "0 3 * * 0" (Sunday 3 AM). Cache invalidation: "*/30 * * * *" (every 30 minutes). Database index rebuilding: "0 1 * * 0" (Sunday 1 AM). Certificate renewal checks: "0 0 1 * *" (first of each month). These tasks keep systems healthy without manual intervention.

API rate limit resets often follow cron schedules. "0 0 * * *" resets daily limits at midnight. "0 * * * *" resets hourly limits. Some APIs use more complex schedules: "0 0 * * 1" for weekly limits, "0 0 1 * *" for monthly limits. Understanding these schedules helps developers optimize API usage—bunch requests before reset times to maximize throughput within rate limits.

Monitoring and health checks run frequently. "*/5 * * * *" (every 5 minutes) checks service availability. "*/1 * * * *" (every minute) monitors critical services. "*/15 * * * *" checks SSL certificate expiry. These frequent checks enable rapid response to outages. The tradeoff: too frequent checks consume resources; too infrequent checks delay issue detection. Most production systems settle on 1-5 minute intervals for critical services, 15-30 minutes for less critical checks.

Debugging and Troubleshooting Cron Jobs

The most common cron issue is jobs not running as expected. First, verify the expression triggers when you think it should. Use a cron parser to calculate next execution times. Many developers think "0 0 1 1 *" runs "every Monday in January," but it actually runs "January 1st OR every Monday"—understanding the OR logic for day fields prevents this confusion. Testing expressions in a parser before deploying prevents surprises.

Environment variables cause subtle bugs. Cron jobs run with a minimal environment—PATH is usually just /usr/bin:/bin. Scripts that work in your shell fail in cron because they can't find commands. Solution: use absolute paths for commands (/usr/bin/python instead of python) or set PATH explicitly in the cron job. Similarly, HOME, USER, and other variables may differ from your interactive shell. Source environment files explicitly if needed.

Logging is essential for debugging. Redirect stdout and stderr to log files: command > /var/log/cronlog 2>&1. Without logging, failed cron jobs fail silently. Set up email notifications for cron failures—traditional cron emails output to the user. Cloud platforms offer logging services (CloudWatch Logs, Stackdriver) that capture cron job output automatically. Review logs regularly to catch issues before they impact services.

Timezone confusion causes jobs to run at unexpected times. A developer in California schedules "0 9 * * *" expecting 9 AM Pacific, but the server runs in UTC, so the job runs at 9 AM UTC (1 AM Pacific). Always document which timezone your cron expressions use. Cloud platforms let you specify timezone explicitly. When dealing with global users, consider whether the schedule should follow server time or user local time.

Overlapping executions cause problems when jobs run longer than their interval. A job scheduled every 5 minutes that sometimes takes 10 minutes results in multiple concurrent executions. Use file locks or distributed locks to prevent concurrent runs: check for a lock file at job start, create it, do work, remove it. Kubernetes CronJobs have a concurrency policy (Allow, Forbid, Replace) that handles this declaratively. Always consider maximum job runtime when setting cron intervals.

Modern Alternatives and Best Practices

Cron has limitations that modern alternatives address. Cron can't handle dependencies between jobs—if job B depends on job A, you must schedule B later and hope A completes in time. Workflow orchestration tools like Apache Airflow, Temporal, or Prefect let you define job dependencies explicitly. Job B waits for job A to complete, regardless of how long A takes. This eliminates fragile timing-based dependencies and makes complex workflows manageable.

Cloud-native schedulers provide features Unix cron lacks. AWS EventBridge handles retries automatically when jobs fail. Google Cloud Scheduler integrates with Cloud Functions and App Engine. Azure Logic Apps include scheduling with rich branching logic. These platforms manage infrastructure, scaling, monitoring, and alerting—features that require extensive custom implementation with traditional cron. The tradeoff is vendor lock-in and often higher cost than self-managed cron.

For simple periodic tasks, cron remains excellent. It's ubiquitous, well-understood, and requires no external dependencies. Running a cleanup script at 3 AM daily doesn't need a complex orchestration platform—cron handles it perfectly. Save the sophisticated tools for complex workflows with dependencies, failure handling, and coordination requirements. Don't over-engineer solutions to simple problems.

Testing cron jobs before production deployment is crucial. Run jobs manually first to ensure they work. Then schedule them in a development environment with more frequent intervals (every minute instead of daily) to verify triggers work correctly. Monitor the first few production runs closely. Document the cron schedule, what the job does, who owns it, and where logs are stored. This documentation saves hours when investigating issues months later.

Security considerations matter for cron jobs. Jobs run with user privileges—ensure cron users have minimum necessary permissions. Avoid running cron jobs as root when possible. Validate and sanitize any external input the job processes. Rotate credentials regularly. Audit cron configurations periodically—old forgotten cron jobs accumulate over time, creating security risks and wasting resources. Many security breaches start with forgotten, vulnerable cron jobs still running on production systems.

FAQ

Why does my cron expression with both day of month and day of week not work as expected?

When both day of month and day of week are specified (neither is *), cron uses OR logic, not AND. The expression "0 0 13 * 5" triggers on the 13th of every month OR every Friday, not just Fridays that fall on the 13th. To trigger only when both conditions match, you'd need to implement custom logic outside the cron expression itself, perhaps by having the cron job check the condition before executing.

What's the difference between */15 and 0,15,30,45 in cron?

They're functionally equivalent for the minute field—both trigger at minutes 0, 15, 30, and 45. */15 means "every 15th value" and is more concise. However, */15 is clearer about the intent (every 15 minutes) while 0,15,30,45 explicitly lists each trigger minute. Use */N for regular intervals and explicit lists for irregular schedules like 0,17,38 (which doesn't follow a simple pattern).

How do I schedule a cron job to run every weekday at 9 AM?

Use "0 9 * * 1-5" where 1-5 represents Monday through Friday. This triggers at 9:00 AM every weekday. Some systems also support "0 9 * * MON-FRI" with day names for better readability. Remember that cron uses 0 for Sunday and 6 for Saturday (though some systems accept 7 as Sunday too). Always test your expression with a cron parser to verify it triggers when you expect.

Why isn't my cron job running even though the expression seems correct?

Common issues: 1) The cron daemon isn't running (check with "systemctl status cron"), 2) Environment variables differ from your shell—use absolute paths for commands, 3) File permissions prevent execution, 4) Timezone differences cause unexpected timing, 5) Syntax errors in the crontab file. Check cron logs (/var/log/cron or /var/log/syslog) for error messages and add output redirection to your command to capture errors.