robfig / cron Goto Github PK
View Code? Open in Web Editor NEWa cron library for go
License: MIT License
a cron library for go
License: MIT License
read config file
I would think 1-31/10
vs */10
would be the same, but the code handles this case differently:
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:
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.
Added a few test cases that fail for hourly schedules in Asia/Calcutta time zone (+0530):
I have a crontab job run every 10 minutes. I found that when it call at 2016/09/13 08:49:59
, it will call again at 2016/09/13 08:50:00
. It will then call twice.
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
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
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().
What license is this code released under?
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.
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.
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?
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
}
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?
In scheduling some job often repeated execution
Test expression:
0/2 * * * * ?
0 */2 * * * *
Wouldn't it be better if we can schedule a job every two or five year?
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.
Any way you could create a v2 release, so people using glide, etc can use semantic versioning in our projects?
Thanks!
It's a library, it shouldn't print anything but return errors when appropriate.
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?
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.
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.
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?
Scheduling every second or minute is too frequent for my use case. I want to put a floor to this?
It does not appear easy to do with this library, because none of the API seem to return the parse time in time data type.
Or if there is a way, how to do it?
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!
my code is here:
cron := cron.New()
cron.AddFunc("@midnight", job.DoSomeJob)
cron.Start()
Sometimes every line of code in job.DoSomeJob will be executed twice.
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.
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.
This might be the wrong venue and perhaps a dumb question, but what is the reason for calling Local()
on time.Now()
? I see it e.g. here: https://github.com/robfig/cron/blob/v2/cron.go#L164
Thanks!
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.
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.
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.
Parse options https://github.com/webconnex/cron/tree/parser-options
This adds Parser
and NewParser
with the ability to:
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.
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.
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).
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 :).
For example:Today is 2015-12-24 , I want 2015-12-28 after to execute once a day. How can I do it now?
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()
.
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
}
# 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.
// 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") })
If call twice
func (c *Cron) Start() {
c.running = true
go c.run()
}
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.
func (c *Cron) RemoveJob(identifier string) {
//remove job by identifier
}
i think add a method like this is useful
How to check whether cron is running or not?
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
Hi @robfig
As the title said, is there any way I can specify the initial execution time for a ConstantDelaySchedule, so that I can describe a job schedule as from 2017-03-23 23:55:00+8:00, run at every 30 minutes
?
Hello,
Is v2 still supported ?
Are recent fixes on master planned to be ported to v2 ?
hello ,
i want to run a job with specific
1 tuesday of every month
3 friday of every month
4 sunday of every month
thanks $ regards
I can run midnight job successfully on my local machine but when I deployed the app on AWS Elastic Beanstalk by using docker then the job was not triggered to run.
This is critical for me now, do you guys have any idea about this?
Thank you so much!
Can you please merge the entryId changes from V2 to Master ?
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
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.