periodic.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package openTime
  2. import (
  3. "fmt"
  4. "regexp"
  5. "strings"
  6. "sync"
  7. "time"
  8. cherryString "github.com/cherry-game/cherry/extend/string"
  9. ctime "github.com/cherry-game/cherry/extend/time"
  10. )
  11. type (
  12. periodicParser struct{}
  13. periodic struct {
  14. mux sync.RWMutex
  15. times []*timeRange
  16. duration time.Duration
  17. interval time.Duration
  18. }
  19. )
  20. // 20230101/12:00|7d|21d
  21. func (p *periodicParser) Parse(params string) (OpenTime, error) {
  22. ts := strings.Split(params, "|")
  23. if len(ts) < 3 {
  24. return nil, fmt.Errorf("nonsupport params: %s", params)
  25. }
  26. startTime, err := parseDateTime(ts[0])
  27. if err != nil {
  28. return nil, err
  29. }
  30. reg, err := regexp.Compile(`(\d+)([yMdhm])`)
  31. if err != nil {
  32. return nil, err
  33. }
  34. duration, err := p.matchDuration(reg, ts[1])
  35. if err != nil {
  36. return nil, err
  37. }
  38. interval, err := p.matchDuration(reg, ts[2])
  39. if err != nil {
  40. return nil, err
  41. }
  42. now := ctime.Now()
  43. for now.After(startTime.Time) && now.After(startTime.Time.Add(duration)) {
  44. startTime.Time = startTime.Add(interval)
  45. }
  46. endTime := ctime.CherryTime{Time: startTime.Add(duration)}
  47. per := &periodic{
  48. times: []*timeRange{
  49. {
  50. startTime: startTime,
  51. endTime: endTime,
  52. },
  53. },
  54. duration: duration,
  55. interval: interval,
  56. }
  57. return per, nil
  58. }
  59. func (p *periodicParser) matchDuration(reg *regexp.Regexp, str string) (time.Duration, error) {
  60. ss := reg.FindStringSubmatch(str)
  61. if len(ss) < 3 {
  62. return 0, fmt.Errorf("nonsupport params: %s", str)
  63. }
  64. i, ok := cherryString.ToInt64(ss[1])
  65. if !ok {
  66. return 0, fmt.Errorf("nonsupport params: %s", str)
  67. }
  68. var duration time.Duration
  69. switch ss[2] {
  70. case "d":
  71. duration = time.Hour * 24 * time.Duration(i)
  72. case "h":
  73. duration = time.Hour * time.Duration(i)
  74. case "m":
  75. duration = time.Minute * time.Duration(i)
  76. default:
  77. return 0, fmt.Errorf("nonsupport params: %s", str)
  78. }
  79. return duration, nil
  80. }
  81. func (p *periodic) Between(t *ctime.CherryTime) bool {
  82. return p.between(0, t)
  83. }
  84. func (p *periodic) between(i int, t *ctime.CherryTime) bool {
  85. flag := false
  86. ts := p.times
  87. p.mux.RLock()
  88. for ; i < len(ts); i++ {
  89. node := ts[i]
  90. if t.After(node.startTime.Time) && t.Before(node.endTime.Time) {
  91. flag = true
  92. break
  93. }
  94. }
  95. p.mux.RUnlock()
  96. if !flag && i == len(ts) && t.After(ts[i-1].endTime.Time) {
  97. p.grow(t)
  98. return p.between(i, t)
  99. }
  100. // if i >= 10 {
  101. // p.trim(i-1)
  102. // }
  103. return flag
  104. }
  105. func (p *periodic) grow(t *ctime.CherryTime) {
  106. p.mux.Lock()
  107. defer p.mux.Unlock()
  108. t0 := p.times[len(p.times)-1]
  109. for t.After(t0.endTime.Time) {
  110. t1 := &timeRange{
  111. startTime: ctime.CherryTime{Time: t0.startTime.Add(p.interval)},
  112. endTime: ctime.CherryTime{Time: t0.startTime.Add(p.duration)},
  113. }
  114. p.times = append(p.times, t1)
  115. t0 = t1
  116. }
  117. }
  118. func (p *periodic) trim(i int) {
  119. p.mux.Lock()
  120. defer p.mux.Unlock()
  121. p.times = p.times[i:]
  122. }
  123. func (p *periodic) TimeRange(t *ctime.CherryTime) (ctime.CherryTime, ctime.CherryTime) {
  124. p.grow(t)
  125. var (
  126. startTime ctime.CherryTime
  127. endTime ctime.CherryTime
  128. )
  129. p.mux.RLock()
  130. for _, v := range p.times {
  131. if v.startTime.After(t.Time) || v.endTime.After(t.Time) {
  132. startTime = v.startTime
  133. endTime = v.endTime
  134. break
  135. }
  136. }
  137. p.mux.RUnlock()
  138. return startTime, endTime
  139. }