时间选定库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

529 lines
13 KiB

5 years ago
5 years ago
3 years ago
5 years ago
3 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
3 years ago
5 years ago
3 years ago
5 years ago
3 years ago
3 years ago
5 years ago
3 years ago
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
5 years ago
3 years ago
5 years ago
3 years ago
3 years ago
3 years ago
3 years ago
5 years ago
3 years ago
5 years ago
3 years ago
3 years ago
5 years ago
5 years ago
3 years ago
5 years ago
3 years ago
5 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
4 years ago
5 years ago
4 years ago
5 years ago
5 years ago
4 years ago
5 years ago
  1. package time_arrow
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "sort"
  7. "strings"
  8. "sync"
  9. "time"
  10. "github.com/google/uuid"
  11. )
  12. type TimeArrowType string
  13. const (
  14. TimeArrowTypeDayOfWeek TimeArrowType = "DAY_OF_WEEK"
  15. TimeArrowTypeDayOfMonth TimeArrowType = "DAY_OF_MONTH"
  16. TimeArrowTypeDateSlice TimeArrowType = "DATE_SLICE"
  17. TimeArrowTypeInHolidays TimeArrowType = "IN_HOLIDAYS" //节假日
  18. TimeArrowTypeAtLastDayHolidays TimeArrowType = "AT_LAST_DAY_HOLIDAYS" //节假日最后一天
  19. TimeArrowTypePriorToDayHolidays TimeArrowType = "PRIOR_TO_DAY_HOLIDAYS" //节假日前一天
  20. )
  21. type DateSlice struct {
  22. Start string `json:"start"`
  23. End string `json:"end"`
  24. }
  25. type TimeArrow struct {
  26. TimeArrowId string `json:"time_arrow_id"`
  27. Group string `json:"group"`
  28. Type TimeArrowType `json:"type"`
  29. //可选
  30. DayOfWeek []int `json:"day_of_week"`
  31. DayOfMonth []int `json:"day_of_month"`
  32. DateSlice []DateSlice `json:"date_slice"`
  33. //必选
  34. TimesOnDay []string `json:"times_on_day"`
  35. ExpandTags []string `json:"expand_tags"`
  36. ExpandValue interface{} `json:"expand_value"`
  37. Weights float64 `json:"weights"`
  38. Extension map[string]interface{} `json:"extension"`
  39. }
  40. type TimeArrows []TimeArrow
  41. func isInDateSlice(t time.Time, ta TimeArrow) bool {
  42. for e := range ta.DateSlice {
  43. startStr := ta.DateSlice[e].Start
  44. startSc := strings.Split(startStr, " ")
  45. if len(startSc) < 2 {
  46. startSc = append(startSc, "00:00:00")
  47. } else {
  48. startSc[1] = timeCompletion(startSc[1])
  49. }
  50. startStr = strings.Join(startSc, " ")
  51. endStr := ta.DateSlice[e].End
  52. endSc := strings.Split(endStr, " ")
  53. if len(endSc) < 2 {
  54. endSc = append(endSc, "00:00:00")
  55. } else {
  56. endSc[1] = timeCompletion(endSc[1])
  57. }
  58. endStr = strings.Join(endSc, " ")
  59. startTime, err := time.ParseInLocation("2006-01-02 15:04:05", startStr, time.Local)
  60. if err != nil {
  61. log.Println("时间段开始时间格式错误", startStr)
  62. continue
  63. }
  64. endTime, err := time.ParseInLocation("2006-01-02 15:04:05", endStr, time.Local)
  65. if err != nil {
  66. log.Println("时间段结束时间格式错误", startStr)
  67. continue
  68. }
  69. if startTime.Unix() > endTime.Unix() {
  70. log.Println("开始时间必须小于结束时间", startStr, endStr)
  71. continue
  72. }
  73. if (t.After(startTime) || t.Equal(startTime)) && t.Before(endTime) {
  74. return true
  75. }
  76. }
  77. return false
  78. }
  79. func isInWeekOfDay(t time.Time, ta TimeArrow) bool {
  80. nowWeekInt := int(t.Weekday())
  81. for k := range ta.DayOfWeek {
  82. if ta.DayOfWeek[k] == nowWeekInt {
  83. return true
  84. }
  85. }
  86. return false
  87. }
  88. var timeCompletionCache sync.Map
  89. func timeCompletion(a string) string {
  90. rets, ok := timeCompletionCache.Load(a)
  91. if ok {
  92. return rets.(string)
  93. }
  94. sc := strings.Split(a, ":")
  95. for len(sc) < 3 {
  96. sc = append(sc, "00")
  97. }
  98. for i := range sc {
  99. if len(sc[i]) == 1 {
  100. sc[i] = "0" + sc[i]
  101. }
  102. }
  103. ret := strings.Join(sc, ":")
  104. timeCompletionCache.Store(a, ret)
  105. return ret
  106. }
  107. func isInDayOfMonth(t time.Time, ta TimeArrow) bool {
  108. day := t.Day()
  109. for e := range ta.DayOfMonth {
  110. if ta.DayOfMonth[e] == day {
  111. return true
  112. }
  113. }
  114. return false
  115. }
  116. var inTimeOfDayCache sync.Map
  117. func isInTimeOfDay(t time.Time, ta TimeArrow) bool {
  118. if len(ta.TimesOnDay) == 0 {
  119. return true
  120. }
  121. tfdate := t.Format("15:04:05")
  122. cacheKey := fmt.Sprint(tfdate, strings.Join(ta.TimesOnDay, "|"))
  123. ret, ok := inTimeOfDayCache.Load(cacheKey)
  124. if ok {
  125. return ret.(bool)
  126. }
  127. for k := range ta.TimesOnDay {
  128. tsp := strings.Split(ta.TimesOnDay[k], "-")
  129. if len(tsp) < 2 {
  130. log.Println("必须为时间段:", ta.TimesOnDay[k])
  131. continue
  132. }
  133. start, end := tsp[0], tsp[1]
  134. if end == "24" || end == "24:00" || end == "24:00:00" {
  135. end = "23:59:59"
  136. }
  137. start = timeCompletion(start)
  138. end = timeCompletion(end)
  139. // fmt.Println("ssee", start, end, tfdate)
  140. if start <= tfdate && tfdate < end {
  141. inTimeOfDayCache.Store(cacheKey, true)
  142. return true
  143. }
  144. }
  145. inTimeOfDayCache.Store(cacheKey, false)
  146. return false
  147. }
  148. func isInTimeOfDay2(t time.Time, ta TimeArrow) bool {
  149. if len(ta.TimesOnDay) == 0 {
  150. return true
  151. }
  152. // t, _ = time.ParseInLocation("2006-01-02 15:04:05", "2011-01-01 "+t.Format("15:04:05"), time.Local)
  153. tfdate := t.Format("2006-01-02")
  154. // cacheKey := fmt.Sprint(t.Format("15:04:05"), strings.Join(ta.TimesOnDay, "|"))
  155. // ret, ok := inTimeOfDayCache.Load(cacheKey)
  156. // if ok {
  157. // return ret.(bool)
  158. // }
  159. // var err error
  160. for k := range ta.TimesOnDay {
  161. tsp := strings.Split(ta.TimesOnDay[k], "-")
  162. if len(tsp) < 2 {
  163. log.Println("必须为时间段:", ta.TimesOnDay[k])
  164. continue
  165. }
  166. start, end := tsp[0], tsp[1]
  167. start = timeCompletion(start)
  168. end = timeCompletion(end)
  169. //tfdate为当天日期
  170. startTimeKey := fmt.Sprintf("%s %s", tfdate, start)
  171. startTime, _ := time.ParseInLocation("2006-01-02 15:04:05", startTimeKey, time.Local)
  172. if startTime.IsZero() {
  173. log.Println("时间段开始时间格式错误", start)
  174. continue
  175. }
  176. if end == "24" || end == "24:00" || end == "24:00:00" {
  177. end = "23:59:59"
  178. }
  179. endTimeKey := fmt.Sprintf("%s %s", tfdate, end)
  180. endTime, _ := time.ParseInLocation("2006-01-02 15:04:05", endTimeKey, time.Local)
  181. if endTime.IsZero() {
  182. log.Println("时间段结束时间格式错误", end)
  183. continue
  184. }
  185. if startTime.Unix() > endTime.Unix() {
  186. log.Println("开始时间必须小于结束时间", end)
  187. continue
  188. }
  189. if (t.After(startTime) || t.Equal(startTime)) && t.Before(endTime) {
  190. // inTimeOfDayCache.Store(cacheKey, true)
  191. return true
  192. }
  193. }
  194. // inTimeOfDayCache.Store(cacheKey, false)
  195. return false
  196. }
  197. func isInHolidays(t time.Time) bool {
  198. holidaysData, err := GetHolidaysDataWithCache()
  199. if err != nil {
  200. return false
  201. }
  202. for e := range holidaysData {
  203. startTime := holidaysData[e].Start.Local()
  204. endTime := holidaysData[e].End.Local()
  205. if (t.After(startTime) || t.Equal(startTime)) && t.Before(endTime) {
  206. return true
  207. }
  208. }
  209. return false
  210. }
  211. func isInAtLastDayHolidays(t time.Time) bool {
  212. t = t.Local()
  213. holidaysData, err := GetHolidaysDataWithCache()
  214. if err != nil {
  215. return false
  216. }
  217. for e := range holidaysData {
  218. startTime, err := time.ParseInLocation("2006-01-02 15:04:05", holidaysData[e].End.Format("2006-01-02 00:00:00"), time.Local)
  219. if err != nil {
  220. log.Println("节假日最后一天时间段开始时间格式错误", holidaysData[e].End)
  221. continue
  222. }
  223. endTime, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 23:59:59", holidaysData[e].End.Format("2006-01-02")), time.Local)
  224. if err != nil {
  225. log.Println("节假日最后一天时间段结束时间格式错误", holidaysData[e].End)
  226. continue
  227. }
  228. if (t.After(startTime) || t.Equal(startTime)) && t.Before(endTime) {
  229. return true
  230. }
  231. }
  232. return false
  233. }
  234. func isInPriorToDayHolidays(t time.Time) bool {
  235. holidaysData, err := GetHolidaysDataWithCache()
  236. if err != nil {
  237. return false
  238. }
  239. for e := range holidaysData {
  240. startTime, err := time.ParseInLocation("2006-01-02 15:04:05", holidaysData[e].Start.AddDate(0, 0, -1).Format("2006-01-02 00:00:00"), time.Local)
  241. if err != nil {
  242. log.Println("节假日前一天时间段开始时间格式错误", holidaysData[e].Start)
  243. continue
  244. }
  245. endTime, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 23:59:59", holidaysData[e].Start.AddDate(0, 0, -1).Format("2006-01-02")), time.Local)
  246. if err != nil {
  247. log.Println("节假日前一天时间段结束时间格式错误", holidaysData[e].Start)
  248. continue
  249. }
  250. if (t.After(startTime) || t.Equal(startTime)) && t.Before(endTime) {
  251. return true
  252. }
  253. }
  254. return false
  255. }
  256. func isInExpandTags(ta TimeArrow, expandTag string) bool {
  257. if len(ta.ExpandTags) == 0 {
  258. return true
  259. }
  260. for e := range ta.ExpandTags {
  261. if ta.ExpandTags[e] == expandTag {
  262. return true
  263. }
  264. }
  265. return false
  266. }
  267. func createUUID() string {
  268. uid := uuid.Must(uuid.NewUUID())
  269. return uid.String()
  270. }
  271. // CreateDayOfWeekTypePlan 创建一个每周计划
  272. func CreateDayOfWeekTypePlan(group string, dayOfWeek []int, timesOnDay []string, expandValue interface{}, expandTags []string, weights float64) TimeArrow {
  273. return TimeArrow{
  274. TimeArrowId: createUUID(),
  275. Type: TimeArrowTypeDayOfWeek,
  276. Group: group,
  277. DayOfWeek: dayOfWeek,
  278. TimesOnDay: timesOnDay,
  279. ExpandValue: expandValue,
  280. ExpandTags: expandTags,
  281. Weights: weights,
  282. }
  283. }
  284. // CreateDayOfMonthTypePlan 创建一个每月计划
  285. func CreateDayOfMonthTypePlan(group string, dayOfMonth []int, timesOnDay []string, expandValue interface{}, expandTags []string, weights float64) TimeArrow {
  286. return TimeArrow{
  287. TimeArrowId: createUUID(),
  288. Type: TimeArrowTypeDayOfMonth,
  289. Group: group,
  290. DayOfMonth: dayOfMonth,
  291. TimesOnDay: timesOnDay,
  292. ExpandValue: expandValue,
  293. ExpandTags: expandTags,
  294. Weights: weights,
  295. }
  296. }
  297. // CreateDateSliceTypePlan 创建一个时间段计划
  298. func CreateDateSliceTypePlan(group string, dateSlice []DateSlice, timesOnDay []string, expandValue interface{}, expandTags []string, weights float64) TimeArrow {
  299. return TimeArrow{
  300. TimeArrowId: createUUID(),
  301. Type: TimeArrowTypeDateSlice,
  302. Group: group,
  303. DateSlice: dateSlice,
  304. TimesOnDay: timesOnDay,
  305. ExpandValue: expandValue,
  306. ExpandTags: expandTags,
  307. Weights: weights,
  308. }
  309. }
  310. type HolidaysItem struct {
  311. Name string `json:"name"`
  312. Start time.Time `json:"start"`
  313. End time.Time `json:"end"`
  314. }
  315. func jsonEncode(v interface{}) string {
  316. b, _ := json.Marshal(v)
  317. return string(b)
  318. }
  319. var GetHolidaysData func() ([]HolidaysItem, error)
  320. var holidayCache *cache
  321. var holidayCacheLock sync.Mutex
  322. func GetHolidaysDataWithCache() ([]HolidaysItem, error) {
  323. holidayCacheLock.Lock()
  324. defer holidayCacheLock.Unlock()
  325. if holidayCache.IsExpired() {
  326. data, err := GetHolidaysData()
  327. if err != nil {
  328. return nil, err
  329. }
  330. holidayCache.SetData(jsonEncode(data), time.Second*60)
  331. }
  332. data := holidayCache.GetData()
  333. var result []HolidaysItem
  334. err := json.Unmarshal([]byte(data), &result)
  335. if len(data) == 0 {
  336. panic("GetHolidaysDataWithCache json unmarshal error")
  337. }
  338. return result, err
  339. }
  340. type TimeArrowHelper struct {
  341. GetData func(group string) (TimeArrows, error)
  342. }
  343. func (th *TimeArrowHelper) CallHitTimeArrow(t time.Time, group string, call func(*TimeArrow), expandTags ...string) error {
  344. ta, err := th.GetData(group)
  345. if err != nil {
  346. return err
  347. }
  348. sort.Slice(ta, func(i, j int) bool {
  349. return ta[i].Weights > ta[j].Weights
  350. })
  351. for e := range ta {
  352. //当天具体时间判断
  353. if !isInTimeOfDay(t, ta[e]) {
  354. continue
  355. }
  356. //扩展标签判断
  357. if !isInExpandTags(ta[e], strings.Join(expandTags, "-")) {
  358. continue
  359. }
  360. switch ta[e].Type {
  361. case TimeArrowTypeDayOfWeek:
  362. //一周中某一天是否判定
  363. if isInWeekOfDay(t, ta[e]) {
  364. call(&ta[e])
  365. }
  366. break
  367. case TimeArrowTypeDayOfMonth:
  368. //一月中某一天是否判定
  369. if isInDayOfMonth(t, ta[e]) {
  370. call(&ta[e])
  371. }
  372. break
  373. case TimeArrowTypeDateSlice:
  374. //一月中某一天是否判定
  375. if isInDateSlice(t, ta[e]) {
  376. call(&ta[e])
  377. }
  378. break
  379. case TimeArrowTypeAtLastDayHolidays:
  380. //节假日最后一天
  381. if isInAtLastDayHolidays(t) {
  382. call(&ta[e])
  383. }
  384. break
  385. case TimeArrowTypePriorToDayHolidays:
  386. //节假日前一天
  387. if isInPriorToDayHolidays(t) {
  388. call(&ta[e])
  389. }
  390. break
  391. case TimeArrowTypeInHolidays:
  392. //节假日
  393. if isInHolidays(t) {
  394. call(&ta[e])
  395. }
  396. break
  397. default:
  398. log.Println("类型错误:", ta[e].Type)
  399. }
  400. }
  401. return nil
  402. }
  403. func (th *TimeArrowHelper) GetHitTimeArrow(t time.Time, group string, expandTags ...string) (*TimeArrow, error) {
  404. ta, err := th.GetData(group)
  405. if err != nil {
  406. return nil, err
  407. }
  408. sort.Slice(ta, func(i, j int) bool {
  409. return ta[i].Weights > ta[j].Weights
  410. })
  411. for e := range ta {
  412. //当天具体时间判断
  413. if !isInTimeOfDay(t, ta[e]) {
  414. continue
  415. }
  416. //扩展标签判断
  417. if !isInExpandTags(ta[e], strings.Join(expandTags, "-")) {
  418. continue
  419. }
  420. switch ta[e].Type {
  421. case TimeArrowTypeDayOfWeek:
  422. //一周中某一天是否判定
  423. if isInWeekOfDay(t, ta[e]) {
  424. return &ta[e], nil
  425. }
  426. break
  427. case TimeArrowTypeDayOfMonth:
  428. //一月中某一天是否判定
  429. if isInDayOfMonth(t, ta[e]) {
  430. return &ta[e], nil
  431. }
  432. break
  433. case TimeArrowTypeDateSlice:
  434. //一月中某一天是否判定
  435. if isInDateSlice(t, ta[e]) {
  436. return &ta[e], nil
  437. }
  438. break
  439. case TimeArrowTypeAtLastDayHolidays:
  440. //节假日最后一天
  441. if isInAtLastDayHolidays(t) {
  442. return &ta[e], nil
  443. }
  444. break
  445. case TimeArrowTypePriorToDayHolidays:
  446. //节假日前一天
  447. if isInPriorToDayHolidays(t) {
  448. return &ta[e], nil
  449. }
  450. break
  451. case TimeArrowTypeInHolidays:
  452. //节假日
  453. if isInHolidays(t) {
  454. return &ta[e], nil
  455. }
  456. break
  457. default:
  458. log.Println("类型错误:", ta[e].Type)
  459. }
  460. }
  461. return nil, nil
  462. }