init
commit
441b552380
@ -0,0 +1,55 @@
|
||||
package startimer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTimer(t *testing.T) {
|
||||
tk:= StarTimer{
|
||||
base: time.Now(),
|
||||
repeat: []*Repeats{
|
||||
{
|
||||
Every: false,
|
||||
Repeat: []Repeat{
|
||||
{
|
||||
Unit: STAR_MINUTE,
|
||||
Value: 7,
|
||||
},
|
||||
{
|
||||
Unit: STAR_HOUR,
|
||||
Value: 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Every: false,
|
||||
Repeat: []Repeat{
|
||||
{
|
||||
Unit: STAR_MINUTE,
|
||||
Value: 15,
|
||||
},
|
||||
{
|
||||
Unit: STAR_HOUR,
|
||||
Value: 14,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Every: true,
|
||||
Repeat: []Repeat{
|
||||
{
|
||||
Unit: STAR_HOUR,
|
||||
Value: 5,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
base:=tk.base
|
||||
for i:=0;i<10;i++{
|
||||
base=tk.parseNextDate(base)
|
||||
fmt.Println(base)
|
||||
}
|
||||
}
|
@ -0,0 +1,300 @@
|
||||
package startimer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewTimer(baseDate time.Time, opts ...TimerOptions) StarTimer {
|
||||
timer := StarTimer{
|
||||
base: baseDate,
|
||||
nextDate: time.Time{},
|
||||
mu: new(sync.RWMutex),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
var op TimerOption
|
||||
opt(&op)
|
||||
switch op.idx {
|
||||
case 3:
|
||||
timer.repeat = append(timer.repeat, &Repeats{
|
||||
Repeat: []Repeat{
|
||||
{
|
||||
Unit: STAR_YEAR,
|
||||
Value: uint32(op.date.Year()),
|
||||
},
|
||||
{
|
||||
Unit: STAR_MONTH,
|
||||
Value: uint32(op.date.Month()),
|
||||
},
|
||||
{
|
||||
Unit: STAR_DAY,
|
||||
Value: uint32(op.date.Day()),
|
||||
},
|
||||
{
|
||||
Unit: STAR_HOUR,
|
||||
Value: uint32(op.date.Hour()),
|
||||
},
|
||||
{
|
||||
Unit: STAR_MINUTE,
|
||||
Value: uint32(op.date.Minute()),
|
||||
},
|
||||
{
|
||||
Unit: STAR_SECOND,
|
||||
Value: uint32(op.date.Second()),
|
||||
},
|
||||
},
|
||||
Every: false,
|
||||
})
|
||||
case 4:
|
||||
timer.repeat = append(timer.repeat, &Repeats{
|
||||
Repeat: []Repeat{op.repeat},
|
||||
Every: true,
|
||||
})
|
||||
}
|
||||
|
||||
if op.repeats != nil {
|
||||
timer.repeat = append(timer.repeat, op.repeats)
|
||||
}
|
||||
if op.tasks != nil {
|
||||
timer.tasks = append(timer.tasks, op.tasks)
|
||||
}
|
||||
}
|
||||
return timer
|
||||
}
|
||||
func (t *StarTimer) IsRunning() bool {
|
||||
t.mu.RLock()
|
||||
defer t.mu.RUnlock()
|
||||
return t.running
|
||||
}
|
||||
|
||||
func (t *StarTimer) AddTask(task func()) {
|
||||
t.tasks = append(t.tasks, task)
|
||||
}
|
||||
|
||||
func (t *StarTimer) SetTasks(tasks []func()) {
|
||||
t.tasks = tasks
|
||||
}
|
||||
|
||||
func (t *StarTimer) Stop() error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if !t.running {
|
||||
return nil
|
||||
}
|
||||
t.running = false
|
||||
t.stopFn()
|
||||
if t.timer != nil {
|
||||
t.timer.Stop()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *StarTimer) NextTimer() time.Time {
|
||||
return t.nextDate
|
||||
}
|
||||
|
||||
func (t *StarTimer) BaseDate() time.Time {
|
||||
return t.base
|
||||
}
|
||||
|
||||
func (t *StarTimer) ResetWithRepeat(base time.Time, repeat []*Repeats) error {
|
||||
t.Stop()
|
||||
t.base = base
|
||||
t.repeat = repeat
|
||||
return t.Run()
|
||||
}
|
||||
|
||||
func (t *StarTimer) Run() error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.running {
|
||||
return nil
|
||||
}
|
||||
t.nextDate = t.parseNextDate(t.base)
|
||||
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 {
|
||||
now := time.Now()
|
||||
t.mu.Lock()
|
||||
t.timer = time.NewTimer(t.nextDate.Sub(now))
|
||||
t.mu.Unlock()
|
||||
select {
|
||||
case <-t.timer.C:
|
||||
t.nextDate = t.parseNextDate(t.nextDate)
|
||||
if t.nextDate.Before(now) {
|
||||
t.Stop()
|
||||
}
|
||||
for _, fn := range t.tasks {
|
||||
go fn()
|
||||
}
|
||||
if !t.running {
|
||||
return
|
||||
}
|
||||
case <-t.stopCtx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *StarTimer) parseNextDate(base time.Time) time.Time {
|
||||
if len(t.repeat) == 0 {
|
||||
return time.Time{}
|
||||
}
|
||||
var dates []time.Time
|
||||
for _, d := range t.repeat {
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
if d.Every {
|
||||
dates = append(dates, t.parseEveryNextDate(base, d)...)
|
||||
} else {
|
||||
dates = append(dates, t.parseStaticNextDate(base, d))
|
||||
}
|
||||
}
|
||||
sort.SliceStable(dates, func(i, j int) bool {
|
||||
return dates[i].UnixNano() < dates[j].UnixNano()
|
||||
})
|
||||
if len(dates) == 0 {
|
||||
return time.Time{}
|
||||
}
|
||||
now := time.Now().UnixNano()
|
||||
for _, v := range dates {
|
||||
if v.UnixNano() > now {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
func (t *StarTimer) parseStaticNextDate(base time.Time, r *Repeats) time.Time {
|
||||
target := base
|
||||
if !r.Every { //固定日期
|
||||
for _, d := range r.Repeat {
|
||||
switch d.Unit {
|
||||
case STAR_SECOND:
|
||||
sub := int(d.Value) - target.Second()
|
||||
if sub < 0 {
|
||||
sub += 60
|
||||
}
|
||||
target = target.Add(time.Second * time.Duration(sub))
|
||||
case STAR_MINUTE:
|
||||
sub := int(d.Value) - target.Minute()
|
||||
if sub < 0 {
|
||||
sub += 60
|
||||
}
|
||||
target = target.Add(time.Minute * time.Duration(sub))
|
||||
case STAR_HOUR:
|
||||
sub := int(d.Value) - target.Hour()
|
||||
if sub < 0 {
|
||||
sub += 24
|
||||
}
|
||||
target = target.Add(time.Hour * time.Duration(sub))
|
||||
case STAR_DAY:
|
||||
sub := int(d.Value) - target.Day()
|
||||
if sub >= 0 {
|
||||
target = target.Add(time.Hour * 24 * time.Duration(sub))
|
||||
continue
|
||||
}
|
||||
target = time.Date(target.Year(), target.Month()+1, int(d.Value), target.Hour(), target.Minute(), target.Second(), 0, target.Location())
|
||||
case STAR_MONTH:
|
||||
sub := int(d.Value) - int(target.Month())
|
||||
if sub < 0 {
|
||||
sub += 12
|
||||
}
|
||||
target = target.AddDate(0, sub, 0)
|
||||
case STAR_YEAR:
|
||||
sub := int(d.Value) - int(target.Year())
|
||||
if sub < 0 {
|
||||
return time.Date(0, 0, 0, 0, 0, 0, 0, nil)
|
||||
}
|
||||
target = target.AddDate(sub, 0, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
if target == base {
|
||||
return time.Time{}
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
func (t *StarTimer) parseEveryNextDate(target time.Time, r *Repeats) []time.Time {
|
||||
var res []time.Time
|
||||
if r.Every { //定期日期
|
||||
for idx, d := range r.Repeat {
|
||||
if d.baseDate.Unix() == -62135596800 {
|
||||
d.baseDate = t.base
|
||||
r.Repeat[idx] = d
|
||||
}
|
||||
if d.baseDate.After(target) {
|
||||
res = append(res, d.baseDate)
|
||||
continue
|
||||
}
|
||||
switch d.Unit {
|
||||
case STAR_SECOND:
|
||||
for {
|
||||
d.baseDate = d.baseDate.Add(time.Second * time.Duration(int(d.Value)))
|
||||
if d.baseDate.After(target) {
|
||||
r.Repeat[idx] = d
|
||||
break
|
||||
}
|
||||
}
|
||||
res = append(res, d.baseDate)
|
||||
case STAR_MINUTE:
|
||||
for {
|
||||
d.baseDate = d.baseDate.Add(time.Minute * time.Duration(int(d.Value)))
|
||||
if d.baseDate.After(target) {
|
||||
r.Repeat[idx] = d
|
||||
break
|
||||
}
|
||||
}
|
||||
res = append(res, d.baseDate)
|
||||
case STAR_HOUR:
|
||||
for {
|
||||
d.baseDate = d.baseDate.Add(time.Hour * time.Duration(int(d.Value)))
|
||||
if d.baseDate.After(target) {
|
||||
r.Repeat[idx] = d
|
||||
break
|
||||
}
|
||||
}
|
||||
res = append(res, d.baseDate)
|
||||
case STAR_DAY:
|
||||
for {
|
||||
d.baseDate = d.baseDate.Add(time.Hour * 24 * time.Duration(int(d.Value)))
|
||||
if d.baseDate.After(target) {
|
||||
r.Repeat[idx] = d
|
||||
break
|
||||
}
|
||||
}
|
||||
res = append(res, d.baseDate)
|
||||
case STAR_MONTH:
|
||||
for {
|
||||
d.baseDate = d.baseDate.AddDate(0, int(d.Value), 0)
|
||||
if d.baseDate.After(target) {
|
||||
r.Repeat[idx] = d
|
||||
break
|
||||
}
|
||||
}
|
||||
res = append(res, d.baseDate)
|
||||
case STAR_YEAR:
|
||||
for {
|
||||
d.baseDate = d.baseDate.AddDate(int(d.Value), 0, 0)
|
||||
if d.baseDate.After(target) {
|
||||
r.Repeat[idx] = d
|
||||
break
|
||||
}
|
||||
}
|
||||
res = append(res, d.baseDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package startimer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Unit uint8
|
||||
|
||||
const (
|
||||
STAR_SECOND Unit = iota
|
||||
STAR_MINUTE
|
||||
STAR_HOUR
|
||||
STAR_DAY
|
||||
STAR_MONTH
|
||||
STAR_YEAR
|
||||
)
|
||||
|
||||
type Repeats struct {
|
||||
Repeat []Repeat
|
||||
Every bool // false=static true=every
|
||||
}
|
||||
|
||||
type Repeat struct {
|
||||
Unit Unit
|
||||
baseDate time.Time
|
||||
Value uint32
|
||||
}
|
||||
|
||||
type StarTimer struct {
|
||||
base time.Time
|
||||
nextDate time.Time
|
||||
timer *time.Timer
|
||||
stopFn context.CancelFunc
|
||||
stopCtx context.Context
|
||||
mu *sync.RWMutex
|
||||
running bool
|
||||
repeat []*Repeats
|
||||
tasks []func()
|
||||
}
|
||||
|
||||
type TimerOptions func(option *TimerOption)
|
||||
|
||||
type TimerOption struct {
|
||||
idx uint8
|
||||
repeats *Repeats
|
||||
tasks func()
|
||||
date time.Time
|
||||
repeat Repeat
|
||||
}
|
||||
|
||||
func WithRepeats(r *Repeats) TimerOptions {
|
||||
return func(option *TimerOption) {
|
||||
option.idx = 1
|
||||
option.repeats = r
|
||||
}
|
||||
}
|
||||
|
||||
func WithRepeat(r Repeat) TimerOptions {
|
||||
return func(option *TimerOption) {
|
||||
option.idx = 4
|
||||
option.repeat = r
|
||||
}
|
||||
}
|
||||
|
||||
func WithTask(t func()) TimerOptions {
|
||||
return func(option *TimerOption) {
|
||||
option.idx = 2
|
||||
option.tasks = t
|
||||
}
|
||||
}
|
||||
|
||||
func WithStaticDate(t time.Time) TimerOptions {
|
||||
return func(option *TimerOption) {
|
||||
option.idx = 3
|
||||
option.date = t
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue