{"id":13413240,"url":"https://github.com/adhocore/gronx","last_synced_at":"2025-05-14T10:10:53.961Z","repository":{"id":37752953,"uuid":"360052118","full_name":"adhocore/gronx","owner":"adhocore","description":"Lightweight, fast and dependency-free Cron expression parser (due checker, next/prev due date finder), task runner, job scheduler and/or daemon for Golang (tested on v1.13+) and standalone usage. If you are bold, use it to replace crontab entirely.","archived":false,"fork":false,"pushed_at":"2024-11-15T10:45:43.000Z","size":163,"stargazers_count":442,"open_issues_count":6,"forks_count":25,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-04-03T21:41:23.339Z","etag":null,"topics":["adhocore","cron","cron-expression","cron-expression-parser","cron-parser","cronjob","crontab","daemon","go","golang","job-manager","job-scheduler","parser","scheduler","task-manager","task-runner","task-scheduler"],"latest_commit_sha":null,"homepage":"https://github.com/adhocore/gronx","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adhocore.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"adhocore","custom":["https://paypal.me/ji10"]}},"created_at":"2021-04-21T06:14:03.000Z","updated_at":"2025-03-25T05:14:11.000Z","dependencies_parsed_at":"2024-06-18T13:51:10.916Z","dependency_job_id":"d58e7c56-049a-43cd-ad16-1e1655b648fc","html_url":"https://github.com/adhocore/gronx","commit_stats":{"total_commits":193,"total_committers":10,"mean_commits":19.3,"dds":"0.49740932642487046","last_synced_commit":"89f74f2441582c0e29a81e1ea286b119a336bb98"},"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adhocore%2Fgronx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adhocore%2Fgronx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adhocore%2Fgronx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adhocore%2Fgronx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adhocore","download_url":"https://codeload.github.com/adhocore/gronx/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248338026,"owners_count":21087150,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adhocore","cron","cron-expression","cron-expression-parser","cron-parser","cronjob","crontab","daemon","go","golang","job-manager","job-scheduler","parser","scheduler","task-manager","task-runner","task-scheduler"],"created_at":"2024-07-30T20:01:35.873Z","updated_at":"2025-04-11T03:36:37.465Z","avatar_url":"https://github.com/adhocore.png","language":"Go","funding_links":["https://github.com/sponsors/adhocore","https://paypal.me/ji10"],"categories":["Job Scheduler","Go","Relational Databases","作业调度器","Uncategorized"],"sub_categories":["Search and Analytic Databases","Advanced Console UIs","检索及分析资料库","Uncategorized"],"readme":"# adhocore/gronx\n\n[![Latest Version](https://img.shields.io/github/release/adhocore/gronx.svg?style=flat-square)](https://github.com/adhocore/gronx/releases)\n[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)\n[![Go Report](https://goreportcard.com/badge/github.com/adhocore/gronx)](https://goreportcard.com/report/github.com/adhocore/gronx)\n[![Test](https://github.com/adhocore/gronx/actions/workflows/test-action.yml/badge.svg)](https://github.com/adhocore/gronx/actions/workflows/test-action.yml)\n[![Lint](https://github.com/adhocore/gronx/actions/workflows/lint-action.yml/badge.svg)](https://github.com/adhocore/gronx/actions/workflows/lint-action.yml)\n[![Codecov](https://img.shields.io/codecov/c/github/adhocore/gronx/main.svg?style=flat-square)](https://codecov.io/gh/adhocore/gronx)\n[![Support](https://img.shields.io/static/v1?label=Support\u0026message=%E2%9D%A4\u0026logo=GitHub)](https://github.com/sponsors/adhocore)\n[![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Lightweight+fast+and+deps+free+cron+expression+parser+for+Golang\u0026url=https://github.com/adhocore/gronx\u0026hashtags=go,golang,parser,cron,cronexpr,cronparser)\n\n`gronx` is Golang [cron expression](#cron-expression) parser ported from [adhocore/cron-expr](https://github.com/adhocore/php-cron-expr) with task runner\nand daemon that supports crontab like task list file. Use it programatically in Golang or as standalone binary instead of crond. If that's not enough, you can use gronx to find the next (`NextTick()`) or previous (`PrevTick()`) run time of an expression from any arbitrary point of time.\n\n- Zero dependency.\n- Very **fast** because it bails early in case a segment doesn't match.\n- Built in crontab like daemon.\n- Supports time granularity of Seconds.\n\nFind gronx in [pkg.go.dev](https://pkg.go.dev/github.com/adhocore/gronx).\n\n## Installation\n\n```sh\ngo get -u github.com/adhocore/gronx\n```\n\n## Usage\n\n```go\nimport (\n\t\"time\"\n\n\t\"github.com/adhocore/gronx\"\n)\n\ngron := gronx.New()\nexpr := \"* * * * *\"\n\n// check if expr is even valid, returns bool\ngron.IsValid(expr) // true\n\n// check if expr is due for current time, returns bool and error\ngron.IsDue(expr) // true|false, nil\n\n// check if expr is due for given time\ngron.IsDue(expr, time.Date(2021, time.April, 1, 1, 1, 0, 0, time.UTC)) // true|false, nil\n```\n\n\u003e Validity can be checked without instantiation:\n\n```go\nimport \"github.com/adhocore/gronx\"\n\ngronx.IsValid(\"* * * * *\") // true\n```\n\n### Batch Due Check\n\nIf you have multiple cron expressions to check due on same reference time use `BatchDue()`:\n```go\ngron := gronx.New()\nexprs := []string{\"* * * * *\", \"0 */5 * * * *\"}\n\n// gives []gronx.Expr{} array, each item has Due flag and Err enountered.\ndues := gron.BatchDue(exprs)\n\nfor _, expr := range dues {\n    if expr.Err != nil {\n        // Handle err\n    } else if expr.Due {\n        // Handle due\n    }\n}\n\n// Or with given time\nref := time.Now()\ngron.BatchDue(exprs, ref)\n```\n\n### Next Tick\n\nTo find out when is the cron due next (in near future):\n```go\nallowCurrent = true // includes current time as well\nnextTime, err := gronx.NextTick(expr, allowCurrent) // gives time.Time, error\n\n// OR, next tick after certain reference time\nrefTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)\nallowCurrent = false // excludes the ref time\nnextTime, err := gronx.NextTickAfter(expr, refTime, allowCurrent) // gives time.Time, error\n```\n\n### Prev Tick\n\nTo find out when was the cron due previously (in near past):\n```go\nallowCurrent = true // includes current time as well\nprevTime, err := gronx.PrevTick(expr, allowCurrent) // gives time.Time, error\n\n// OR, prev tick before certain reference time\nrefTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)\nallowCurrent = false // excludes the ref time\nnextTime, err := gronx.PrevTickBefore(expr, refTime, allowCurrent) // gives time.Time, error\n```\n\n\u003e The working of `PrevTick*()` and `NextTick*()` are mostly the same except the direction.\n\u003e They differ in lookback or lookahead.\n\n### Standalone Daemon\n\nIn a more practical level, you would use this tool to manage and invoke jobs in app itself and not\nmess around with `crontab` for each and every new tasks/jobs.\n\nIn crontab just put one entry with `* * * * *` which points to your Go entry point that uses this tool.\nThen in that entry point you would invoke different tasks if the corresponding Cron expr is due.\nSimple map structure would work for this.\n\nCheck the section below for more sophisticated way of managing tasks automatically using `gronx` daemon called `tasker`.\n\n---\n### Go Tasker\n\nTasker is a task manager that can be programatically used in Golang applications. It runs as a daemon and invokes tasks scheduled with cron expression:\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"time\"\n\n\t\"github.com/adhocore/gronx/pkg/tasker\"\n)\n\nfunc main() {\n\ttaskr := tasker.New(tasker.Option{\n\t\tVerbose: true,\n\t\t// optional: defaults to local\n\t\tTz:      \"Asia/Bangkok\",\n\t\t// optional: defaults to stderr log stream\n\t\tOut:     \"/full/path/to/output-file\",\n\t})\n\n\t// add task to run every minute\n\ttaskr.Task(\"* * * * *\", func(ctx context.Context) (int, error) {\n\t\t// do something ...\n\n\t\t// then return exit code and error, for eg: if everything okay\n\t\treturn 0, nil\n\t}).Task(\"*/5 * * * *\", func(ctx context.Context) (int, error) { // every 5 minutes\n\t\t// you can also log the output to Out file as configured in Option above:\n\t\ttaskr.Log.Printf(\"done something in %d s\", 2)\n\n\t\treturn 0, nil\n\t})\n\n\t// run task without overlap, set concurrent flag to false:\n\tconcurrent := false\n\ttaskr.Task(\"* * * * * *\", , tasker.Taskify(\"sleep 2\", tasker.Option{}), concurrent)\n\n\t// every 10 minute with arbitrary command\n\ttaskr.Task(\"@10minutes\", taskr.Taskify(\"command --option val -- args\", tasker.Option{Shell: \"/bin/sh -c\"}))\n\n\t// ... add more tasks\n\n\t// optionally if you want tasker to stop after 2 hour, pass the duration with Until():\n\ttaskr.Until(2 * time.Hour)\n\n\t// finally run the tasker, it ticks sharply on every minute and runs all the tasks due on that time!\n\t// it exits gracefully when ctrl+c is received making sure pending tasks are completed.\n\ttaskr.Run()\n}\n```\n\n#### Concurrency\n\nBy default the tasks can run concurrently i.e if previous run is still not finished\nbut it is now due again, it will run again.\nIf you want to run only one instance of a task at a time, set concurrent flag to false:\n\n```go\ntaskr := tasker.New(tasker.Option{})\n\nconcurrent := false\nexpr, task := \"* * * * * *\", tasker.Taskify(\"php -r 'sleep(2);'\")\ntaskr.Task(expr, task, concurrent)\n```\n\n### Task Daemon\n\nIt can also be used as standalone task daemon instead of programmatic usage for Golang application.\n\nFirst, just install tasker command:\n```sh\ngo install github.com/adhocore/gronx/cmd/tasker@latest\n```\n\nOr you can also download latest prebuilt binary from [release](https://github.com/adhocore/gronx/releases/latest) for platform of your choice.\n\nThen prepare a taskfile ([example](./tests/../test/taskfile.txt)) in crontab format\n(or can even point to existing crontab).\n\u003e `user` is not supported: it is just cron expr followed by the command.\n\nFinally run the task daemon like so\n```\ntasker -file path/to/taskfile\n```\n\u003e You can pass more options to control the behavior of task daemon, see below.\n\n####  Tasker command options:\n\n```txt\n-file string \u003crequired\u003e\n    The task file in crontab format\n-out string\n    The fullpath to file where output from tasks are sent to\n-shell string\n    The shell to use for running tasks (default \"/usr/bin/bash\")\n-tz string\n    The timezone to use for tasks (default \"Local\")\n-until int\n    The timeout for task daemon in minutes\n-verbose\n    The verbose mode outputs as much as possible\n```\n\nExamples:\n```sh\ntasker -verbose -file path/to/taskfile -until 120 # run until next 120min (i.e 2hour) with all feedbacks echoed back\ntasker -verbose -file path/to/taskfile -out path/to/output # with all feedbacks echoed to the output file\ntasker -tz America/New_York -file path/to/taskfile -shell zsh # run all tasks using zsh shell based on NY timezone\n```\n\n\u003e File extension of taskfile for (`-file` option) does not matter: can be any or none.\n\u003e The directory for outfile (`-out` option) must exist, file is created by task daemon.\n\n\u003e Same timezone applies for all tasks currently and it might support overriding timezone per task in future release.\n\n#### Notes on Windows\n\nIn Windows if it doesn't find `bash.exe` or `git-bash.exe` it will use `powershell`.\n`powershell` may not be compatible with Unix flavored commands. Also to note:\nyou can't do chaining with `cmd1 \u0026\u0026 cmd2` but rather `cmd1 ; cmd2`.\n\n---\n### Cron Expression\n\nA complete cron expression consists of 7 segments viz:\n```\n\u003csecond\u003e \u003cminute\u003e \u003chour\u003e \u003cday\u003e \u003cmonth\u003e \u003cweekday\u003e \u003cyear\u003e\n```\n\nHowever only 5 will do and this is most commonly used. 5 segments are interpreted as:\n```\n\u003cminute\u003e \u003chour\u003e \u003cday\u003e \u003cmonth\u003e \u003cweekday\u003e\n```\nin which case a default value of 0 is prepended for `\u003csecond\u003e` position.\n\nIn a 6 segments expression, if 6th segment matches `\u003cyear\u003e` (i.e 4 digits at least) it will be interpreted as:\n```\n\u003cminute\u003e \u003chour\u003e \u003cday\u003e \u003cmonth\u003e \u003cweekday\u003e \u003cyear\u003e\n```\nand a default value of 0 is prepended for `\u003csecond\u003e` position.\n\nFor each segments you can have **multiple choices** separated by comma:\n\u003e Eg: `0 0,30 * * * *` means either 0th or 30th minute.\n\nTo specify **range of values** you can use dash:\n\u003e Eg: `0 10-15 * * * *` means 10th, 11th, 12th, 13th, 14th and 15th minute.\n\nTo specify **range of step** you can combine a dash and slash:\n\u003e Eg: `0 10-15/2 * * * *` means every 2 minutes between 10 and 15 i.e 10th, 12th and 14th minute.\n\nFor the `\u003cday\u003e` and `\u003cweekday\u003e` segment, there are additional [**modifiers**](#modifiers) (optional).\n\nAnd if you want, you can mix the multiple choices, ranges and steps in a single expression:\n\u003e `0 5,12-20/4,55 * * * *` matches if any one of `5` or `12-20/4` or `55` matches the minute.\n\n### Real Abbreviations\n\nYou can use real abbreviations (3 chars) for month and week days. eg: `JAN`, `dec`, `fri`, `SUN`\n\n### Tags\n\nFollowing tags are available and they are converted to real cron expressions before parsing:\n\n- *@yearly* or *@annually* - every year\n- *@monthly* - every month\n- *@daily* - every day\n- *@weekly* - every week\n- *@hourly* - every hour\n- *@5minutes* - every 5 minutes\n- *@10minutes* - every 10 minutes\n- *@15minutes* - every 15 minutes\n- *@30minutes* - every 30 minutes\n- *@always* - every minute\n- *@everysecond* - every second\n\n\u003e For BC reasons, `@always` still means every minute for now, in future release it may mean every seconds instead.\n\n```go\n// Use tags like so:\ngron.IsDue(\"@hourly\")\ngron.IsDue(\"@5minutes\")\n```\n\n### Modifiers\n\nFollowing modifiers supported\n\n- *Day of Month / 3rd of 5 segments / 4th of 6+ segments:*\n    - `L` stands for last day of month (eg: `L` could mean 29th for February in leap year)\n    - `W` stands for closest week day (eg: `10W` is closest week days (MON-FRI) to 10th date)\n- *Day of Week / 5th of 5 segments / 6th of 6+ segments:*\n    - `L` stands for last weekday of month (eg: `2L` is last tuesday)\n    - `#` stands for nth day of week in the month (eg: `1#2` is second monday)\n\n---\n## License\n\n\u003e \u0026copy; [MIT](./LICENSE) | 2021-2099, Jitendra Adhikari\n\n## Credits\n\nThis project is ported from [adhocore/cron-expr](https://github.com/adhocore/php-cron-expr) and\nrelease managed by [please](https://github.com/adhocore/please).\n\n---\n### Other projects\n\nMy other golang projects you might find interesting and useful:\n\n- [**urlsh**](https://github.com/adhocore/urlsh) - URL shortener and bookmarker service with UI, API, Cache, Hits Counter and forwarder using postgres and redis in backend, bulma in frontend; has [web](https://urlssh.xyz) and cli client\n- [**fast**](https://github.com/adhocore/fast) - Check your internet speed with ease and comfort right from the terminal\n- [**goic**](https://github.com/adhocore/goic) - Go Open ID Connect, is OpenID connect client library for Golang, supports the Authorization Code Flow of OpenID Connect specification.\n- [**chin**](https://github.com/adhocore/chin) - A Go lang command line tool to show a spinner as user waits for some long running jobs to finish.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadhocore%2Fgronx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadhocore%2Fgronx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadhocore%2Fgronx/lists"}