diff --git a/crontab.go b/crontab.go index 810a7cd..3bbdfc1 100644 --- a/crontab.go +++ b/crontab.go @@ -1 +1,149 @@ package curl2info + +import ( + "errors" + "fmt" + "regexp" + "strconv" + "strings" + + "github.com/davecgh/go-spew/spew" +) + +type timePointer struct { + left, right int + leftlimit, rightlimit int + per int + isAll bool +} + +func (tp *timePointer) String() string { + return fmt.Sprintf("left: %d, right: %d, leftlimit: %d, rightlimit: %d, per: %d", tp.left, tp.right, tp.leftlimit, tp.rightlimit, tp.per) +} + +// Crontab 的string解析 +type Crontab struct { + Min []timePointer + Hour []timePointer + Day []timePointer + Month []timePointer + Week []timePointer +} + +// NewCrontab create 一个crontab +func NewCrontab(crontab string) *Crontab { + cron := &Crontab{} + cron.FromString(crontab) + return cron +} + +func (cron *Crontab) String() string { + return fmt.Sprintf("min:%s\nhour:%s\nday:%s\nmonth:%s\nweek:%s\n", spew.Sdump(cron.Min), spew.Sdump(cron.Hour), spew.Sdump(cron.Day), spew.Sdump(cron.Month), spew.Sdump(cron.Week)) +} + +// FromString 解析crontab 的 表达式 +func (cron *Crontab) FromString(crontab string) error { + crontab = strings.TrimSpace(crontab) + + matches := regexp.MustCompile("[^ ]+").FindAllString(crontab, -1) + mlen := len(matches) + switch mlen { + case 5: + cron.Min = createTimePointer(matches[0], 0, 59, true) + cron.Hour = createTimePointer(matches[1], 0, 23, true) + cron.Day = createTimePointer(matches[2], 1, 31, false) + cron.Month = createTimePointer(matches[3], 1, 12, true) + cron.Week = createTimePointer(matches[4], 1, 7, true) + default: + return errors.New("mathches len != want, check crontab string") + } + return nil +} + +func createTimePointer(min string, llimit, rlimit int, fixedLeftRight bool) []timePointer { + + var result []timePointer + + exelist := strings.Split(min, ",") + for _, exe := range exelist { + tp := timePointer{} + + takeper := strings.Split(exe, "/") // per + var rangevalue, per string + if len(takeper) == 1 { + rangevalue = exe + per = "1" + } else { + rangevalue = takeper[0] + per = takeper[1] + } + // takeRange + be := strings.Split(rangevalue, "-") + var left, rigth string + switch len(be) { + case 1: + left = be[0] + rigth = be[0] + case 2: + left = be[0] + rigth = be[1] + default: + panic(errors.New("range value is > 2")) + } + + if left == "*" { + tp.left = llimit + } else { + ileft, err := strconv.Atoi(strings.Replace(left, "^", "-", -1)) + if err != nil { + panic(err) + } + tp.left = ileft + } + + if rigth == "*" { + tp.right = rlimit + } else { + iright, err := strconv.Atoi(strings.Replace(rigth, "^", "-", -1)) + if err != nil { + panic(err) + } + tp.right = iright + } + + iper, err := strconv.Atoi(per) + if err != nil { + panic(err) + } + + tp.per = iper + tp.leftlimit = llimit + tp.rightlimit = rlimit + + // 修正左值 + leftfixed := tp.left + if leftfixed < 0 { + leftfixed += tp.rightlimit + 1 + if fixedLeftRight { + tp.left = leftfixed + } + } + + rightfixed := tp.right + if rightfixed < 0 { + rightfixed += tp.rightlimit + 1 + if fixedLeftRight { + tp.right = rightfixed + } + } + + // 全部符合 当左等于左 且 右等于右最大 并且 per == 1 TODO: 如果加入时间间隔 就需要 加多一个 附加值 + if leftfixed == tp.leftlimit && rightfixed == tp.rightlimit && tp.per == 1 { + tp.isAll = true + } + + result = append(result, tp) + } + + return result +} diff --git a/crontab_test.go b/crontab_test.go index af044a7..ab08a4c 100644 --- a/crontab_test.go +++ b/crontab_test.go @@ -1,244 +1,52 @@ package curl2info import ( - "errors" "fmt" - "regexp" + "log" "runtime" - "strconv" - "strings" "testing" "time" - - "github.com/davecgh/go-spew/spew" ) // type LRValue struct { // left, right int // } -type TimePointer struct { - left, right int - leftlimit, rightlimit int - per int - isAll bool -} - -func (tp *TimePointer) String() string { - return fmt.Sprintf("left: %d, right: %d, leftlimit: %d, rightlimit: %d, per: %d", tp.left, tp.right, tp.leftlimit, tp.rightlimit, tp.per) -} - -type Crontab struct { - Min []TimePointer - Hour []TimePointer - Day []TimePointer - Month []TimePointer - Week []TimePointer - - logMissedTime bool - plans []time.Time -} - -func NewCrontab(crontab string) *Crontab { - cron := &Crontab{} - cron.FromString(crontab) - return cron -} - -func (cron *Crontab) SetMissedLog(missedlog bool) { - cron.logMissedTime = missedlog -} - -func countDays(year int, month int) (days int) { - if month != 2 { - if month == 4 || month == 6 || month == 9 || month == 11 { - days = 30 - - } else { - days = 31 - } - } else { - if ((year%4) == 0 && (year%100) != 0) || (year%400) == 0 { - days = 29 - } else { - days = 28 - } - } - return days -} - -func DayAccordWith(day int, now *time.Time, lm *TimePointer) { - // maxday := countDays(now.Year(), int(now.Month())) - - // left := lm.left - // if left < 0 { - // left = maxday + left - // } - - // right := lm.right - // if right < 0 { - // right = maxday + right - // } - - // if lm.left <= lm.right { - // switch { - // case day < lm.left: - // return &LRValue{lm.left, lm.right}, nil - // case day > lm.right: - // return nil, nil - // case lm.left <= day && day <= lm.right: - // return &LRValue{day, lm.right}, nil - // } - // } - - // return nil, fmt.Errorf("day left > right? left: %d, right: %d", lm.left, lm.right) -} - -func MonthAccordWith(month int, lm *TimePointer) { - - // if lm.left <= lm.right { - // switch { - // case month < lm.left: - // return &LRValue{lm.left, lm.right}, nil - // case month > lm.right: - // return nil, nil - // case lm.left <= month && month <= lm.right: - // return &LRValue{month, lm.right}, nil - // } - // } - - //maxday := countDays(now.Year(), month) - - // 1-12月 - // left := lm.left - // if left < 0 { - // left = maxday + left - // } - - // right := lm.right - // if right < 0 { - // right = maxday + right - // } - - // if left <= right { - // switch { - // case day < lm.left: - // return &LRValue{lm.left, lm.right}, nil - // case month > lm.right: - // return nil, nil - // case lm.left <= month && month <= lm.right: - // return &LRValue{month, lm.right}, nil - // } - // } - -} - -func (cron *Crontab) TimeUp() bool { - - return false -} - -func (cron *Crontab) String() string { - return fmt.Sprintf("min:%s\nhour:%s\nday:%s\nmonth:%s\nweek:%s\n", spew.Sdump(cron.Min), spew.Sdump(cron.Hour), spew.Sdump(cron.Day), spew.Sdump(cron.Month), spew.Sdump(cron.Week)) -} - -func (cron *Crontab) FromString(crontab string) error { - crontab = strings.TrimSpace(crontab) - - matches := regexp.MustCompile("[^ ]+").FindAllString(crontab, -1) - mlen := len(matches) - switch mlen { - case 5: - cron.Min = createTimePointer(matches[0], 0, 59) - cron.Hour = createTimePointer(matches[1], 0, 23) - cron.Day = createTimePointer(matches[2], 1, 31) - cron.Month = createTimePointer(matches[3], 1, 12) - cron.Week = createTimePointer(matches[4], 1, 7) - default: - return errors.New("mathches len != 5, check crontab string") - } - return nil -} - -func createTimePointer(min string, llimit, rlimit int) []TimePointer { - - var result []TimePointer - - exelist := strings.Split(min, ",") - for _, exe := range exelist { - tp := TimePointer{} - - takeper := strings.Split(exe, "/") // per - var rangevalue, per string - if len(takeper) == 1 { - rangevalue = exe - per = "0" - } else { - rangevalue = takeper[0] - per = takeper[1] - } - // takeRange - be := strings.Split(rangevalue, "-") - var left, rigth string - switch len(be) { - case 1: - left = be[0] - rigth = be[0] - case 2: - left = be[0] - rigth = be[1] - default: - panic(errors.New("range value is > 2")) - } - - if left == "*" { - tp.left = llimit - tp.isAll = true - } else { - ileft, err := strconv.Atoi(strings.Replace(left, "^", "-", -1)) - if err != nil { - panic(err) - } - tp.left = ileft - } - - if rigth == "*" { - tp.right = rlimit - tp.isAll = true - } else { - iright, err := strconv.Atoi(strings.Replace(rigth, "^", "-", -1)) - if err != nil { - panic(err) - } - tp.right = iright - } - - iper, err := strconv.Atoi(per) - if err != nil { - panic(err) - } - tp.per = iper - tp.leftlimit = llimit - tp.rightlimit = rlimit - - result = append(result, tp) - } - - return result -} - func TestParseCrontab(t *testing.T) { - crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围 - - t.Error(NewCrontab(crontab)) + // crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围 + crontab := "* * * * *" + t.Error("") + // t.Error(NewCrontab(crontab)) PrintMemUsage() - ty := CreateTrieYear(2018) + log.Println(time.Now().Hour()) - spew.Dump(ty) + ty := NewTrieYear(2018) + ty.InsertCrontab(NewCrontab(crontab)) + log.Println(len(ty.AllPlanTime(time.Now()))) + + // for { + + // i := ty.TimeUp() + // if i != nil { + // if i.GetStatus() { + // spew.Dump(i) + // log.Println(time.Now()) + // break + // } + // } + // time.Sleep(time.Millisecond * 200) + // } PrintMemUsage() + +} + +func TestCrontabPlus(t *testing.T) { + // crontab := "0-5/2,7-30/3,30,35,40-^1 * * * *" //(秒) 分 时 号(每月的多少号, 要注意月可可能性) 星期几(每个星期的) /每 ,列表 -范围 + // crondata := NewCrontab("*22 * * * *") + } func PrintMemUsage() { diff --git a/trie_test.go b/trie_test.go index 4dc2cf9..171a925 100644 --- a/trie_test.go +++ b/trie_test.go @@ -1,50 +1,94 @@ package curl2info -import "time" +import ( + "time" +) type SecondNode int +type INode interface { + Get() interface{} + Set(value interface{}) + GetStatus() bool + SetStatus(bool) +} + +type TYNode struct { + IsOk bool + Value interface{} +} + +func (min *TYNode) Get() interface{} { + return min.Value +} + +func (min *TYNode) Set(value interface{}) { + min.Value = value +} + +func (min *TYNode) SetStatus(status bool) { + min.IsOk = status +} + +func (min *TYNode) GetStatus() bool { + return min.IsOk +} + type MinuteNode struct { - IsOK bool - // Second [60]SecondNode + TYNode +} + +func NewMinuteNode() *MinuteNode { + return &MinuteNode{} } type HourNode struct { - IsOK bool - Minute [60]MinuteNode + TYNode + All *MinuteNode + Minute [60]*MinuteNode } -type DayNode struct { - IsOK bool - Week time.Weekday - Hour [24]*HourNode +func (hour *HourNode) CreateMinute(nminute int) { + min := &MinuteNode{} + min.IsOk = true + hour.Minute[nminute] = min } func NewHourNode() *HourNode { return &HourNode{} } -func (hour *HourNode) CreateHour(IsOk bool) { - h := &HourNode{} - h.IsOK = IsOk +type DayNode struct { + TYNode + + Week time.Weekday + All *HourNode + Hour [24]*HourNode +} + +func (day *DayNode) CreateHour(nhour int) { + hour := &HourNode{} + hour.IsOk = true + day.Hour[nhour] = hour } func NewDayNode(curday *time.Time) *DayNode { day := &DayNode{} week := curday.Weekday() - day.IsOK = true + day.IsOk = true day.Week = week return day } type MonthNode struct { - IsOK bool - MaxDay int + TYNode - First *time.Time - Day [32]*DayNode + MaxDay int + All *DayNode + First *time.Time + Day [32]*DayNode } func (month *MonthNode) CreateDay(nday int) { @@ -55,7 +99,7 @@ func (month *MonthNode) CreateDay(nday int) { func NewMonthNode(year, month int) *MonthNode { Month := &MonthNode{} First := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.Local) - Month.IsOK = false + Month.IsOk = true Month.First = &First Month.MaxDay = Month.First.AddDate(0, 1, -1).Day() return Month @@ -63,28 +107,244 @@ func NewMonthNode(year, month int) *MonthNode { type TrieYear struct { Year int + All *MonthNode Month [13]*MonthNode } -func CreateTrieYear(year int) *TrieYear { +func (ty *TrieYear) CheckYear() bool { + year := time.Now().Year() + return ty.Year == year +} +func NewTrieYear(year int) *TrieYear { ty := TrieYear{} - // ty.Month[0].IsOK = false + ty.Year = time.Now().Year() + return &ty +} + +func (ty *TrieYear) AllPlanTime(aftertime time.Time) []time.Time { + + now := aftertime + + var result []time.Time for i := 1; i <= 12; i++ { - ty.Month[i] = NewMonthNode(year, i) - month := ty.Month[i] - for begin := 1; begin <= month.MaxDay; begin++ { + if i < int(now.Month()) { + continue + } - month.CreateDay(begin) + var month *MonthNode + if ty.All != nil { + month = ty.All + } else { + month = ty.Month[i] + } - for j := 0; j < 24; j++ { - month.Day[begin].Hour[j] = NewHourNode() + if month != nil { + for j := 1; j <= 31; j++ { + + if j < int(now.Day()) { + continue + } + + var day *DayNode + if month.All != nil { + day = month.All + } else { + day = month.Day[j] + } + + if day != nil { + for k := 0; k <= 23; k++ { + + if k < int(now.Hour()) { + continue + } + + var hour *HourNode + if day.All != nil { + hour = day.All + } else { + hour = day.Hour[k] + } + + if hour != nil { + + for n := 0; n <= 59; n++ { + + if n < int(now.Minute()) { + continue + } + + var min *MinuteNode + if hour.All != nil { + min = hour.All + } else { + min = hour.Minute[n] + } + + if min != nil { + if min.IsOk { + result = append(result, time.Date(ty.Year, time.Month(i), j, k, n, 0, 0, time.Local)) + } + } + } + + } + } + } } } + } + + return result +} + +func (ty *TrieYear) TimeUp() INode { + now := time.Now() + + // 月 + var month *MonthNode + if ty.All != nil { + month = ty.All + } else { + month = ty.Month[int(now.Month())] + } + + // 天 + if month != nil { + + var day *DayNode + if month.All != nil { + day = month.All + } else { + day = month.Day[now.Day()] + } + + if day != nil { + + // 星期suit + + var hour *HourNode + if day.All != nil { + hour = day.All + } else { + hour = day.Hour[now.Hour()] + } + + var min *MinuteNode + if hour != nil { + min = hour.Minute[now.Minute()] + } + + return min + } } - return &ty + return nil +} + +func (ty *TrieYear) InsertCrontab(cron *Crontab) { + // 月的填充 + for _, month := range cron.Month { + + if month.isAll { + ty.All = NewMonthNode(ty.Year, 1) + insertDay(cron, ty.All) + } else { + + left := month.left + right := month.right + + for i := left; i <= right; i += month.per { + curMonth := NewMonthNode(ty.Year, i) + ty.Month[i] = curMonth + // 天的填充 + insertDay(cron, curMonth) + } + } + } +} + +func filterDay(cron *Crontab, curday *DayNode) bool { + + for _, w := range cron.Week { + if w.isAll { + return true + } + + for n := w.left; n <= w.right; n += w.per { + if time.Weekday(n) == curday.Week { + return true + } + } + } + return false +} + +func insertDay(cron *Crontab, curMonth *MonthNode) { + for _, day := range cron.Day { + if day.isAll { + curMonth.All = NewDayNode(curMonth.First) + insertHour(cron, curMonth.All) + } else { + + left := day.left + if left < 0 { + left += curMonth.MaxDay + 1 + } + + right := day.right + if right < 0 { + right += curMonth.MaxDay + 1 + } + + for j := left; j <= right; j += day.per { + curMonth.CreateDay(j) + curDay := curMonth.Day[j] + + insertHour(cron, curDay) + } + } + } +} + +func insertHour(cron *Crontab, curDay *DayNode) { + // 时的填充 + for _, hour := range cron.Hour { + if hour.isAll { + curDay.All = NewHourNode() + + insertMinute(cron, curDay.All) + } else { + + left := hour.left + right := hour.right + + for k := left; k <= right; k += hour.per { + curDay.CreateHour(k) + curHour := curDay.Hour[k] + + insertMinute(cron, curHour) + } + } + } +} + +func insertMinute(cron *Crontab, curHour *HourNode) { + for _, min := range cron.Min { + if min.isAll { + curHour.All = NewMinuteNode() + curHour.All.IsOk = true + } else { + left := min.left + right := min.right + + for l := left; l <= right; l += min.per { + curHour.CreateMinute(l) + } + } + } }