c#解析cron表示式
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IDH.Common.Utility
{
/// <summary>
/// 表示式物件
/// </summary>
class Cron
{
/// <summary>
/// 秒位,表示1-60秒
/// </summary>
private int[] seconds = new int[60];
/// <summary>
/// 分位,表示1-60分
/// </summary>
private int[] minutes = new int[60];
/// <summary>
/// 小時位,表示1-12小時
/// </summary>
private int[] hours = new int[24];
/// <summary>
/// 天位,表示1-31天
/// </summary>
private int[] days = new int[31];
/// <summary>
/// 月位,表示1-12月
/// </summary>
private int[] month = new int[12];
/// <summary>
/// 星期位,表示星期1-星期天
/// </summary>
private int[] weeks = new int[7];
//年位,表示2019-2099年
private int[] year = new int[80];
public int[] Seconds { get => seconds; set => seconds = value; }
public int[] Minutes { get => minutes; set => minutes = value; }
public int[] Hours { get => hours; set => hours = value; }
public int[] Days { get => days; set => days = value; }
public int[] Month { get => month; set => month = value; }
public int[] Weeks { get => weeks; set => weeks = value; }
public int[] Year { get => year; set => year = value; }
public Cron()
{
for (int i = 0; i < 60; i++)
{
seconds[i] = 0;
minutes[i] = 0;
}
for (int i = 0; i < 24; i++)
{
hours[i] = 0;
}
for (int i = 0; i < 31; i++)
{
days[i] = 0;
}
for (int i = 0; i < 12; i++)
{
month[i] = 0;
}
for (int i = 0; i < 7; i++)
{
weeks[i] = 0;
}
for (int i = 0; i < 80; i++)
{
year[i] = 0;
}
}
/// <summary>
/// 初始化星期和天
/// </summary>
public void Init()
{
for (int i = 0; i < 7; i++)
{
weeks[i] = 0;
}
for (int i = 0; i < 31; i++)
{
days[i] = 0;
}
}
}
/// <summary>
/// cron表示式幫助類
/// 在week上使用 5L表示本月最後一個星期五
/// 7L表示本月最後一個星期天
/// 在week上使用 7#3表示每月的第三個星期天
/// 2#4表示每月的第四個星期二
/// </summary>
public class CronHelper
{
/// <summary>
/// Cron表示式轉換(預設開始時間為當前)
/// </summary>
/// <param name="cron">表示式</param>
/// <returns>最近5次要執行的時間</returns>
private static List<DateTime> CronToDateTime(string cron)
{
try
{
List<DateTime> lits = new List<DateTime>();
Cron c = new Cron();
string[] arr = cron.Split(' ');
Seconds(c, arr[0]);
Minutes(c, arr[1]);
Hours(c, arr[2]);
Month(c, arr[4]);
if (arr.Length < 7)
{
Year(c, null);
}
else
{
Year(c, arr[6]);
}
DateTime now = DateTime.Now;
int addtime = 1;
while (true)
{
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)
{
if (arr[3] != "?")
{
Days(c, arr[3], DateTime.DaysInMonth(now.Year, now.Month), now);
int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
{
lits.Add(now);
}
}
else
{
Weeks(c, arr[5], DateTime.DaysInMonth(now.Year, now.Month), now);
int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
{
lits.Add(now);
}
}
}
if (lits.Count >= 5)
{
break;
}
c.Init();
if (!arr[1].Contains('-') && !arr[1].Contains(',') && !arr[1].Contains('*') && !arr[1].Contains('/'))
{
if (now.Minute == int.Parse(arr[1]))
{
addtime = 3600;
}
}
else if (arr[0] == "0" && now.Second == 0)
{
addtime = 60;
}
now = now.AddSeconds(addtime);
}
return lits;
}
catch
{
return null;
}
}
/// <summary>
/// Cron表示式轉換(自定義開始時間)
/// </summary>
/// <param name="cron">表示式</param>
/// <param name="now">開始時間</param>
/// <returns>最近5次要執行的時間</returns>
private static List<DateTime> CronToDateTime(string cron, DateTime now)
{
try
{
List<DateTime> lits = new List<DateTime>();
Cron c = new Cron();
string[] arr = cron.Split(' ');
Seconds(c, arr[0]);
Minutes(c, arr[1]);
Hours(c, arr[2]);
Month(c, arr[4]);
if (arr.Length < 7)
{
Year(c, null);
}
else
{
Year(c, arr[6]);
}
int addtime = 1;
while (true)
{
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)
{
if (arr[3] != "?")
{
Days(c, arr[3], DateTime.DaysInMonth(now.Year, now.Month), now);
int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
{
lits.Add(now);
}
}
else
{
Weeks(c, arr[5], DateTime.DaysInMonth(now.Year, now.Month), now);
int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
{
lits.Add(now);
}
}
}
if (lits.Count >= 5)
{
break;
}
c.Init();
if (!arr[1].Contains('-') && !arr[1].Contains(',') && !arr[1].Contains('*') && !arr[1].Contains('/'))
{
if (now.Minute == int.Parse(arr[1]))
{
addtime = 3600;
}
}
else if (arr[0] == "0" && now.Second == 0)
{
addtime = 60;
}
now = now.AddSeconds(addtime);
}
return lits;
}
catch
{
return null;
}
}
/// <summary>
/// Cron表示式轉換(預設開始時間為當前)
/// </summary>
/// <param name="cron">表示式</param>
/// <returns>最近要執行的時間字串</returns>
public static string GetNextDateTime(string cron)
{
try
{
DateTime now = DateTime.Now;
string[] arr = cron.Split(' ');
if (IsOrNoOne(cron))
{
string date = arr[6] + "/" + arr[4] + "/" + arr[3] + " " + arr[2] + ":" + arr[1] + ":" + arr[0];
if (DateTime.Compare(Convert.ToDateTime(date), now) >= 0)
{
return date;
}
else
{
return null;
}
}
Cron c = new Cron();
Seconds(c, arr[0]);
Minutes(c, arr[1]);
Hours(c, arr[2]);
Month(c, arr[4]);
if (arr.Length < 7)
{
Year(c, null);
}
else
{
Year(c, arr[6]);
}
int addtime = 1;
while (true)
{
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)
{
if (arr[3] != "?")
{
Days(c, arr[3], DateTime.DaysInMonth(now.Year, now.Month), now);
int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
{
return now.ToString("yyyy/MM/dd HH:mm:ss");
}
}
else
{
Weeks(c, arr[5], DateTime.DaysInMonth(now.Year, now.Month), now);
int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
{
return now.ToString("yyyy/MM/dd HH:mm:ss");
}
}
}
c.Init();
if (!arr[1].Contains('-') && !arr[1].Contains(',') && !arr[1].Contains('*') && !arr[1].Contains('/'))
{
if (now.Minute == int.Parse(arr[1]))
{
addtime = 3600;
}
}
else if (arr[0] == "0" && now.Second == 0)
{
addtime = 60;
}
now = now.AddSeconds(addtime);
}
}
catch
{
return null;
}
}
/// <summary>
/// Cron表示式轉換(自定義開始時間)
/// </summary>
/// <param name="cron">表示式</param>
/// <param name="now">開始時間</param>
/// <returns>最近要執行的時間字串</returns>
public static string GetNextDateTime(string cron, DateTime now)
{
try
{
string[] arr = cron.Split(' ');
if (IsOrNoOne(cron))
{
string date = arr[6] + "/" + arr[4] + "/" + arr[3] + " " + arr[2] + ":" + arr[1] + ":" + arr[0];
if (DateTime.Compare(Convert.ToDateTime(date), now) > 0)
{
return date;
}
else
{
return null;
}
}
Cron c = new Cron();
//now秒位於1-60秒哪一位
Seconds(c, arr[0]);
//now分位於1-60分哪一位
Minutes(c, arr[1]);
//now小時位於1-24小時哪一位
Hours(c, arr[2]);
//now月位於1-12月哪一位
Month(c, arr[4]);
if (arr.Length < 7)
{
Year(c, null);
}
else
{
Year(c, arr[6]);
}
//迴圈遞增時間為1秒
int addtime = 1;
while (true)
{
//判斷now的秒,分,時,月,年是否滿足條件
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)
{
if (arr[3] != "?")
{
Days(c, arr[3], DateTime.DaysInMonth(now.Year, now.Month), now);
int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
{
return now.ToString("yyyy/MM/dd HH:mm:ss");
}
}
else
{
Weeks(c, arr[5], DateTime.DaysInMonth(now.Year, now.Month), now);
int DayOfWeek = (((int)now.DayOfWeek) + 6) % 7;
if (c.Days[now.Day - 1] == 1 && c.Weeks[DayOfWeek] == 1)
{
return now.ToString("yyyy/MM/dd HH:mm:ss");
}
}
}
c.Init();
//判斷分位是否有指定數值,並且秒位為0,迴圈遞增時間改為1小時
if (!arr[1].Contains('-') && !arr[1].Contains(',') && !arr[1].Contains('*') && !arr[1].Contains('/'))
{
if (now.Minute == int.Parse(arr[1]) && now.Second == 0)
{
addtime = 3600;
}
}//判斷秒位數值是否指定為0,並且時間秒位為0,迴圈遞增時間改為60秒
else if (arr[0] == "0" && now.Second == 0)
{
addtime = 60;
}
//按秒遞增時間
now = now.AddSeconds(addtime);
}
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// Cron表示式轉換成中文描述
/// </summary>
/// <param name="cronExp">表示式</param>
/// <returns></returns>
public static string TranslateToChinese(string cronExp)
{
if (cronExp == null || cronExp.Length < 1)
{
return "cron表示式為空";
}
string[] tmpCorns = cronExp.Split(' ');
StringBuilder sBuffer = new StringBuilder();
if (tmpCorns.Length == 6)
{
//解析月
if (!tmpCorns[4].Equals("*"))
{
sBuffer.Append(tmpCorns[4]).Append("月");
}
else
{
sBuffer.Append("每月");
}
//解析周
if (!tmpCorns[5].Equals("*") && !tmpCorns[5].Equals("?"))
{
char[] tmpArray = tmpCorns[5].ToCharArray();
foreach (char tmp in tmpArray)
{
switch (tmp)
{
case '1':
sBuffer.Append("星期天");
break;
case '2':
sBuffer.Append("星期一");
break;
case '3':
sBuffer.Append("星期二");
break;
case '4':
sBuffer.Append("星期三");
break;
case '5':
sBuffer.Append("星期四");
break;
case '6':
sBuffer.Append("星期五");
break;
case '7':
sBuffer.Append("星期六");
break;
case '-':
sBuffer.Append("至");
break;
default:
sBuffer.Append(tmp);
break;
}
}
}
//解析日
if (!tmpCorns[3].Equals("?"))
{
if (!tmpCorns[3].Equals("*"))
{
sBuffer.Append(tmpCorns[3]).Append("日");
}
else
{
sBuffer.Append("每日");
}
}
//解析時
if (!tmpCorns[2].Equals("*"))
{
sBuffer.Append(tmpCorns[2]).Append("時");
}
else
{
sBuffer.Append("每時");
}
//解析分
if (!tmpCorns[1].Equals("*"))
{
sBuffer.Append(tmpCorns[1]).Append("分");
}
else
{
sBuffer.Append("每分");
}
//解析秒
if (!tmpCorns[0].Equals("*"))
{
sBuffer.Append(tmpCorns[0]).Append("秒");
}
else
{
sBuffer.Append("每秒");
}
}
return sBuffer.ToString();
}
#region 初始化Cron物件
/// <summary>
/// 指定秒位
/// </summary>
/// <param name="c">cron表示式</param>
/// <param name="str">秒位</param>
private static void Seconds(Cron c, string str)
{
if (str == "*")
{
for (int i = 0; i < 60; i++)
{
c.Seconds[i] = 1;
}
}
else if (str.Contains('-'))
{
int begin = int.Parse(str.Split('-')[0]);
int end = int.Parse(str.Split('-')[1]);
for (int i = begin; i <= end; i++)
{
c.Seconds[i] = 1;
}
}
else if (str.Contains('/'))
{
int begin = int.Parse(str.Split('/')[0]);
int interval = int.Parse(str.Split('/')[1]);
while (true)
{
c.Seconds[begin] = 1;
if ((begin + interval) >= 60)
break;
begin += interval;
}
}
else if (str.Contains(','))
{
for (int i = 0; i < str.Split(',').Length; i++)
{
c.Seconds[int.Parse(str.Split(',')[i])] = 1;
}
}
else
{
c.Seconds[int.Parse(str)] = 1;
}
}
/// <summary>
/// 指定分位
/// </summary>
/// <param name="c">cron表示式</param>
/// <param name="str">分位</param>
private static void Minutes(Cron c, string str)
{
if (str == "*")
{
for (int i = 0; i < 60; i++)
{
c.Minutes[i] = 1;
}
}
else if (str.Contains('-'))
{
int begin = int.Parse(str.Split('-')[0]);
int end = int.Parse(str.Split('-')[1]);
for (int i = begin; i <= end; i++)
{
c.Minutes[i] = 1;
}
}
else if (str.Contains('/'))
{
int begin = int.Parse(str.Split('/')[0]);
int interval = int.Parse(str.Split('/')[1]);
while (true)
{
c.Minutes[begin] = 1;
if ((begin + interval) >= 60)
break;
begin += interval;
}
}
else if (str.Contains(','))
{
for (int i = 0; i < str.Split(',').Length; i++)
{
c.Minutes[int.Parse(str.Split(',')[i])] = 1;
}
}
else
{
c.Minutes[int.Parse(str)] = 1;
}
}
/// <summary>
/// 指定小時位
/// </summary>
/// <param name="c">cron表示式</param>
/// <param name="str">小時位</param>
private static void Hours(Cron c, string str)
{
if (str == "*")
{
for (int i = 0; i < 24; i++)
{
c.Hours[i] = 1;
}
}
else if (str.Contains('-'))
{
int begin = int.Parse(str.Split('-')[0]);
int end = int.Parse(str.Split('-')[1]);
for (int i = begin; i <= end; i++)
{
c.Hours[i] = 1;
}
}
else if (str.Contains('/'))
{
int begin = int.Parse(str.Split('/')[0]);
int interval = int.Parse(str.Split('/')[1]);
while (true)
{
c.Hours[begin] = 1;
if ((begin + interval) >= 24)
break;
begin += interval;
}
}
else if (str.Contains(','))
{
for (int i = 0; i < str.Split(',').Length; i++)
{
c.Hours[int.Parse(str.Split(',')[i])] = 1;
}
}
else
{
c.Hours[int.Parse(str)] = 1;
}
}
/// <summary>
/// 指定月位
/// </summary>
/// <param name="c">cron表示式</param>
/// <param name="str">月位</param>
private static void Month(Cron c, string str)
{
if (str == "*")
{
for (int i = 0; i < 12; i++)
{
c.Month[i] = 1;
}
}
else if (str.Contains('-'))
{
int begin = int.Parse(str.Split('-')[0]);
int end = int.Parse(str.Split('-')[1]);
for (int i = begin; i <= end; i++)
{
c.Month[i - 1] = 1;
}
}
else if (str.Contains('/'))
{
int begin = int.Parse(str.Split('/')[0]);
int interval = int.Parse(str.Split('/')[1]);
while (true)
{
c.Month[begin - 1] = 1;
if ((begin + interval) >= 12)
break;
begin += interval;
}
}
else if (str.Contains(','))
{
for (int i = 0; i < str.Split(',').Length; i++)
{
c.Month[int.Parse(str.Split(',')[i]) - 1] = 1;
}
}
else
{
c.Month[int.Parse(str) - 1] = 1;
}
}
/// <summary>
/// 指定年位
/// </summary>
/// <param name="c">cron表示式</param>
/// <param name="str">年位</param>
private static void Year(Cron c, string str)
{
if (str == null || str == "*")
{
for (int i = 0; i < 80; i++)
{
c.Year[i] = 1;
}
}
else if (str.Contains('-'))
{
int begin = int.Parse(str.Split('-')[0]);
int end = int.Parse(str.Split('-')[1]);
for (int i = begin - 2019; i <= end - 2019; i++)
{
c.Year[i] = 1;
}
}
else
{
c.Year[int.Parse(str) - 2019] = 1;
}
}
/// <summary>
/// 指定天位
/// </summary>
/// <param name="c">cron表示式</param>
/// <param name="str">天位</param>
private static void Days(Cron c, string str, int len, DateTime now)
{
for (int i = 0; i < 7; i++)
{
c.Weeks[i] = 1;
}
if (str == "*" || str == "?")
{
for (int i = 0; i < len; i++)
{
c.Days[i] = 1;
}
}
else if (str.Contains('-'))
{
int begin = int.Parse(str.Split('-')[0]);
int end = int.Parse(str.Split('-')[1]);
for (int i = begin; i <= end; i++)
{
c.Days[i - 1] = 1;
}
}
else if (str.Contains('/'))
{
int begin = int.Parse(str.Split('/')[0]);
int interval = int.Parse(str.Split('/')[1]);
while (true)
{
c.Days[begin - 1] = 1;
if ((begin + interval) >= len)
break;
begin += interval;
}
}
else if (str.Contains(','))
{
for (int i = 0; i < str.Split(',').Length; i++)
{
c.Days[int.Parse(str.Split(',')[i]) - 1] = 1;
}
}
else if (str.Contains('L'))
{
int i = str.Replace("L", "") == "" ? 0 : int.Parse(str.Replace("L", ""));
c.Days[len - 1 - i] = 1;
}
else if (str.Contains('W'))
{
c.Days[len - 1] = 1;
}
else
{
c.Days[int.Parse(str) - 1] = 1;
}
}
/// <summary>
/// 指定星期位
/// </summary>
/// <param name="c">cron表示式</param>
/// <param name="str">星期位</param>
private static void Weeks(Cron c, string str, int len, DateTime now)
{
if (str == "*" || str == "?")
{
for (int i = 0; i < 7; i++)
{
c.Weeks[i] = 1;
}
}
else if (str.Contains('-'))
{
int begin = int.Parse(str.Split('-')[0]);
int end = int.Parse(str.Split('-')[1]);
for (int i = begin; i <= end; i++)
{
c.Weeks[i - 1] = 1;
}
}
else if (str.Contains(','))
{
for (int i = 0; i < str.Split(',').Length; i++)
{
c.Weeks[int.Parse(str.Split(',')[i]) - 1] = 1;
}
}
else if (str.Contains('L'))
{
int i = str.Replace("L", "") == "" ? 0 : int.Parse(str.Replace("L", ""));
if (i == 0)
{
c.Weeks[6] = 1;
}
else
{
c.Weeks[i - 1] = 1;
c.Days[GetLastWeek(i, now) - 1] = 1;
return;
}
}
else if (str.Contains('#'))
{
int i = int.Parse(str.Split('#')[0]);
int j = int.Parse(str.Split('#')[1]);
c.Weeks[i - 1] = 1;
c.Days[GetWeek(i - 1, j, now)] = 1;
return;
}
else
{
c.Weeks[int.Parse(str) - 1] = 1;
}
//week中初始化day,則說明day沒要求
for (int i = 0; i < len; i++)
{
c.Days[i] = 1;
}
}
#endregion
#region 方法
/// <summary>
/// 表示式是否是確切時間
/// </summary>
/// <param name="cron">表示式</param>
/// <returns></returns>
public static bool IsOrNoOne(string cron)
{
if (cron.Contains('-') || cron.Contains(',') || cron.Contains('/') || cron.Contains('*'))
{
return false;
}
else
{
return true;
}
}
/// <summary>
/// 獲取最後一個星期幾的day
/// </summary>
/// <param name="i">星期幾</param>
/// <param name="now">現在時間</param>
/// <returns></returns>
private static int GetLastWeek(int i, DateTime now)
{
DateTime d = now.AddDays(1 - now.Day).Date.AddMonths(1).AddSeconds(-1);
int DayOfWeek = ((((int)d.DayOfWeek) + 6) % 7) + 1;
int a = DayOfWeek >= i ? DayOfWeek - i : 7 + DayOfWeek - i;
return DateTime.DaysInMonth(now.Year, now.Month) - a;
}
/// <summary>
/// 獲取當月第幾個星期幾的day
/// </summary>
/// <param name="i">星期幾</param>
/// <param name="j">第幾周</param>
/// <param name="now">現在時間</param>
/// <returns></returns>
public static int GetWeek(int i, int j, DateTime now)
{
int day = 0;
DateTime d = new DateTime(now.Year, now.Month, 1);
int DayOfWeek = ((((int)d.DayOfWeek) + 6) % 7) + 1;
if (i >= DayOfWeek)
{
day = (7 - DayOfWeek + 1) + 7 * (j - 2) + i;
}
else
{
day = (7 - DayOfWeek + 1) + 7 * (j - 1) + i;
}
return day;
}
#endregion
}
}