package openTime import ( "fmt" "regexp" "strings" "sync" "time" cherryString "github.com/cherry-game/cherry/extend/string" ctime "github.com/cherry-game/cherry/extend/time" ) type ( periodicParser struct{} periodic struct { mux sync.RWMutex times []*timeRange duration time.Duration interval time.Duration } ) // 20230101/12:00|7d|21d func (p *periodicParser) Parse(params string) (OpenTime, error) { ts := strings.Split(params, "|") if len(ts) < 3 { return nil, fmt.Errorf("nonsupport params: %s", params) } startTime, err := parseDateTime(ts[0]) if err != nil { return nil, err } reg, err := regexp.Compile(`(\d+)([yMdhm])`) if err != nil { return nil, err } duration, err := p.matchDuration(reg, ts[1]) if err != nil { return nil, err } interval, err := p.matchDuration(reg, ts[2]) if err != nil { return nil, err } now := ctime.Now() for now.After(startTime.Time) && now.After(startTime.Time.Add(duration)) { startTime.Time = startTime.Add(interval) } endTime := ctime.CherryTime{Time: startTime.Add(duration)} per := &periodic{ times: []*timeRange{ { startTime: startTime, endTime: endTime, }, }, duration: duration, interval: interval, } return per, nil } func (p *periodicParser) matchDuration(reg *regexp.Regexp, str string) (time.Duration, error) { ss := reg.FindStringSubmatch(str) if len(ss) < 3 { return 0, fmt.Errorf("nonsupport params: %s", str) } i, ok := cherryString.ToInt64(ss[1]) if !ok { return 0, fmt.Errorf("nonsupport params: %s", str) } var duration time.Duration switch ss[2] { case "d": duration = time.Hour * 24 * time.Duration(i) case "h": duration = time.Hour * time.Duration(i) case "m": duration = time.Minute * time.Duration(i) default: return 0, fmt.Errorf("nonsupport params: %s", str) } return duration, nil } func (p *periodic) Between(t *ctime.CherryTime) bool { return p.between(0, t) } func (p *periodic) between(i int, t *ctime.CherryTime) bool { flag := false ts := p.times p.mux.RLock() for ; i < len(ts); i++ { node := ts[i] if t.After(node.startTime.Time) && t.Before(node.endTime.Time) { flag = true break } } p.mux.RUnlock() if !flag && i == len(ts) && t.After(ts[i-1].endTime.Time) { p.grow(t) return p.between(i, t) } // if i >= 10 { // p.trim(i-1) // } return flag } func (p *periodic) grow(t *ctime.CherryTime) { p.mux.Lock() defer p.mux.Unlock() t0 := p.times[len(p.times)-1] for t.After(t0.endTime.Time) { t1 := &timeRange{ startTime: ctime.CherryTime{Time: t0.startTime.Add(p.interval)}, endTime: ctime.CherryTime{Time: t0.startTime.Add(p.duration)}, } p.times = append(p.times, t1) t0 = t1 } } func (p *periodic) trim(i int) { p.mux.Lock() defer p.mux.Unlock() p.times = p.times[i:] } func (p *periodic) TimeRange(t *ctime.CherryTime) (ctime.CherryTime, ctime.CherryTime) { p.grow(t) var ( startTime ctime.CherryTime endTime ctime.CherryTime ) p.mux.RLock() for _, v := range p.times { if v.startTime.After(t.Time) || v.endTime.After(t.Time) { startTime = v.startTime endTime = v.endTime break } } p.mux.RUnlock() return startTime, endTime }