master
兔子 1 year ago
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…
Cancel
Save