• Stars
    star
    317
  • Rank 127,277 (Top 3 %)
  • Language
    Go
  • License
    MIT License
  • Created about 3 years ago
  • Updated 8 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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.

adhocore/gronx

Latest Version Software License Go Report Test Lint Codecov Support Tweet

gronx is Golang cron expression parser ported from adhocore/cron-expr with task runner and 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.

  • Zero dependency.
  • Very fast because it bails early in case a segment doesn't match.
  • Built in crontab like daemon.
  • Supports time granularity of Seconds.

Find gronx in pkg.go.dev.

Installation

go get -u github.com/adhocore/gronx

Usage

import (
	"time"

	"github.com/adhocore/gronx"
)

gron := gronx.New()
expr := "* * * * *"

// check if expr is even valid, returns bool
gron.IsValid(expr) // true

// check if expr is due for current time, returns bool and error
gron.IsDue(expr) // true|false, nil

// check if expr is due for given time
gron.IsDue(expr, time.Date(2021, time.April, 1, 1, 1, 0, 0, time.UTC)) // true|false, nil

Batch Due Check

If you have multiple cron expressions to check due on same reference time use BatchDue():

gron := gronx.New()
exprs := []string{"* * * * *", "0 */5 * * * *"}

// gives []gronx.Expr{} array, each item has Due flag and Err enountered.
dues := gron.BatchDue(exprs)

for _, expr := range dues {
    if expr.Err != nil {
        // Handle err
    } else if expr.Due {
        // Handle due
    }
}

// Or with given time
ref := time.Now()
gron.BatchDue(exprs, ref)

Next Tick

To find out when is the cron due next (in near future):

allowCurrent = true // includes current time as well
nextTime, err := gronx.NextTick(expr, allowCurrent) // gives time.Time, error

// OR, next tick after certain reference time
refTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)
allowCurrent = false // excludes the ref time
nextTime, err := gronx.NextTickAfter(expr, refTime, allowCurrent) // gives time.Time, error

Prev Tick

To find out when was the cron due previously (in near past):

allowCurrent = true // includes current time as well
prevTime, err := gronx.PrevTick(expr, allowCurrent) // gives time.Time, error

// OR, prev tick before certain reference time
refTime = time.Date(2022, time.November, 1, 1, 1, 0, 0, time.UTC)
allowCurrent = false // excludes the ref time
nextTime, err := gronx.PrevTickBefore(expr, refTime, allowCurrent) // gives time.Time, error

The working of PrevTick*() and NextTick*() are mostly the same except the direction. They differ in lookback or lookahead.

Standalone Daemon

In a more practical level, you would use this tool to manage and invoke jobs in app itself and not mess around with crontab for each and every new tasks/jobs.

In crontab just put one entry with * * * * * which points to your Go entry point that uses this tool. Then in that entry point you would invoke different tasks if the corresponding Cron expr is due. Simple map structure would work for this.

Check the section below for more sophisticated way of managing tasks automatically using gronx daemon called tasker.


Go Tasker

Tasker is a task manager that can be programatically used in Golang applications. It runs as a daemon and invokes tasks scheduled with cron expression:

package main

import (
	"context"
	"time"

	"github.com/adhocore/gronx/pkg/tasker"
)

func main() {
	taskr := tasker.New(tasker.Option{
		Verbose: true,
		// optional: defaults to local
		Tz:      "Asia/Bangkok",
		// optional: defaults to stderr log stream
		Out:     "/full/path/to/output-file",
	})

	// add task to run every minute
	taskr.Task("* * * * *", func(ctx context.Context) (int, error) {
		// do something ...

		// then return exit code and error, for eg: if everything okay
		return 0, nil
	}).Task("*/5 * * * *", func(ctx context.Context) (int, error) { // every 5 minutes
		// you can also log the output to Out file as configured in Option above:
		taskr.Log.Printf("done something in %d s", 2)

		return 0, nil
	})

	// run task without overlap, set concurrent flag to false:
	concurrent := false
	taskr.Task("* * * * * *", , tasker.Taskify("sleep 2", tasker.Option{}), concurrent)

	// every 10 minute with arbitrary command
	taskr.Task("@10minutes", taskr.Taskify("command --option val -- args", tasker.Option{Shell: "/bin/sh -c"}))

	// ... add more tasks

	// optionally if you want tasker to stop after 2 hour, pass the duration with Until():
	taskr.Until(2 * time.Hour)

	// finally run the tasker, it ticks sharply on every minute and runs all the tasks due on that time!
	// it exits gracefully when ctrl+c is received making sure pending tasks are completed.
	taskr.Run()
}

Concurrency

By default the tasks can run concurrently i.e if previous run is still not finished but it is now due again, it will run again. If you want to run only one instance of a task at a time, set concurrent flag to false:

taskr := tasker.New(tasker.Option{})

concurrent := false
expr, task := "* * * * * *", tasker.Taskify("php -r 'sleep(2);'")
taskr.Task(expr, task, concurrent)

Task Daemon

It can also be used as standalone task daemon instead of programmatic usage for Golang application.

First, just install tasker command:

go install github.com/adhocore/gronx/cmd/tasker@latest

Or you can also download latest prebuilt binary from release for platform of your choice.

Then prepare a taskfile (example) in crontab format (or can even point to existing crontab).

user is not supported: it is just cron expr followed by the command.

Finally run the task daemon like so

tasker -file path/to/taskfile

You can pass more options to control the behavior of task daemon, see below.

Tasker command options:

-file string <required>
    The task file in crontab format
-out string
    The fullpath to file where output from tasks are sent to
-shell string
    The shell to use for running tasks (default "/usr/bin/bash")
-tz string
    The timezone to use for tasks (default "Local")
-until int
    The timeout for task daemon in minutes
-verbose
    The verbose mode outputs as much as possible

Examples:

tasker -verbose -file path/to/taskfile -until 120 # run until next 120min (i.e 2hour) with all feedbacks echoed back
tasker -verbose -file path/to/taskfile -out path/to/output # with all feedbacks echoed to the output file
tasker -tz America/New_York -file path/to/taskfile -shell zsh # run all tasks using zsh shell based on NY timezone

File extension of taskfile for (-file option) does not matter: can be any or none. The directory for outfile (-out option) must exist, file is created by task daemon.

Same timezone applies for all tasks currently and it might support overriding timezone per task in future release.

Notes on Windows

In Windows if it doesn't find bash.exe or git-bash.exe it will use powershell. powershell may not be compatible with Unix flavored commands. Also to note: you can't do chaining with cmd1 && cmd2 but rather cmd1 ; cmd2.


Cron Expression

A complete cron expression consists of 7 segments viz:

<second> <minute> <hour> <day> <month> <weekday> <year>

However only 5 will do and this is most commonly used. 5 segments are interpreted as:

<minute> <hour> <day> <month> <weekday>

in which case a default value of 0 is prepended for <second> position.

In a 6 segments expression, if 6th segment matches <year> (i.e 4 digits at least) it will be interpreted as:

<minute> <hour> <day> <month> <weekday> <year>

and a default value of 0 is prepended for <second> position.

For each segments you can have multiple choices separated by comma:

Eg: 0 0,30 * * * * means either 0th or 30th minute.

To specify range of values you can use dash:

Eg: 0 10-15 * * * * means 10th, 11th, 12th, 13th, 14th and 15th minute.

To specify range of step you can combine a dash and slash:

Eg: 0 10-15/2 * * * * means every 2 minutes between 10 and 15 i.e 10th, 12th and 14th minute.

For the <day> and <weekday> segment, there are additional modifiers (optional).

And if you want, you can mix the multiple choices, ranges and steps in a single expression:

0 5,12-20/4,55 * * * * matches if any one of 5 or 12-20/4 or 55 matches the minute.

Real Abbreviations

You can use real abbreviations (3 chars) for month and week days. eg: JAN, dec, fri, SUN

Tags

Following tags are available and they are converted to real cron expressions before parsing:

  • @yearly or @annually - every year
  • @monthly - every month
  • @daily - every day
  • @weekly - every week
  • @hourly - every hour
  • @5minutes - every 5 minutes
  • @10minutes - every 10 minutes
  • @15minutes - every 15 minutes
  • @30minutes - every 30 minutes
  • @always - every minute
  • @everysecond - every second

For BC reasons, @always still means every minute for now, in future release it may mean every seconds instead.

// Use tags like so:
gron.IsDue("@hourly")
gron.IsDue("@5minutes")

Modifiers

Following modifiers supported

  • Day of Month / 3rd of 5 segments / 4th of 6+ segments:
    • L stands for last day of month (eg: L could mean 29th for February in leap year)
    • W stands for closest week day (eg: 10W is closest week days (MON-FRI) to 10th date)
  • Day of Week / 5th of 5 segments / 6th of 6+ segments:
    • L stands for last weekday of month (eg: 2L is last tuesday)
    • # stands for nth day of week in the month (eg: 1#2 is second monday)

License

Β© MIT | 2021-2099, Jitendra Adhikari

Credits

This project is ported from adhocore/cron-expr and release managed by please.


Other projects

My other golang projects you might find interesting and useful:

  • 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 and cli client
  • fast - Check your internet speed with ease and comfort right from the terminal
  • goic - Go Open ID Connect, is OpenID connect client library for Golang, supports the Authorization Code Flow of OpenID Connect specification.
  • chin - A Go lang command line tool to show a spinner as user waits for some long running jobs to finish.

More Repositories

1

php-cli

PHP Console Application made easy- build great console apps with ease. Comes with Zero Dependency and Autocompletion support. Think of it as a PHP cli application framework.
PHP
319
star
2

php-jwt

Ultra lightweight, dependency free and standalone JSON web token (JWT) library for PHP5.6 to PHP8.2. This library makes JWT a cheese. It is a minimal JWT integration for PHP.
PHP
271
star
3

urlsh

Golang URL shortener and bookmarker service with UI, API, Cache, Hits Counter and forwarder using postgres and redis in backend, bulma in frontend. Think of it as self hosting ready url shortener.
Go
144
star
4

docker-phpfpm

Lightweight (~100mb) Docker PHP FPM for both arm and amd arch on alpine 3.17 with PHP8.0.30/8.1.25/8.2.12 (also 7.4.33) with ~82-84 useful extensions (you can disable not necessary ones easily)
Dockerfile
96
star
5

phint

Interactively scaffolds and init new (or fixup old) PHP project/library with sane defaults using templates in no time
PHP
93
star
6

please

please is semver release made easy, detects current version from API or tags and next version from commits, creates detailed changelogs that are configurable.
Shell
86
star
7

fast

Check your internet speed/bandwidth right from your terminal. Built on Golang using chromedp
Go
81
star
8

php-cron-expr

Ultra lightweight, Dependency free and Super Fast Cron Expression parser for PHP
PHP
51
star
9

htmlup

Light and fast markdown parser, that parses markdown in a way human does
PHP
50
star
10

php-json-fixer

Fix truncated JSON data
PHP
45
star
11

php-underscore

PHP underscore inspired &/or cloned from _.js, with extra goodies like higher order messaging
PHP
44
star
12

phalcon-ext

Foundations, adapters, extensions, middlewares and utilities for Phalcon v4
PHP
42
star
13

tusc.sh

tus 1.0.0 client protocol implementation for bash. Resumable large file upload to Tus sever from terminal using bash script
Shell
32
star
14

chin

A Go lang library to show a spinner as user waits for any long running jobs to finish.
Go
31
star
15

goic

Golang OpenID Connect Client
Go
28
star
16

php-json-comment

Lightweight JSON comment and trailing comma stripper library for PHP with support for literal newlines and nested JSON strings.
PHP
27
star
17

php-env

A small and fast .env loader for PHP
PHP
23
star
18

with

With provides object like fluent interface for scalars and non-objects
PHP
17
star
19

jquery-plugins

jQuery plugins: BS calendar & datepicker, auto form filler, css grid float top, lazy & ajax pagination
JavaScript
16
star
20

crx-jtrans

jTransliter - the roman to unicode transliter as Google chrome extension
JavaScript
15
star
21

dsa

data structure and algorithm - examples and implementations
PHP
15
star
22

stiky

modular backbonejs application for sticky notes
JavaScript
14
star
23

jsonc

Golang (v1.13+) JSON5 preprocessor supporting comments, trailing comma, unquoted key/single-quoted string, hex number, trailing decimal point, literal newlines and more.
Go
14
star
24

twig-yall

Resource lazy loader extension for twig using malchata/yall.js
PHP
12
star
25

crx-loshin

loshin, the Load Shedding Indicator
JavaScript
12
star
26

php-cli-syntax

PHP Code Syntax Highlighter and/or exporter for CLI. Zero Dependency.
PHP
11
star
27

vue-inventory

Vue JS inventory app for hotel
HTML
11
star
28

php-env-bench

Benchmarking env loaders/parsers for PHP.
PHP
11
star
29

fsb

f...ing simple benchmark(ing)
PHP
10
star
30

get-in

Handy Traversal of chained objects with error trap and default value (suited for View)
PHP
9
star
31

sublime-phpcsfixer

A simple and minimal plugin for sublime editor to fix php files with php-cs-fixer via context menu
Python
9
star
32

php-cron-bench

Benchmarking cron parsers for PHP.
PHP
9
star
33

asserts

More PHPUnit assertions as a Trait
PHP
9
star
34

log-viewer

Simple log viewer app built with Lumen framework and VueJS
PHP
9
star
35

php-polyfills

Miscellaneous polyfills for older PHP versions
PHP
8
star
36

py-routes

Python recursive shortest path algo to find the optimal route between two points in terms of number of stops and duration
Python
8
star
37

win-cli-launcher

a command line launcher for windows os
Batchfile
8
star
38

php-tools

php tools for string, numbers, dates etc
PHP
7
star
39

gh-sc

Cancel all but the last SC automatically
JavaScript
7
star
40

plastic

PHP elasticsearch wrapper designed to be minimal, intuitive and dependency free
PHP
7
star
41

ci-captcha

Easy, hassle free, plug and play Code Igniter captcha library [OLD CODE]
PHP
7
star
42

live-console

PHP live console in browser
PHP
6
star
43

crx-adblock

chrome extension to hide/block custom ads
JavaScript
6
star
44

crx-joom

auto zoom the webpage for more readability and less eye stress
JavaScript
6
star
45

adhocore.github.io

adhocore.github.io
HTML
5
star
46

adhocore

5
star
47

php-adhocore

A PHP framework I created in early 2013 - inspired by CodeIgniter. Published here for historical reason. It was still a work in progress and was not complete- I am afraid it may as well never be. Check the initial commit.
PHP
5
star
48

leetcode

Just some leet code solutions in Python
Python
3
star
49

zshist

ZSH history manager: merge/deduplicate/normalize history entries for zsh, arrow suggestion UX is improved (plus save space, remove redundancy)
Go
2
star
50

opentelemetry

Temp mirror of old version of open-telemetry/opentelemetry
PHP
2
star
51

import-configuration-jms

Clone of techdivision/import-configuration-jms
PHP
2
star
52

docker-php-fpm-old-alpine

Mirror of official PHP docker images built on alpine 3.13/3/14 instead of 3.15/3.16
Dockerfile
2
star