Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
3aa431985b | |||
3ec2007a7d | |||
557abef590 |
45
time_test.go
45
time_test.go
@ -219,7 +219,7 @@ func TestPrepareCron2(t *testing.T) {
|
|||||||
|
|
||||||
func TestRun(t *testing.T) {
|
func TestRun(t *testing.T) {
|
||||||
now := time.Now().Add(time.Second * 2)
|
now := time.Now().Add(time.Second * 2)
|
||||||
tmr := NewTimer(time.Now(), WithStaticDate(now), WithRunCountLimit(1))
|
tmr, _ := NewTimer(time.Now(), WithStaticDate(now), WithRunCountLimit(1))
|
||||||
c := make(chan int)
|
c := make(chan int)
|
||||||
tmr.AddTask(func() {
|
tmr.AddTask(func() {
|
||||||
fmt.Println("hello world")
|
fmt.Println("hello world")
|
||||||
@ -238,3 +238,46 @@ func TestRun(t *testing.T) {
|
|||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInvalid(t *testing.T) {
|
||||||
|
exp := `[{"repeat":[{"unit":3,"value":19},{"unit":2,"value":8},{"unit":1,"value":15},{"unit":0,"value":0}],"every":false}]`
|
||||||
|
tmr, _ := NewTimer(time.Now())
|
||||||
|
err := tmr.ImportRepeats(exp)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(tmr.NextTimer())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDate(t *testing.T) {
|
||||||
|
n := time.Now().Add(time.Minute)
|
||||||
|
var rp = Repeats{
|
||||||
|
Repeat: []Repeat{
|
||||||
|
{
|
||||||
|
Unit: STAR_HOUR,
|
||||||
|
Value: uint32(n.Hour()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Unit: STAR_MINUTE,
|
||||||
|
Value: uint32(n.Minute()),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Unit: STAR_SECOND,
|
||||||
|
Value: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Every: false,
|
||||||
|
}
|
||||||
|
tmr, _ := NewTimer(time.Now(), WithRepeats(&rp))
|
||||||
|
tmr.AddTask(func() {
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
fmt.Println(tmr.NextTimer())
|
||||||
|
})
|
||||||
|
err := tmr.Run()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(tmr.NextTimer())
|
||||||
|
fmt.Println(tmr.NextTimerAfterDate(time.Now().Add(time.Hour)))
|
||||||
|
time.Sleep(time.Hour)
|
||||||
|
}
|
||||||
|
130
timer.go
130
timer.go
@ -4,12 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewTimer(baseDate time.Time, opts ...TimerOptions) StarTimer {
|
func NewTimer(baseDate time.Time, opts ...TimerOptions) (StarTimer, error) {
|
||||||
timer := StarTimer{
|
timer := StarTimer{
|
||||||
base: baseDate,
|
base: baseDate,
|
||||||
nextDate: time.Time{},
|
nextDate: time.Time{},
|
||||||
@ -57,6 +58,9 @@ func NewTimer(baseDate time.Time, opts ...TimerOptions) StarTimer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if op.repeats != nil {
|
if op.repeats != nil {
|
||||||
|
if err := timer.repeatsCheck(op.repeats); err != nil {
|
||||||
|
return timer, err
|
||||||
|
}
|
||||||
timer.repeat = append(timer.repeat, op.repeats)
|
timer.repeat = append(timer.repeat, op.repeats)
|
||||||
}
|
}
|
||||||
if op.tasks != nil {
|
if op.tasks != nil {
|
||||||
@ -66,8 +70,43 @@ func NewTimer(baseDate time.Time, opts ...TimerOptions) StarTimer {
|
|||||||
timer.runLimit = op.runLimit
|
timer.runLimit = op.runLimit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return timer
|
return timer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *StarTimer) repeatsCheck(rp *Repeats) error {
|
||||||
|
if rp == nil {
|
||||||
|
return errors.New("nil point of Repeats")
|
||||||
|
}
|
||||||
|
if rp.Every {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, r := range rp.Repeat {
|
||||||
|
switch r.Unit {
|
||||||
|
case STAR_MINUTE, STAR_SECOND:
|
||||||
|
if r.Value > 60 || r.Value < 0 {
|
||||||
|
return fmt.Errorf("invalid value:%d", r.Value)
|
||||||
|
}
|
||||||
|
case STAR_HOUR:
|
||||||
|
if r.Value > 24 || r.Value < 0 {
|
||||||
|
return fmt.Errorf("invalid value:%d", r.Value)
|
||||||
|
}
|
||||||
|
case STAR_MONTH:
|
||||||
|
if r.Value > 12 || r.Value < 1 {
|
||||||
|
return fmt.Errorf("invalid value:%d", r.Value)
|
||||||
|
}
|
||||||
|
case STAR_DAY:
|
||||||
|
if r.Value > 31 || r.Value < 1 {
|
||||||
|
return fmt.Errorf("invalid value:%d", r.Value)
|
||||||
|
}
|
||||||
|
case STAR_WEEK:
|
||||||
|
if r.Value > 7 || r.Value < 0 {
|
||||||
|
return fmt.Errorf("invalid value:%d", r.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *StarTimer) IsRunning() bool {
|
func (t *StarTimer) IsRunning() bool {
|
||||||
t.mu.RLock()
|
t.mu.RLock()
|
||||||
defer t.mu.RUnlock()
|
defer t.mu.RUnlock()
|
||||||
@ -105,6 +144,9 @@ func (t *StarTimer) Stop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *StarTimer) NextTimer() time.Time {
|
func (t *StarTimer) NextTimer() time.Time {
|
||||||
|
if t.base.Before(time.Now()) {
|
||||||
|
return t.parseNextDate(time.Now(), true)
|
||||||
|
}
|
||||||
return t.parseNextDate(t.base, true)
|
return t.parseNextDate(t.base, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +170,7 @@ func (t *StarTimer) ImportRepeats(r string) error {
|
|||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
if t.running {
|
if t.running {
|
||||||
return errors.New("coonot import repeats to already running timer")
|
return errors.New("cannot import repeats to already running timer")
|
||||||
}
|
}
|
||||||
var rep []Repeats
|
var rep []Repeats
|
||||||
err := json.Unmarshal([]byte(r), &rep)
|
err := json.Unmarshal([]byte(r), &rep)
|
||||||
@ -136,6 +178,11 @@ func (t *StarTimer) ImportRepeats(r string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.repeat = make([]*Repeats, 0, len(rep))
|
t.repeat = make([]*Repeats, 0, len(rep))
|
||||||
|
for _, v := range rep {
|
||||||
|
if err = t.repeatsCheck(&v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, v := range rep {
|
for _, v := range rep {
|
||||||
t.repeat = append(t.repeat, &v)
|
t.repeat = append(t.repeat, &v)
|
||||||
}
|
}
|
||||||
@ -149,9 +196,21 @@ func (t *StarTimer) SetBaseDate(date time.Time) {
|
|||||||
t.base = date
|
t.base = date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *StarTimer) StaticMode() bool {
|
||||||
|
return t.staticMode
|
||||||
|
}
|
||||||
|
func (t *StarTimer) SetStaticMode(s bool) {
|
||||||
|
t.staticMode = s
|
||||||
|
}
|
||||||
|
|
||||||
func (t *StarTimer) ResetWithRepeat(base time.Time, repeat []*Repeats) error {
|
func (t *StarTimer) ResetWithRepeat(base time.Time, repeat []*Repeats) error {
|
||||||
t.Stop()
|
t.Stop()
|
||||||
t.base = base
|
t.base = base
|
||||||
|
for _, v := range repeat {
|
||||||
|
if err := t.repeatsCheck(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
t.repeat = repeat
|
t.repeat = repeat
|
||||||
return t.Run()
|
return t.Run()
|
||||||
}
|
}
|
||||||
@ -160,13 +219,74 @@ func (t *StarTimer) Repeats() []*Repeats {
|
|||||||
return t.repeat
|
return t.repeat
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *StarTimer) Run() error {
|
func (t *StarTimer) runAsStaticMode() error {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
if t.running {
|
if t.running {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t.nextDate = t.parseNextDate(time.Now(), false)
|
for _, v := range t.repeat {
|
||||||
|
if err := t.repeatsCheck(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.base = time.Now()
|
||||||
|
base := t.base
|
||||||
|
t.nextDate = t.parseNextDate(base, false)
|
||||||
|
if t.nextDate.Before(time.Now()) {
|
||||||
|
return errors.New("Invalid Timer Options,Please Check")
|
||||||
|
}
|
||||||
|
t.running = true
|
||||||
|
t.stopCtx, t.stopFn = context.WithCancel(context.Background())
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
if t.runLimit > 0 && t.runCount >= t.runLimit {
|
||||||
|
t.Stop()
|
||||||
|
}
|
||||||
|
now := time.Now()
|
||||||
|
t.nextDate = t.parseNextDate(now, false)
|
||||||
|
if t.nextDate.Before(now) || t.runLimit > 0 && t.runCount+1 >= t.runLimit {
|
||||||
|
t.Stop()
|
||||||
|
}
|
||||||
|
t.mu.Lock()
|
||||||
|
t.timer = time.NewTimer(t.nextDate.Sub(now))
|
||||||
|
t.mu.Unlock()
|
||||||
|
select {
|
||||||
|
case <-t.timer.C:
|
||||||
|
t.runCount++
|
||||||
|
for _, fn := range t.tasks {
|
||||||
|
go fn()
|
||||||
|
}
|
||||||
|
if !t.running {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-t.stopCtx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *StarTimer) Run() error {
|
||||||
|
if t.staticMode {
|
||||||
|
return t.runAsStaticMode()
|
||||||
|
}
|
||||||
|
t.mu.Lock()
|
||||||
|
defer t.mu.Unlock()
|
||||||
|
if t.running {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, v := range t.repeat {
|
||||||
|
if err := t.repeatsCheck(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
base := time.Now()
|
||||||
|
if t.base.After(base) {
|
||||||
|
base = t.base
|
||||||
|
}
|
||||||
|
t.nextDate = t.parseNextDate(base, false)
|
||||||
if t.nextDate.Before(time.Now()) {
|
if t.nextDate.Before(time.Now()) {
|
||||||
return errors.New("Invalid Timer Options,Please Check")
|
return errors.New("Invalid Timer Options,Please Check")
|
||||||
}
|
}
|
||||||
|
2
typed.go
2
typed.go
@ -41,6 +41,8 @@ type StarTimer struct {
|
|||||||
runLimit int
|
runLimit int
|
||||||
repeat []*Repeats
|
repeat []*Repeats
|
||||||
tasks []func()
|
tasks []func()
|
||||||
|
//staticMode 意味永久以当前时间为基准进行动作,例如crontab
|
||||||
|
staticMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimerOptions func(option *TimerOption)
|
type TimerOptions func(option *TimerOption)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user