Giter Site home page Giter Site logo

cron's People

Contributors

bgaifullin avatar bruth avatar cettoana avatar choleraehyq avatar cloudfly avatar daddye avatar daft-panda avatar dannyfeliz avatar dennisfrancis avatar elimisteve avatar icholy avatar ktogo avatar lukescott avatar mtojek avatar nnao45 avatar nono avatar patito avatar ricardolonga avatar robfig avatar soltysh avatar sparrc avatar tdterry avatar theothertomelliott avatar tilinna avatar vankofalcon avatar vayan avatar xiaoxiaosn avatar yvanoers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cron's Issues

missing 0th hour when called with TimeZone in cron.v2

This a bug with v2 branch

I tried to see if this is related to #55 , but its mentioned that the code is fixed. v2 diverged from master.

Is there a plan to merge v2 with master?

import "gopkg.in/robfig/cron.v2"
func main() {
	c := cron.New()
	c.Start()
	weekday := "0,1,2,3,4,5,6"
	hour := "0,1,2,3,4,5,6,7,8,9,10" // ,11,12,13,16,17,18,19,20,21,22,23 // hour does not work as expected
	tzName := "Asia/Kolkata"
	cron1 := fmt.Sprintf("TZ=%s 0 0 %s * * %s", tzName, hour, weekday)
	id, _ := c.AddFunc(cron1, func() { fmt.Println("Func to call") })
	entry := c.Entry(id)

	t := entry.Next
	fmt.Println(t)
	for i := 0; i < 24; i++ {
		t = entry.Schedule.Next(t)
		fmt.Println(t)
	}
}

Output is missing 00:00:00 from 9th Dec.
Current Date: Thu Dec 8 19:02:53 IST 2016

2016-12-09 01:00:00 +0530 IST
2016-12-09 02:00:00 +0530 IST
2016-12-09 03:00:00 +0530 IST
2016-12-09 04:00:00 +0530 IST
2016-12-09 05:00:00 +0530 IST
2016-12-09 06:00:00 +0530 IST
2016-12-09 07:00:00 +0530 IST
2016-12-09 08:00:00 +0530 IST
2016-12-09 09:00:00 +0530 IST
2016-12-09 10:00:00 +0530 IST
2016-12-10 00:00:00 +0530 IST
2016-12-10 01:00:00 +0530 IST
2016-12-10 02:00:00 +0530 IST
2016-12-10 03:00:00 +0530 IST
2016-12-10 04:00:00 +0530 IST
2016-12-10 05:00:00 +0530 IST
2016-12-10 06:00:00 +0530 IST
2016-12-10 07:00:00 +0530 IST
2016-12-10 08:00:00 +0530 IST
2016-12-10 09:00:00 +0530 IST
2016-12-10 10:00:00 +0530 IST
2016-12-11 00:00:00 +0530 IST
2016-12-11 01:00:00 +0530 IST
2016-12-11 02:00:00 +0530 IST
2016-12-11 03:00:00 +0530 IST

works fine without timezone set. master does not have code for TZ . Fails when set.

import 	"github.com/robfig/cron"
func main() {
	c := cron.New()
	c.Start()
	weekday := "0,1,2,3,4,5,6"
	hour := "0,1,2,3,4,5,6,7,8,9,10" // ,11,12,13,16,17,18,19,20,21,22,23 // hour does not work as expected
	// tzName := "Asia/Kolkata"
	cron1 := fmt.Sprintf("0 0 %s * * %s", hour, weekday)
	c.AddFunc(cron1, func() { fmt.Println("Func to call") })

	fmt.Println(len(c.Entries()))
	entry := c.Entries()[0]

	t := entry.Next
	fmt.Println(t)
	for i := 0; i < 24; i++ {
		t = entry.Schedule.Next(t)
		fmt.Println(t)
	}

}

Output:

2016-12-09 00:00:00 +0530 IST
2016-12-09 01:00:00 +0530 IST
2016-12-09 02:00:00 +0530 IST
2016-12-09 03:00:00 +0530 IST
2016-12-09 04:00:00 +0530 IST
2016-12-09 05:00:00 +0530 IST
2016-12-09 06:00:00 +0530 IST
2016-12-09 07:00:00 +0530 IST
2016-12-09 08:00:00 +0530 IST
2016-12-09 09:00:00 +0530 IST
2016-12-09 10:00:00 +0530 IST
2016-12-10 00:00:00 +0530 IST
2016-12-10 01:00:00 +0530 IST
2016-12-10 02:00:00 +0530 IST
2016-12-10 03:00:00 +0530 IST
2016-12-10 04:00:00 +0530 IST
2016-12-10 05:00:00 +0530 IST
2016-12-10 06:00:00 +0530 IST
2016-12-10 07:00:00 +0530 IST
2016-12-10 08:00:00 +0530 IST
2016-12-10 09:00:00 +0530 IST
2016-12-10 10:00:00 +0530 IST
2016-12-11 00:00:00 +0530 IST
2016-12-11 01:00:00 +0530 IST
2016-12-11 02:00:00 +0530 IST

Putting the clock forwards causes cron to catch-up and run multiple times

If you set a job to run @every 5s and start it works perfectly within my test program. However if you put the clock fowards an hour it will run approximately 720 times as the effective time will be playing catch-up with the current time (now). This can happen when laptops goes to sleep and then wake up. This is the issue I'm facing and my maintenance tasks to run multiple times on laptop resume.

By quickly taking a look I think the fix is to change line 161 from:

e.Next = e.Schedule.Next(effective)

To

e.Next = e.Schedule.Next(now)

I don't want to patch my code with this before you agree that this fix is correct. Please let me know.

Pass the JobID to the Job

cron.AddFunc()/cron.AddJob() return the ID of the added job.

When cron runs the job, it should pass Run() the ID of the job.

feature: from the end of the month

I know the CRON expressions are modeled after CRON, however, I have a basic need to schedule events at the end of the month. Some times the last day and some times the last 4 or 5 days. As of systemd 233 they added a '~' to the expression syntax to support exactly that. Any chance of getting that included?

Multiple jobs run in unexpected time interval

# go run hello.go 
2017/06/07 19:54:19 Start
2017/06/07 19:54:29 @every 1m30s
2017/06/07 19:54:39 @every 1m30s
2017/06/07 19:54:49 @every 1m30s
2017/06/07 19:54:59 @every 1m30s
2017/06/07 19:55:09 @every 1m30s
2017/06/07 19:55:19 @every 1m30s
2017/06/07 19:55:19 @every 1m30s
2017/06/07 19:55:19 @every 1m
2017/06/07 19:55:29 @every 1m30s
2017/06/07 19:55:39 @every 1m30s
2017/06/07 19:55:49 @every 1m30s
2017/06/07 19:55:49 @every 1m30s
2017/06/07 19:55:59 @every 1m30s
2017/06/07 19:56:09 @every 1m30s
2017/06/07 19:56:19 @every 1m30s
2017/06/07 19:56:19 @every 1m30s
2017/06/07 19:56:19 @every 1m
2017/06/07 19:56:29 @every 1m30s
2017/06/07 19:56:39 @every 1m30s
2017/06/07 19:56:49 @every 1m30s
2017/06/07 19:56:59 @every 1m30s
# cat hello.go
package main

import "log"
import "github.com/robfig/cron"
import "time"

func main() {
        c := cron.New()
        c.AddFunc("@every 1m", func() {
                log.Println("@every 1m")
        })
        for _, v := range []string{"@every 10s", "@every 1m", "@every 24h", "@every 1m30s"} {
                c.AddFunc(v, func() {
                        log.Println(v)
                })
        }
        c.Start()
        log.Println("Start")
        time.Sleep(600 * time.Second)
}

As the above result and code, when multiple jobs are added, the first 10s-interval job executes the last 1m30s-interval task.

"@every" is starting from program start

Hi,

We noticed a weird behaviour, or at least something worth documenting:

@every 12h is going to run every 12h, from the program start, so it's not equivalent to 1 run at midnight and 1 at noon (which we expected).

Thanks

v2 Release

Any way you could create a v2 release, so people using glide, etc can use semantic versioning in our projects?

Thanks!

Add start time

For example:Today is 2015-12-24 , I want 2015-12-28 after to execute once a day. How can I do it now?

Go process need to stand by? Or else scheduled job will not be running?

Just read through the docs, so this is not an exactly Unix/Linux crontab style cron?

After I start the job runner, the go process still need to stand by there forever, or else all the scheduled job will not be running? Not like handling over the control to os, then even the go program exit, os will still invoke the jobs as scheduled periodically?

Like:

func main() {
  c := cron.New()
  c.AddFunc("* * * * * *", func() { fmt.Println("?????????????????????????") })
  c.Start()
  time.Sleep(time.Second * 50)
}

Then go program exit, nothing is there....

Am I correct?

Non-idiomatic struct field naming convention.

This is a very minor issue.

Go allows struct fields to be named by their type alone:

type myStruct struct {
    int
}

func (m myStruct) getVal() int {
    return m.int
}

The code in this package often uses this convention, but names the fields explicitly:

type myStruct struct {
    int int
}

The idiomatic way would be to omit the name and simply list the type as shown in the first example. Again, it's a very minor concern.

A small bug when using c.AddFunc after c.Start

when using

c:=cron.New()
c.Start()
time.Sleep(5*time.Second)
c.AddFunc("* * * * * *",myfunc)

the myfunc is

func myfunc(){
fmt.Println(time.Unix(time.Now().Unix(), 0).Format("2006-01-02 15:04:05"))

the output is:(current time is 15:16:07)

2015-11-23 15:16:07  <- first in sleep 5
2015-11-23 15:16:07  <- 2nd in sleep 5
2015-11-23 15:16:07  <- 3rd in sleep 5
2015-11-23 15:16:07  <- 4th in sleep 5
2015-11-23 15:16:07  <- 5th in sleep 5
2015-11-23 15:16:07  <- truely the first second
2015-11-23 15:16:08  <- truely the second second
2015-11-23 15:16:09  <- truely the third second

However,modify cron.go from

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) Run() {
    // Figure out the next activation times for each entry.
    now := time.Now().Local()
    for _, entry := range c.entries {
        entry.Next = entry.Schedule.Next(now)
    }

    for {

        // Determine the next entry to run.
        sort.Sort(byTime(c.entries))

To

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) Run() {
    // Figure out the next activation times for each entry.
-   now := time.Now().Local()                                 <--------------assigned 'new' before the for loop
-   for _, entry := range c.entries {                      
-       entry.Next = entry.Schedule.Next(now)
-   }                                                                     

    for {
+       now := time.Now().Local()                     
+       for _, entry := range c.entries {              
+           entry.Next = entry.Schedule.Next(now) 
+       }                                                                      

        // Determine the next entry to run.
        sort.Sort(byTime(c.entries))

may solve this problem.

weekly cron seems to never run

Hey! I was wondering if there's an inherent issue with long-term jobs, I was running 0 10 * * 6 to run each Saturday with no luck. I'll try it again this week but I wanted to check to make sure it should work fine

thanks!

Using Asia/Kolkata(+0530) timezone cause schedule inaccuracy

Example Code:

func main() {
    schedule, _ := cron.Parse("0 0 12 * * ?")
    fmt.Printf("%s\n", time.Now())
    fmt.Printf("%s\n", schedule.Next(time.Now()))
}

Output:
2016-04-01 11:09:16.342426894 +0530 IST
2016-04-02 12:00:00 +0530 IST

After examination of spec.go, I think the problem was caused by usage of
time.Truncate in SpecSchedule's method Next, which should use time.Date
instead to round up hours/minutes/seconds.

Any other ideas?

Possible deadlock

It looks that there is a a deadlock somewhere.
I've used tried of using cron with a service manager, and the error is:

Mar 11 18:51:54 serve foo[2257]: panic: runtime error: invalid memory address or nil pointer dereference
Mar 11 18:51:54 serve foo[2257]: [signal 0xb code=0x1 addr=0x30 pc=0x4acd83]
Mar 11 18:51:54 serve foo[2257]: goroutine 1 [running]:
Mar 11 18:51:54 serve foo[2257]: panic(0x6bc260, 0xc82000a0b0)
Mar 11 18:51:54 serve foo[2257]:         /usr/local/lib/go/src/runtime/panic.go:464 +0x3ff
Mar 11 18:51:54 serve foo[2257]: github.com/robfig/cron.(*Cron).AddJob(0x0, 0x70b480, 0xa, 0x7f227577e528, 0x775390, 0x0, 0x0)
Mar 11 18:51:54 serve foo[2257]:         /usr/local/lib/go-3party/src/github.com/robfig/cron/cron.go:96 +0x1c3
Mar 11 18:51:54 serve foo[2257]: github.com/robfig/cron.(*Cron).AddFunc(0x0, 0x70b480, 0xa, 0x775390, 0x0, 0x0)
Mar 11 18:51:54 serve foo[2257]:         /usr/local/lib/go-3party/src/github.com/robfig/cron/cron.go:87 +0x8d
Mar 11 18:51:54 serve foo[2257]: main.Program.Start(0x7f227577e4c0, 0xc820062140, 0x0, 0xc82005a600, 0xc820056180, 0x7f227577e4c0, 0xc820062140, 0x0, 0x0)
Mar 11 18:51:54 serve foo[2257]:         /data/taka/code/go/src/indoo/cmd/foo/foo.go:68 +0xb5
Mar 11 18:51:54 serve foo[2257]: main.(*Program).Start(0xc82005c6c0, 0x7f227577e4c0, 0xc820062140, 0x0, 0x0)
Mar 11 18:51:54 serve foo[2257]:         <autogenerated>:1 +0xe0
Mar 11 18:51:54 serve foo[2257]: github.com/kardianos/service.(*systemd).Run(0xc820062140, 0x0, 0x0)
Mar 11 18:51:54 serve foo[2257]:         /usr/local/lib/go-3party/src/github.com/kardianos/service/service_systemd_linux.go:126 +0xc4
Mar 11 18:51:54 serve foo[2257]: main.main()
Mar 11 18:51:54 serve foo[2257]:         /data/taka/code/go/src/indoo/cmd/foo/foo.go:50 +0x55c

And the code is:

package main

import (
    "flag"
    "os"
    "time"

    log "github.com/Sirupsen/logrus"
    "github.com/kardianos/service"
    "github.com/robfig/cron"
)

var (
    FlagInstall   = flag.Bool("install", false, "install setups up the service in the OS")
    FlagUninstall = flag.Bool("uninstall", false, "uninstall removes the service from the OS")
)

func main() {
    flag.Parse()

    serviceConfig := &service.Config{
        Name:        "foo",
        DisplayName: "Foo daemon",
        Description: "tester of daemon",
        //UserName:    "ubuntu",
    }

    serv, err := service.New(Program{}, serviceConfig)
    if err != nil {
        log.Fatal(err)
    }
    serviceManager := service.ChosenSystem()

    if *FlagInstall {
        if !serviceManager.Detect() {
            log.Fatal("the service manager of this system is not available")
        }
        if err = serv.Install(); err != nil {
            log.Fatal(err)
        }
        os.Exit(0)
    } else if *FlagUninstall {
        if err = serv.Uninstall(); err != nil {
            log.Fatal(err)
        }
        os.Exit(0)
    }

    if err = serv.Run(); err != nil {
        log.Fatal(err)
    }
}

type Program struct {
    service service.Service
    cron_   *cron.Cron
}

func (p Program) Start(s service.Service) error {
    p.service = s
    p.cron_.AddFunc("@every 20s", func() {
        log.Print("++")
    })

    p.cron_.Start()
    go p.run()
    log.Info("Start")
    return nil
}

func (p Program) run() {
    //p.cron_.Start()

    log.Info("run")
}

func (p Program) Stop(s service.Service) error {
    p.cron_.Stop()
    log.Info("Stop")

    time.Sleep(10*time.Millisecond)
    return nil
}

Job run twice when timer triggers to early

I have had an instance where some jobs ran twice which looks to be cause by the timer triggering a few seconds to early.

The line (cron.go:201 on current master)
e.Next = e.Schedule.Next(now)
will cause a job to run again if the timer pops early.

In my case the jobs was scheduled a few days ahead - i don't have an explantation why the started a few seconds early.

I notice this was changed from:
e.Next = e.Schedule.Next(effectiveTime)
to prevent catch up effect in the machine sleeps.

However this new behaviour is a real problem for me.

I would suggest a check if when the timer is triggered: is now before the effective time - if so continue and let the timer be scheduled again. We could also mitigate by never setting timer for more than 1 hour so now is regularly reset?

However this could be an issue if it triggers before and keeps waking up to early?

Can I also ask about the v1 and v2 branches (i assume v1 is very old and should be ignored) v2 has extra features - but is missing some fixes from master....

I am going to submit a pull request

Flesh out README

It'd be helpful if the README at least had a subset of the examples in the godocs, as well as a pointer that the user should be using the gopkg.in paths.

trigger twice or thrice

In scheduling some job often repeated execution
Test expression:
0/2 * * * * ?
0 */2 * * * *

Cron Format not the same as Cron Spec

Out of all the libraries and sites I've seen everybody who uses the six * supports year instead of seconds.
For Example:
http://www.nncron.ru/help/EN/working/cron-format.htm

I personally think it would be better to follow what everyone else is doing to avoid confusion. It also lets it work with libraries that convert cron strings into human-readable format.

I don't think running a function every second is very good use case nor is running functions at specific second intervals. Might as well just use "sleep" or tickers. Years probably won't be that useful either but having a more compatible format would be nice.

Cron not doing anything

Hi @robfig,

I set a cron for some tasks but they weren't being executed. In order to understand I did this:

func main() {
c := cron.New()
c.AddFunc("@every 10s", func() { log.Println("Every sec") })
c.Start()
}

Go run this file and it doesn't print the message. What am I missing ?

Improper statement in doc about job concurrency

The doc statements, below, makes me feel ambiguous.

Callers may register Funcs to be invoked on a given schedule. Cron will run them in their own goroutines.

I think cron will run each cron job in different goroutines. I.e., cron will handle the concurrency about the jobs.
However, after reading the code in cron.go, i find that cron just call the registered job and handle them sequentially.
I think this statements may be more proper:

Callers may registers Funcs to be invoked on a given schedule.
Single cron instance will run registered Funcs sequentially.
Different cron instances will be run concurrently by cron package itself.

Monthly job executed every day

I had a strange behavior:

I have a monthly job for sending out emails running at 8:30 first day of every month.

    c := cron.New()
        ...
    c.AddFunc("15 8 1 * *", func() { EmailMonthlySummaries(db) })
    c.Start()

But the job executed every day for the three consecutive days. Any ideas what is going on?

There is no way to wait for completion of started Jobs after calling Stop()

When calling Stop() there may still be be Jobs whose Goroutines have been created but were never scheduled. It is therefore impossible to wait for all Jobs to complete, because some may not yet be running, making it impossible to track them.

The right place for adding to a sync.WaitGroup would be in Cron:run() right before go c.runWithRecovery(e.Job). You could then add a new Cron:Wait() function for waiting for jobs to finish after calling Stop().

The function multiple starts immediately after add into cron

I'm trying to add function into CRON to running every minute:
c.AddFunc("*/1 * * * *", func() { log.Println("Run every minute") })

If I do it immediately after cron object was created, then code works as expected - the function will be executed every minute, starting at the beginning of the next minute.
But if I will add the function in a minute after the current (or later - after 2, 3 and more minutes), it begins to execute immediately after you add into the cron.
In addition, it executed as many times as the minutes passed between the creation of the cron and function was added.
For example, if you try to run the following code:

package main

import (
        "log"
        "time"
        "gopkg.in/robfig/cron.v2"
)

func main() {
        c := cron.New()
        c.Start()
        log.Println("Start CRON.")
        time.Sleep(2 * time.Minute)
        log.Println("Add function into CRON.")
        c.AddFunc("*/1 * * * *", func() { log.Println("Run every minute") })
        time.Sleep(5 * time.Second)
        c.Stop()
}

So, the function will call exactly 2 times immediately after adding into cron, because it took 2 minutes between the start of the cron (12:31:36) and adding the function into it (12:33:36).

2017/05/29 12:31:36 Start CRON.
2017/05/29 12:33:36 Add function into CRON.
2017/05/29 12:33:36 Run every minute
2017/05/29 12:33:36 Run every minute

If between execute c.Start() and c.AddFunc took 3 minutes, then we would see in the logs three calls Run every minute.
In my opinion, the first time the function should be executed at the beginning of the next minute - 12:34:00 and repeat every minute.

Cron:Entries() may return nil when called concurrently

This is a theoretical issue I have not yet encountered in practice (but think could actually happen) that I came across while thinking about the source code, in particular these bits:

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil // A
		x := <-c.snapshot // B
		return x
	}
	return c.entrySnapshot()
}
		case <-c.snapshot: // C
			c.snapshot <- c.entrySnapshot() // D

Let's say Goroutine 1 is executing the run() loop containing C and D. Now Goroutine 2 executes Entries() until A, where it blocks. Goroutine 1 executes C, but before it gets a chance to execute D the scheduler decides to preempt it in favour of Goroutine 3, which executes Entries() until A, where it puts nil into the snapshot channel, leading Goroutine 2 to receive nil in B!

Since entrySnapshot() is a non-trivial function I feel like there's a good chance the Goroutine might get preempted during its execution.

The issue lies in using the same channel for communication in both directions. You could instead have a channel of channels over which you send a result channel created in Entries().

1-31/10 vs */10 dom

I would think 1-31/10 vs */10 would be the same, but the code handles this case differently:

master/spec.go

    if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
        return domMatch && dowMatch
    }
    return domMatch || dowMatch

Is there any doc somewhere that specifies why this is?

I'm asking because I added week year to my fork, and this is how I'm currently handling it:

wy-field/spec.go

    if s.Dom&starBit > 0 || s.Dow&starBit > 0 {
        return domMatch && dowMatch && woyMatch
    }
    return domMatch || (dowMatch && woyMatch)

I'm wondering if domMatch && dowMatch && woyMatch is correct in the starBit case.

@midnight doesn't seem to be working

I have 2 jobs one that runs @every 15m and another at @midnight. Midnight one didn't even start, I don't see anything wrong with my code and since first one is working I am bit confused. Should I use regular cron expression ?

Parse options & other features

We use the cron package at the company I work for, and we've made some modifications that were needed for our backend. I have four features separated into separate branches, and I'm wondering if you would be interested in merging these.

  1. Parse options https://github.com/webconnex/cron/tree/parser-options
    This adds Parser and NewParser with the ability to:

    • Select which fields are included. So you can exclude seconds, or any other field you don't want to use.
    • Set other options that might be useful, like making certain assumptions about the schedule.

    The Parse function now looks like this:

    var defaultParser = NewParser(
        Second | Minute | Hour | Dom | Month | DowOptinal | Descriptor,
    )
    
    // Parse returns a new crontab schedule representing the given spec.
    // It returns a descriptive error if the spec is not valid.
    //
    // It accepts
    //   - Full crontab specs, e.g. "* * * * * ?"
    //   - Descriptors, e.g. "@midnight", "@every 1h30m"
    func Parse(spec string) (_ Schedule, err error) {
        return defaultParser.Parse(spec)
    }

    This could be a resolution for #58. All you would need to do is remove Second and it would be spec compliant. But for those who need it, they can create a new parser with the option.

    I'm not sure about Dow being optional and being "spec complaint" - but there are two options for this - Dow and DowOptional which are interchangeable - one makes it required, the other allows you to omit it.

  2. Week year (built upon parser-options) https://github.com/webconnex/cron/tree/wy-field

    Adds another option after Dow that specifies the week of the year. This allows you to do bi-weekly schedules. It's used in conjunction with Dow.

  3. Year field (built upon wy-field + parser-options) https://github.com/webconnex/cron/tree/year-field

    Adds year field after week year. Range 2000 - 2099. Utilizes multi-bits. Can change to larger range, it just uses a larger slice (current size is 2).

  4. Approx Dom (built upon parser-options) https://github.com/webconnex/cron/tree/approx-date

    Adds a ~ symbol to be used at the beginning of a field (currently only supported for Dom) to specify approximate. If, for example, you say 31 and there are only 30 days in the target month, Next will choose the 30th. Or the 28th/29th in February.

    This is similar to L or L-1 in quartz, but differs from L-1 in that we don't want "the second to last day" we want a specific day in the month if it exists, but fall back to an earlier day if it doesn't. So L-1 would always land on the 30th, 28th (leap year Feb), or 27th (non-leap year Feb). ~30 would be the 30th, 29th (leap year Feb), and 28th (non-leap year Feb).

    An ApproxDay parser option was added to allow you to say "always assume approx date" without having to specify ~. In our case we always want this option in our system, while others may want to specify it in the schedule specifically.

The naming of the parser options could probably use an adjustment. And there may be some room for some better tests. I have added some for week year and approx dom. And the documentation would have to be updated. If interested, please let me know which areas need some improvement.

I also have plans to add L and # options from quartz - not sure how to approach those yet. Those options are probably further down pipe for me as the above features were to fill an immediate need for us.

I'm also planning on taking another stab at DST and not skipping / repeating. I had a PR for that a while back - and there was a comment about it being supported now - but the docs doesn't indicate that. I think I can also handle that in a less confusing way this time around :).

Stop a not running cron block program

Hello,
I think that when Stop() is called on a not running cron, it block the program.
Is there a when to know if cron is started ? running is private.

Panic if stop is called before start

This was obviously an error on my part, but discovered that calling Stop without calling Start will block since nothing is receiving c.stop <- struct{}{}. This is definitely a programmer error, I suggest panicking if the cron is not running.

// Stop the cron scheduler.
func (c *Cron) Stop() {
    if !c.running {
        panic("cron: not running")
    }
    c.stop <- struct{}{}
    c.running = false
}

The question mark logic seems implemented wrongly

The doc said that:

Question mark ( ? )

Question mark may be used instead of '*' for leaving either day-of-month or day-of-> week blank.

However, according to the code in cron, question mark and asterisk is equal.

    if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
        start = r.min
        end = r.max
        extra_star = starBit
        }

And ignoring the day of week will make it like adding asterisk as the value of day of week

License

What license is this code released under?

Version 2

Hello,
Is v2 still supported ?
Are recent fixes on master planned to be ported to v2 ?

Seconds field should be optional to conform to unix standard

The below is an excerpt from the crontab man page. I think that it would be useful to accept the same input as the crontab. You can always add a 0 for the seconds field if it isn't provided.

INPUT FILES
       In the POSIX locale, the user or application shall ensure that a crontab entry is a text file consisting of lines of six fields each. The fields shall be separated by <blank>s. The first five fields
       shall be integer patterns that specify the following:

        1. Minute [0,59]

        2. Hour [0,23]

        3. Day of the month [1,31]

        4. Month of the year [1,12]

        5. Day of the week ([0,6] with 0=Sunday)

       Each of these patterns can be either an asterisk (meaning all valid values), an element, or a list of elements separated by commas. An element shall be either a number or two numbers separated by  a
       hyphen (meaning an inclusive range). The specification of days can be made by two fields (day of the month and day of the week).  If month, day of month, and day of week are all asterisks, every day
       shall be matched. If either the month or day of month is specified as an element or list, but the day of week is an asterisk, the month and day of month fields shall specify the days that match.  If
       both month and day of month are specified as an asterisk, but day of week is an element or list, then only the specified days of the week match. Finally, if either the month or day of month is spec‐
       ified as an element or list, and the day of week is also specified as an element or list, then any day matching either the month and day of month, or the day of week, shall be matched.

       The sixth field of a line in a crontab entry is a string that shall be executed by sh at the specified times. A percent sign character in this field shall be translated to a <newline>. Any character
       preceded  by  a backslash (including the '%' ) shall cause that character to be treated literally. Only the first line (up to a '%' or end-of-line) of the command field shall be executed by the com‐
       mand interpreter. The other lines shall be made available to the command as standard input.

       Blank lines and those whose first non- <blank> is '#' shall be ignored.

       The text files /usr/lib/cron/cron.allow and /usr/lib/cron/cron.deny shall contain zero or more user names, one per line, of users who are, respectively, authorized or denied access  to  the  service
       underlying the crontab utility.

Question - sequencing

Is it possible to sequence a job to start after another job has finished ?
You never know how long the first job will take.
This might be an anti - pattern I know, because sequences and timed jobs are really opposites, but worth asking.

Thanks in advance for this library

Usage error in doc.go

// This:
c.AddFunc("0 5 * * * *",  func() { fmt.Println("Every 5 minutes") })

// Should be:
c.AddFunc("0 */5 * * * *",  func() { fmt.Println("Every 5 minutes") })
// OR
c.AddFunc("0 5 * * * *",  func() { fmt.Println("5th minute of every hour") })

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.