zcron

package
v0.0.0-alpha.14 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2025 License: Apache-2.0 Imports: 8 Imported by: 1

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrNilConfig indicates nil was given as config.
	ErrNilConfig = errors.New("ztime/zcron: nil config")
	// ErrNilJob indicates nil job function was given.
	ErrNilJob = errors.New("ztime/zcron: nil job function")
)

Functions

This section is empty.

Types

type Config

type Config struct {
	// Crontab is the cron expression.
	// See [Parse] for the syntax.
	Crontab string
	// MaxConcurrency is the maximum concurrency
	// of the currently running JobFunc.
	// Values should be 1, 2, ...
	// 1 means exactly 1 job at a time.
	// If less than 1, 1 is used.
	MaxConcurrency int
	// MaxRetry is the maximum number to retry
	// when the JobFunc returned non-nil error.
	// Values should be 0, 1, 2, ...
	// 0 means no retry. If less than 0, 0 is used.
	MaxRetry int
	// JobFunc is the job to run.
	JobFunc func(context.Context) error
	// WithContext provides a context which passed to the [Job.Run].
	// This is called once for a run and not called for retry.
	// [context.Background] is used when WithContext is nil.
	WithContext func() context.Context
	// EventHook is the function that hooks [Event]s.
	// The [Event] is notified through the first argument.
	// Additional information such as error is passed by a
	// depending on the event type.
	// 	- For OnJobFailed: error is given by a[0].
	// 	- For OnJobPanicked: recovered value given by a[0].
	EventHook func(e Event, a ...any)
}

Config is the configuration for the Cron.

type Cron

type Cron struct {
	// contains filtered or unexported fields
}

Cron schedules and runs jobs based on Crontab.

Example
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/aileron-projects/go/ztime/zcron"
)

func main() {
	now := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
	count := 0
	stop := make(chan struct{})

	job, _ := zcron.NewCron(&zcron.Config{
		Crontab: "* * * * * *",
		JobFunc: func(ctx context.Context) error {
			if count++; count > 3 {
				stop <- struct{}{}
				return nil
			}
			now = now.Add(time.Second)
			fmt.Println(now.Format(time.DateTime), "Hello Go!")
			return nil
		},
	})

	job.WithTimeFunc(func() time.Time { return now })
	go job.Start()
	<-stop

}
Output:

2000-01-01 00:00:01 Hello Go!
2000-01-01 00:00:02 Hello Go!
2000-01-01 00:00:03 Hello Go!

func NewCron

func NewCron(c *Config) (*Cron, error)

NewCron returns a new instance of Cron.

func (*Cron) Start

func (c *Cron) Start()

Start starts the cron. The cron scheduling can only run 1 process at a time. Calling Start multiple times does not run the multiple crons. If the next scheduling is after 10 minutes or more, the internal timer calibrates the time after 95% of the times. For examples, now a job is scheduled after 10 minutes, the con calibrates timer to fire the job after 9min30sec. It blocks the process. Run in a new goroutine if blocking is not necessary like below.

cron, _ := zcron.NewCron(.....)
go cron.Start()

func (*Cron) Stop

func (c *Cron) Stop()

Stop stops new scheduling of jobs. Stopping cron does not stop the already running jobs.

func (*Cron) WithTimeAfterFunc

func (c *Cron) WithTimeAfterFunc(timeAfter func(time.Duration) <-chan time.Time)

WithTimeAfterFunc replaces internal wait functions.

func (*Cron) WithTimeFunc

func (c *Cron) WithTimeFunc(timeNow func() time.Time)

WithTimeFunc replaces internal clock.

type Crontab

type Crontab struct {
	// contains filtered or unexported fields
}

Crontab is a cron scheduler.

Example
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go/ztime/zcron"
)

func main() {
	ct, err := zcron.Parse("59 59 23 31 * *") // Schedule at 31st at 23:59:59
	if err != nil {
		panic(err)
	}

	// Replace internal clock for testing.
	now := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
	ct.WithTimeFunc(func() time.Time { return now })

	for range 10 {
		fmt.Println(now.Format(time.DateTime), "|", ct.Next().Format(time.DateTime))
		now = now.Add(15 * 24 * time.Hour) // Forward 15 days.
	}

}
Output:

2000-01-01 00:00:00 | 2000-01-31 23:59:59
2000-01-16 00:00:00 | 2000-01-31 23:59:59
2000-01-31 00:00:00 | 2000-01-31 23:59:59
2000-02-15 00:00:00 | 2000-03-31 23:59:59
2000-03-01 00:00:00 | 2000-03-31 23:59:59
2000-03-16 00:00:00 | 2000-03-31 23:59:59
2000-03-31 00:00:00 | 2000-03-31 23:59:59
2000-04-15 00:00:00 | 2000-05-31 23:59:59
2000-04-30 00:00:00 | 2000-05-31 23:59:59
2000-05-15 00:00:00 | 2000-05-31 23:59:59
Example (EverySeconds)
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go/ztime/zcron"
)

func main() {
	ct, err := zcron.Parse("* * * * * *") // Every seconds.
	if err != nil {
		panic(err)
	}

	// Replace internal clock for testing.
	now := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
	ct.WithTimeFunc(func() time.Time { return now })

	for range 6 {
		fmt.Println(now.Format(time.DateTime), "|", ct.Next().Format(time.DateTime))
		now = now.Add(500 * time.Millisecond) // Forward 500ms.
	}
}
Output:

2000-01-01 00:00:00 | 2000-01-01 00:00:01
2000-01-01 00:00:00 | 2000-01-01 00:00:01
2000-01-01 00:00:01 | 2000-01-01 00:00:02
2000-01-01 00:00:01 | 2000-01-01 00:00:02
2000-01-01 00:00:02 | 2000-01-01 00:00:03
2000-01-01 00:00:02 | 2000-01-01 00:00:03

func Parse

func Parse(crontab string) (*Crontab, error)

Parse parses the given cron expression and returns Crontab. The syntax follows

TZ=UTC * * * * * *
|      | | | | | |
|      | | | | | |- Day of week
|      | | | | |--- Month
|      | | | |----- Day of month
|      | | |------- Hour
|      | |--------- Minute
|      |----------- Second (Optional)
|------------------ Timezone (Optional)

Field name   | Mandatory  | Values          | Special characters
----------   | ---------- | --------------  | -------------------
Timezone     | No         | Timezone name   |
Second       | No         | 0-59            | * / , -
Minute       | Yes        | 0-59            | * / , -
Hours        | Yes        | 0-23            | * / , -
Day of month | Yes        | 1-31            | * / , -
Month        | Yes        | 1-12 or JAN-DEC | * / , -
Day of week  | Yes        | 0-6 or SUN-SAT  | * / , -

Note that the "Day of month" and the "Day of week" are evaluated with AND condition.

Following aliases are defined for convenience.

Alias name   | Alias value | Usage example         |
----------   | ----------- | --------------------  |
CRON_TZ      | TZ          | CRON_TZ=UTC 0 0 * * * |
@monthly     | 0 0 1 * *   | TZ=UTC @monthly       |
@weekly      | 0 0 * * 0   | TZ=UTC @weekly        |
@daily       | 0 0 * * *   | TZ=UTC @daily         |
@hourly      | 0 * * * *   | TZ=UTC @hourly        |
@sunday      | 0 0 * * 0   | TZ=UTC @sunday        |
@monday      | 0 0 * * 1   | TZ=UTC @monday        |
@tuesday     | 0 0 * * 2   | TZ=UTC @tuesday       |
@wednesday   | 0 0 * * 3   | TZ=UTC @wednesday     |
@thursday    | 0 0 * * 4   | TZ=UTC @thursday      |
@friday      | 0 0 * * 5   | TZ=UTC @friday        |
@saturday    | 0 0 * * 6   | TZ=UTC @saturday      |

In addition, "@every <Duration>" expression can be used. Format of the duration must follow the time.ParseDuration.

Example of "@every" expression.

Duration  | Resolved Cron        | Notes                 |
--------- | -------------------- | --------------------- |
-1s       | ERROR                | Duration must be >0s  |
0s        | ERROR                | Duration must be >0s  |
1s        | */1 * * * * *        |                       |
1m        | 0 */1 * * * *        |                       |
1h        | 0 0 */1 * * *        |                       |
61s       | */1 */1 * * *        |                       |
15m30s    | */30 */15 * * * *    |                       |
65m30s    | */30 */5 */1 * * *   |                       |
1h30m     | 0 */30 */1 * * *     |                       |
23h59m59s | */59 */59 */23 * * * |                       |
24h       | ERROR                | Duration must be <24h |

See the references.

func (*Crontab) Next

func (c *Crontab) Next() time.Time

Next returns the next cron scheduled time. It internally calls Crontab.NextAfter with the current time returned from the internal clock. Use Crontab.WithTimeFunc when replacing the internal clock.

func (*Crontab) NextAfter

func (c *Crontab) NextAfter(t time.Time) time.Time

NexAfter returns the next cron scheduled time after t. Timezone of the returned time is the same as given t.

func (*Crontab) Now

func (c *Crontab) Now() time.Time

Now returns the current time returned from the internal clock. Use Crontab.WithTimeFunc to replace the internal clock.

func (*Crontab) WithTimeFunc

func (c *Crontab) WithTimeFunc(timeNow func() time.Time)

type Event

type Event int

Event is the event definition on cron. Following types of events are defined.

const (
	Undefined     Event = iota
	OnJobRun            // OnJobRun triggered when job is scheduled and run.
	OnJobAccepted       // OnJobAccepted triggered when job is accepted.
	OnJobDeclined       // OnJobAccepted triggered when job is declined (Max concurrency reached).
	OnJobStarted        // OnJobStarted triggered just before [Config.JobFunc] is called.
	OnJobExited         // OnJobExited triggered just after [Config.JobFunc] is returned.
	OnJobFailed         // OnJobFailed triggered when [Config.JobFunc] returned an error. This trigger is before [OnJobExited].
	OnJobPanicked       // OnJobPanicked triggered when [Config.JobFunc] panicked. This trigger is before [OnJobExited].
)

type ParseError

type ParseError struct {
	Err   error
	What  string
	Value string
}

ParseError reports cron parse error.

func (*ParseError) Error

func (e *ParseError) Error() string

func (*ParseError) Is

func (e *ParseError) Is(target error) bool

func (*ParseError) Unwrap

func (e *ParseError) Unwrap() error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL