CronHelper.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. namespace WingServerCommon.Utilities
  2. {
  3. public class CronHelper
  4. {
  5. /// <summary>
  6. /// Cron表达式转换(自定义开始时间)
  7. /// </summary>
  8. /// <param name="cron">表达式</param>
  9. /// <param name="now">开始时间</param>
  10. /// <returns>最近要执行的时间字符串</returns>
  11. public static string GetNextDateTime(string cron, DateTime now)
  12. {
  13. try
  14. {
  15. string[] arr = cron.Split(' ');
  16. if (IsOrNoOne(cron))
  17. {
  18. string date = arr[6] + "/" + arr[4] + "/" + arr[3] + " " + arr[2] + ":" + arr[1] + ":" + arr[0];
  19. if(DateTime.Compare(Convert.ToDateTime(date),now)>0)
  20. {
  21. return date;
  22. }
  23. else
  24. {
  25. return null;
  26. }
  27. }
  28. Cron c = new Cron();
  29. Seconds(c, arr[0]);
  30. Minutes(c, arr[1]);
  31. Hours(c, arr[2]);
  32. Month(c, arr[4]);
  33. if (arr.Length < 7)
  34. {
  35. Year(c, null);
  36. }
  37. else
  38. {
  39. Year(c, arr[6]);
  40. }
  41. int addtime = 1;
  42. while (true)
  43. {
  44. if (c.Seconds[now.Second] == 1 && c.Minutes[now.Minute] == 1 && c.Hours[now.Hour] == 1 && c.Month[now.Month - 1] == 1 && c.Year[now.Year - 2019] == 1)
  45. {
  46. if (arr[3] != "?")
  47. {
  48. Days(c, arr[3], DateTime.DaysInMonth(now.Year, now.Month), now);
  49. int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
  50. if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
  51. {
  52. return now.ToString("yyyy/MM/dd HH:mm:ss");
  53. }
  54. }
  55. else
  56. {
  57. Weeks(c, arr[5], DateTime.DaysInMonth(now.Year, now.Month), now);
  58. int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
  59. if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
  60. {
  61. return now.ToString("yyyy/MM/dd HH:mm:ss");
  62. }
  63. }
  64. }
  65. else if(c.Seconds[0] == 1 && c.Minutes[now.Minute] == 1 && c.Hours[now.Hour] == 1 && c.Month[now.Month - 1] == 1 && c.Year[now.Year - 2019] == 1)
  66. {
  67. if (arr[3] != "?")
  68. {
  69. Days(c, arr[3], DateTime.DaysInMonth(now.Year, now.Month), now);
  70. int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
  71. if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
  72. {
  73. return (now.ToString("yyyy/MM/dd HH:mm") + ":00");
  74. }
  75. }
  76. else
  77. {
  78. Weeks(c, arr[5], DateTime.DaysInMonth(now.Year, now.Month), now);
  79. int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
  80. if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
  81. {
  82. return (now.ToString("yyyy/MM/dd HH:mm") + ":00");
  83. }
  84. }
  85. }
  86. c.Init();
  87. if (!arr[1].Contains('-') && !arr[1].Contains(',') && !arr[1].Contains('*') && !arr[1].Contains('/'))
  88. {
  89. if (now.Minute == int.Parse(arr[1]))
  90. {
  91. addtime = 3600;
  92. }
  93. }
  94. else if (arr[0] == "0" && now.Second == 0)
  95. {
  96. addtime = 60;
  97. }
  98. now = now.AddSeconds(addtime);
  99. }
  100. }
  101. catch
  102. {
  103. return null;
  104. }
  105. }
  106. /// <summary>
  107. /// Cron表达式转换(自定义开始时间)
  108. /// </summary>
  109. /// <param name="cron">表达式</param>
  110. /// <param name="now">开始时间</param>
  111. /// <returns>最近5次要执行的时间</returns>
  112. public static List<DateTime> CronToDateTime(string cron, DateTime now)
  113. {
  114. try
  115. {
  116. List<DateTime> lits = new List<DateTime>();
  117. Cron c = new Cron();
  118. string[] arr = cron.Split(' ');
  119. Seconds(c, arr[0]);
  120. Minutes(c, arr[1]);
  121. Hours(c, arr[2]);
  122. Month(c, arr[4]);
  123. if (arr.Length < 7)
  124. {
  125. Year(c, null);
  126. }
  127. else
  128. {
  129. Year(c, arr[6]);
  130. }
  131. int addtime = 1;
  132. while (true)
  133. {
  134. if (c.Seconds[now.Second] == 1 && c.Minutes[now.Minute] == 1 && c.Hours[now.Hour] == 1 && c.Month[now.Month - 1] == 1 && c.Year[now.Year - 2019] == 1)
  135. {
  136. if (arr[3] != "?")
  137. {
  138. Days(c, arr[3], DateTime.DaysInMonth(now.Year, now.Month), now);
  139. int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
  140. if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
  141. {
  142. lits.Add(now);
  143. }
  144. }
  145. else
  146. {
  147. Weeks(c, arr[5], DateTime.DaysInMonth(now.Year, now.Month), now);
  148. int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
  149. if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
  150. {
  151. lits.Add(now);
  152. }
  153. }
  154. }
  155. if (lits.Count >= 5)
  156. {
  157. break;
  158. }
  159. c.Init();
  160. if (!arr[1].Contains('-') && !arr[1].Contains(',') && !arr[1].Contains('*') && !arr[1].Contains('/'))
  161. {
  162. if (now.Minute == int.Parse(arr[1]))
  163. {
  164. addtime = 3600;
  165. }
  166. }
  167. else if (arr[0] == "0" && now.Second == 0)
  168. {
  169. addtime = 60;
  170. }
  171. now = now.AddSeconds(addtime);
  172. }
  173. return lits;
  174. }
  175. catch
  176. {
  177. return null;
  178. }
  179. }
  180. #region 初始化Cron对象
  181. /// <summary>
  182. /// 指定秒位
  183. /// </summary>
  184. /// <param name="c">cron表达式</param>
  185. /// <param name="str">秒位</param>
  186. private static void Seconds(Cron c, string str)
  187. {
  188. if (str == "*")
  189. {
  190. for (int i = 0; i < 60; i++)
  191. {
  192. c.Seconds[i] = 1;
  193. }
  194. }
  195. else if (str.Contains('-'))
  196. {
  197. int begin = int.Parse(str.Split('-')[0]);
  198. int end = int.Parse(str.Split('-')[1]);
  199. for (int i = begin; i <= end; i++)
  200. {
  201. c.Seconds[i] = 1;
  202. }
  203. }
  204. else if (str.Contains('/'))
  205. {
  206. int begin = int.Parse(str.Split('/')[0]);
  207. int interval = int.Parse(str.Split('/')[1]);
  208. while (true)
  209. {
  210. c.Seconds[begin] = 1;
  211. if ((begin + interval) >= 60)
  212. break;
  213. begin += interval;
  214. }
  215. }
  216. else if (str.Contains(','))
  217. {
  218. for (int i = 0; i < str.Split(',').Length; i++)
  219. {
  220. c.Seconds[int.Parse(str.Split(',')[i])] = 1;
  221. }
  222. }
  223. else
  224. {
  225. c.Seconds[int.Parse(str)] = 1;
  226. }
  227. }
  228. /// <summary>
  229. /// 指定分位
  230. /// </summary>
  231. /// <param name="c">cron表达式</param>
  232. /// <param name="str">分位</param>
  233. private static void Minutes(Cron c, string str)
  234. {
  235. if (str == "*")
  236. {
  237. for (int i = 0; i < 60; i++)
  238. {
  239. c.Minutes[i] = 1;
  240. }
  241. }
  242. else if (str.Contains('-'))
  243. {
  244. int begin = int.Parse(str.Split('-')[0]);
  245. int end = int.Parse(str.Split('-')[1]);
  246. for (int i = begin; i <= end; i++)
  247. {
  248. c.Minutes[i] = 1;
  249. }
  250. }
  251. else if (str.Contains('/'))
  252. {
  253. int begin = int.Parse(str.Split('/')[0]);
  254. int interval = int.Parse(str.Split('/')[1]);
  255. while (true)
  256. {
  257. c.Minutes[begin] = 1;
  258. if ((begin + interval) >= 60)
  259. break;
  260. begin += interval;
  261. }
  262. }
  263. else if (str.Contains(','))
  264. {
  265. for (int i = 0; i < str.Split(',').Length; i++)
  266. {
  267. c.Minutes[int.Parse(str.Split(',')[i])] = 1;
  268. }
  269. }
  270. else
  271. {
  272. c.Minutes[int.Parse(str)] = 1;
  273. }
  274. }
  275. /// <summary>
  276. /// 指定小时位
  277. /// </summary>
  278. /// <param name="c">cron表达式</param>
  279. /// <param name="str">小时位</param>
  280. private static void Hours(Cron c, string str)
  281. {
  282. if (str == "*")
  283. {
  284. for (int i = 0; i < 24; i++)
  285. {
  286. c.Hours[i] = 1;
  287. }
  288. }
  289. else if (str.Contains('-'))
  290. {
  291. int begin = int.Parse(str.Split('-')[0]);
  292. int end = int.Parse(str.Split('-')[1]);
  293. for (int i = begin; i <= end; i++)
  294. {
  295. c.Hours[i] = 1;
  296. }
  297. }
  298. else if (str.Contains('/'))
  299. {
  300. int begin = int.Parse(str.Split('/')[0]);
  301. int interval = int.Parse(str.Split('/')[1]);
  302. while (true)
  303. {
  304. c.Hours[begin] = 1;
  305. if ((begin + interval) >= 24)
  306. break;
  307. begin += interval;
  308. }
  309. }
  310. else if (str.Contains(','))
  311. {
  312. for (int i = 0; i < str.Split(',').Length; i++)
  313. {
  314. c.Hours[int.Parse(str.Split(',')[i])] = 1;
  315. }
  316. }
  317. else
  318. {
  319. c.Hours[int.Parse(str)] = 1;
  320. }
  321. }
  322. /// <summary>
  323. /// 指定月位
  324. /// </summary>
  325. /// <param name="c">cron表达式</param>
  326. /// <param name="str">月位</param>
  327. private static void Month(Cron c, string str)
  328. {
  329. if (str == "*")
  330. {
  331. for (int i = 0; i < 12; i++)
  332. {
  333. c.Month[i] = 1;
  334. }
  335. }
  336. else if (str.Contains('-'))
  337. {
  338. int begin = int.Parse(str.Split('-')[0]);
  339. int end = int.Parse(str.Split('-')[1]);
  340. for (int i = begin; i <= end; i++)
  341. {
  342. c.Month[i - 1] = 1;
  343. }
  344. }
  345. else if (str.Contains('/'))
  346. {
  347. int begin = int.Parse(str.Split('/')[0]);
  348. int interval = int.Parse(str.Split('/')[1]);
  349. while (true)
  350. {
  351. c.Month[begin - 1] = 1;
  352. if ((begin + interval) >= 12)
  353. break;
  354. begin += interval;
  355. }
  356. }
  357. else if (str.Contains(','))
  358. {
  359. for (int i = 0; i < str.Split(',').Length; i++)
  360. {
  361. c.Month[int.Parse(str.Split(',')[i]) - 1] = 1;
  362. }
  363. }
  364. else
  365. {
  366. c.Month[int.Parse(str) - 1] = 1;
  367. }
  368. }
  369. /// <summary>
  370. /// 指定年位
  371. /// </summary>
  372. /// <param name="c">cron表达式</param>
  373. /// <param name="str">年位</param>
  374. private static void Year(Cron c, string str)
  375. {
  376. if (str == null || str == "*")
  377. {
  378. for (int i = 0; i < 80; i++)
  379. {
  380. c.Year[i] = 1;
  381. }
  382. }
  383. else if (str.Contains('-'))
  384. {
  385. int begin = int.Parse(str.Split('-')[0]);
  386. int end = int.Parse(str.Split('-')[1]);
  387. for (int i = begin - 2019; i <= end - 2019; i++)
  388. {
  389. c.Year[i] = 1;
  390. }
  391. }
  392. else
  393. {
  394. c.Year[int.Parse(str) - 2019] = 1;
  395. }
  396. }
  397. /// <summary>
  398. /// 指定天位
  399. /// </summary>
  400. /// <param name="c">cron表达式</param>
  401. /// <param name="str">天位</param>
  402. private static void Days(Cron c, string str, int len, DateTime now)
  403. {
  404. for (int i = 0; i < 7; i++)
  405. {
  406. c.Weeks[i] = 1;
  407. }
  408. if (str == "*" || str == "?")
  409. {
  410. for (int i = 0; i < len; i++)
  411. {
  412. c.Days[i] = 1;
  413. }
  414. }
  415. else if (str.Contains('-'))
  416. {
  417. int begin = int.Parse(str.Split('-')[0]);
  418. int end = int.Parse(str.Split('-')[1]);
  419. for (int i = begin; i <= end; i++)
  420. {
  421. c.Days[i - 1] = 1;
  422. }
  423. }
  424. else if (str.Contains('/'))
  425. {
  426. int begin = int.Parse(str.Split('/')[0]);
  427. int interval = int.Parse(str.Split('/')[1]);
  428. while (true)
  429. {
  430. c.Days[begin - 1] = 1;
  431. if ((begin + interval) >= len)
  432. break;
  433. begin += interval;
  434. }
  435. }
  436. else if (str.Contains(','))
  437. {
  438. for (int i = 0; i < str.Split(',').Length; i++)
  439. {
  440. c.Days[int.Parse(str.Split(',')[i]) - 1] = 1;
  441. }
  442. }
  443. else if (str.Contains('L'))
  444. {
  445. int i = str.Replace("L", "") == "" ? 0 : int.Parse(str.Replace("L", ""));
  446. c.Days[len - 1 - i] = 1;
  447. }
  448. else if (str.Contains('W'))
  449. {
  450. c.Days[len - 1] = 1;
  451. }
  452. else
  453. {
  454. c.Days[int.Parse(str) - 1] = 1;
  455. }
  456. }
  457. /// <summary>
  458. /// 指定星期位
  459. /// </summary>
  460. /// <param name="c">cron表达式</param>
  461. /// <param name="str">星期位</param>
  462. private static void Weeks(Cron c, string str, int len, DateTime now)
  463. {
  464. if (str == "*" || str == "?")
  465. {
  466. for (int i = 0; i < 7; i++)
  467. {
  468. c.Weeks[i] = 1;
  469. }
  470. }
  471. else if (str.Contains('-'))
  472. {
  473. int begin = int.Parse(str.Split('-')[0]);
  474. int end = int.Parse(str.Split('-')[1]);
  475. for (int i = begin; i <= end; i++)
  476. {
  477. c.Weeks[i - 1] = 1;
  478. }
  479. }
  480. else if (str.Contains(','))
  481. {
  482. for (int i = 0; i < str.Split(',').Length; i++)
  483. {
  484. c.Weeks[int.Parse(str.Split(',')[i]) - 1] = 1;
  485. }
  486. }
  487. else if (str.Contains('L'))
  488. {
  489. int i = str.Replace("L", "") == "" ? 0 : int.Parse(str.Replace("L", ""));
  490. if (i == 0)
  491. {
  492. c.Weeks[6] = 1;
  493. }
  494. else
  495. {
  496. c.Weeks[i - 1] = 1;
  497. c.Days[GetLastWeek(i, now) - 1] = 1;
  498. return;
  499. }
  500. }
  501. else if (str.Contains('#'))
  502. {
  503. int i = int.Parse(str.Split('#')[0]);
  504. int j = int.Parse(str.Split('#')[1]);
  505. c.Weeks[i - 1] = 1;
  506. c.Days[GetWeek(i - 1, j, now)] = 1;
  507. return;
  508. }
  509. else
  510. {
  511. c.Weeks[int.Parse(str) - 1] = 1;
  512. }
  513. //week中初始化day,则说明day没要求
  514. for (int i = 0; i < len; i++)
  515. {
  516. c.Days[i] = 1;
  517. }
  518. }
  519. #endregion
  520. /// <summary>
  521. /// 获取最后一个星期几的day
  522. /// </summary>
  523. /// <param name="i">星期几</param>
  524. /// <param name="now">现在时间</param>
  525. /// <returns></returns>
  526. private static int GetLastWeek(int i, DateTime now)
  527. {
  528. DateTime d = now.AddDays(1 - now.Day).Date.AddMonths(1).AddSeconds(-1);
  529. int DayOfWeek = ((((int)d.DayOfWeek) + 6) % 7) + 1;
  530. int a = DayOfWeek >= i ? DayOfWeek - i : 7 + DayOfWeek - i;
  531. return DateTime.DaysInMonth(now.Year, now.Month) - a;
  532. }
  533. /// <summary>
  534. /// 获取当月第几个星期几的day
  535. /// </summary>
  536. /// <param name="i">星期几</param>
  537. /// <param name="j">第几周</param>
  538. /// <param name="now">现在时间</param>
  539. /// <returns></returns>
  540. private static int GetWeek(int i, int j, DateTime now)
  541. {
  542. int day = 0;
  543. DateTime d = new DateTime(now.Year, now.Month, 1);
  544. int DayOfWeek = ((((int)d.DayOfWeek) + 6) % 7) + 1;
  545. if (i >= DayOfWeek)
  546. {
  547. day = (7 - DayOfWeek + 1) + 7 * (j - 2) + i;
  548. }
  549. else
  550. {
  551. day = (7 - DayOfWeek + 1) + 7 * (j - 1) + i;
  552. }
  553. return day;
  554. }
  555. /// <summary>
  556. /// 是否一个
  557. /// </summary>
  558. public static bool IsOrNoOne(string cron)
  559. {
  560. if (cron.Contains('-') || cron.Contains(',') || cron.Contains('/') || cron.Contains('*'))
  561. {
  562. return false;
  563. }
  564. else
  565. {
  566. return true;
  567. }
  568. }
  569. }
  570. public class Cron
  571. {
  572. /// <summary>
  573. /// 秒位,表示1-60秒
  574. /// </summary>
  575. private int[] seconds = new int[60];
  576. /// <summary>
  577. /// 分位,表示1-60分
  578. /// </summary>
  579. private int[] minutes = new int[60];
  580. /// <summary>
  581. /// 小时位,表示1-12小时
  582. /// </summary>
  583. private int[] hours = new int[24];
  584. /// <summary>
  585. /// 天位,表示1-31天
  586. /// </summary>
  587. private int[] days = new int[31];
  588. /// <summary>
  589. /// 月位,表示1-12月
  590. /// </summary>
  591. private int[] month = new int[12];
  592. /// <summary>
  593. /// 星期位,表示星期1-星期天
  594. /// </summary>
  595. private int[] weeks = new int[7];
  596. //年位,表示2019-2099年
  597. private int[] year = new int[80];
  598. public int[] Seconds { get => seconds; set => seconds = value; }
  599. public int[] Minutes { get => minutes; set => minutes = value; }
  600. public int[] Hours { get => hours; set => hours = value; }
  601. public int[] Days { get => days; set => days = value; }
  602. public int[] Month { get => month; set => month = value; }
  603. public int[] Weeks { get => weeks; set => weeks = value; }
  604. public int[] Year { get => year; set => year = value; }
  605. public Cron()
  606. {
  607. for (int i = 0; i < 60; i++)
  608. {
  609. seconds[i] = 0;
  610. minutes[i] = 0;
  611. }
  612. for (int i = 0; i < 24; i++)
  613. {
  614. hours[i] = 0;
  615. }
  616. for (int i = 0; i < 31; i++)
  617. {
  618. days[i] = 0;
  619. }
  620. for (int i = 0; i < 12; i++)
  621. {
  622. month[i] = 0;
  623. }
  624. for (int i = 0; i < 7; i++)
  625. {
  626. weeks[i] = 0;
  627. }
  628. for (int i = 0; i < 80; i++)
  629. {
  630. year[i] = 0;
  631. }
  632. }
  633. /// <summary>
  634. /// 初始化星期和天
  635. /// </summary>
  636. public void Init()
  637. {
  638. for (int i = 0; i < 7; i++)
  639. {
  640. weeks[i] = 0;
  641. }
  642. for (int i = 0; i < 31; i++)
  643. {
  644. days[i] = 0;
  645. }
  646. }
  647. }
  648. }