/// <summary> /// 验证表达式字符串 /// </summary> /// <param name="columns">The columns.</param> public static void ValidateCronExpression(string[] columns) { if (columns == null) { throw new ArgumentNullException("columns"); } // 表达式字符串必须7列 if (columns.Length != 7) { CronExpressionValidationException.Throw("CronExpression must be 7 columns."); } // 每列字符必须为合法字符 foreach (string item in columns) { char[] charList = item.ToCharArray(); if (charList.Length <= 0) { CronExpressionValidationException.Throw("Every column length cannot be zero."); } // 每列字符必须为合法字符 foreach (char c in charList) { if (!ValidChars.Contains(c)) { CronExpressionValidationException.Throw("CronExpression chars are illegal."); } } // 某特殊字符在一列中只能出现一次 foreach (char special in ValidSpecialChars) { if (special == ',') { // ','不能连续出现 if (new String(charList).Contains(@",,")) { CronExpressionValidationException.Throw("CronExpression commas cannot be consecutive."); } // ','不能开始和结尾 if (new String(charList).StartsWith(@",", StringComparison.CurrentCulture) || new String(charList).EndsWith(@",", StringComparison.CurrentCulture)) { CronExpressionValidationException.Throw("CronExpression cannot start with or end with a comma."); } } else { // 特殊字符在一列中只能出现一次 if (charList.Where(p => p == special).Count() > 1) { CronExpressionValidationException.Throw("The special char must only appear once in CronExpression"); } // 特殊字符不能和','同时出现 if (charList.Where(p => p == special).Count() > 0 && charList.Where(p => p == ',').Count() > 0) { CronExpressionValidationException.Throw("The special char cannot appear with comma in CronExpression"); } if (special == '*') { // '*' '-' 不能同时出现 if (charList.Where(p => p == special).Count() > 0 && charList.Where(p => p == '-').Count() > 0) { CronExpressionValidationException.Throw("The special char '*' and '-' cannot appear simultaneously in CronExpression"); } } } } } // 每列中的值必须在合理范围内 for (int j = 0; j < columns.Length; j++) { char[] charList = columns[j].ToCharArray(); List <string> strList = new List <string>(charList.Length); for (int i = 0; i < charList.Length; i++) { strList.Add(new String(new char[] { charList[i] })); } string special = new String(ValidSpecialChars); for (int i = 0; i < strList.Count; i++) { if (!special.Contains(strList[i])) { while (true) { if ((i + 1 < strList.Count) && !special.Contains(strList[i + 1])) { strList[i] += strList[i + 1]; strList.RemoveAt(i + 1); } else { break; } } } } for (int i = 0; i < strList.Count; i++) { if (strList[i].Length == 1 && special.Contains(strList[i])) { // 纯特殊字符 } else { int parser = int.Parse(strList[i], CultureInfo.InvariantCulture); // 数字在范围内 switch (j) { case 0: // 秒 if (parser < 0 || parser > 59) { CronExpressionValidationException.Throw("Seconds", string.Format(CultureInfo.InvariantCulture, @"Must in range {0}-{1}.", 0, 59)); } break; case 1: // 分钟 if (parser < 0 || parser > 59) { CronExpressionValidationException.Throw("Minutes", string.Format(CultureInfo.InvariantCulture, @"Must in range {0}-{1}.", 0, 59)); } break; case 2: // 小时 if (parser < 0 || parser > 23) { CronExpressionValidationException.Throw("Hours", string.Format(CultureInfo.InvariantCulture, @"Must in range {0}-{1}.", 0, 23)); } break; case 3: // 日期 if (parser < 1 || parser > 31) { CronExpressionValidationException.Throw("Days", string.Format(CultureInfo.InvariantCulture, @"Must in range {0}-{1}.", 1, 31)); } break; case 4: // 月份 if (parser < 1 || parser > 12) { CronExpressionValidationException.Throw("Months", string.Format(CultureInfo.InvariantCulture, @"Must in range {0}-{1}.", 1, 12)); } break; case 5: // 星期 if (parser < 0 || parser > 6) { CronExpressionValidationException.Throw("Weekdays", string.Format(CultureInfo.InvariantCulture, @"Must in range {0}-{1}.", 0, 6)); } break; case 6: // 年份 if (parser < DateTime.Now.Year || parser > 2099) { CronExpressionValidationException.Throw("Years", string.Format(CultureInfo.InvariantCulture, @"Must in range {0}-{1}.", DateTime.Now.Year, 2099)); } break; default: CronExpressionValidationException.Throw("Must not reach here."); break; } } } } }
/// <summary> /// 编译分析时程表达式 /// </summary> private void BuildExpression() { // Format : "* * * * * * *" // 第1列表示秒数0~59 每 秒用*或者*/1表示 // 第2列表示分钟0~59 每分钟用*或者*/1表示 // 第3列表示小时0~23 每小时用*或者*/1表示 // 第4列表示日期1~31 每 天用*或者*/1表示 // 第5列表示月份1~12 每 月用*或者*/1表示 // 第6列表示星期0~6 每 天用*表示*/1表示 0表示星期天 // 第7列表示月份2000~2099 每年用*或者*/1表示 // * 代表任意 * 每个 // / 代表每隔 /2 每隔2 // - 代表区间 1-7 从1至7 // , 表示单独 1,3 1和3 // 例子: 1-10/2 * * 17 9 * * 描述为 9月17日,在每分钟内,从1-10秒间每隔2秒触发一次 if (string.IsNullOrEmpty(CronExpressionString)) { throw new FormatException("CronExpressionString bad format."); } // 分割字符串 string[] columns = CronExpressionString.Trim().Split(new char[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); // 验证表达式字符串 CronExpressionHelper.ValidateCronExpression(columns); // 构造基础数据 for (int i = 0; i < columns.Length; i++) { string item = columns[i]; switch (i) { case 0: // 秒 SecondsList = CronExpressionHelper.ParseSecondExpression(item); break; case 1: // 分钟 MinutesList = CronExpressionHelper.ParseMinuteExpression(item); break; case 2: // 小时 HoursList = CronExpressionHelper.ParseHourExpression(item); break; case 3: // 日期 DaysList = CronExpressionHelper.ParseDayExpression(item); break; case 4: // 月份 MonthsList = CronExpressionHelper.ParseMonthExpression(item); break; case 5: // 星期 WeekdaysList = CronExpressionHelper.ParseWeekdayExpression(item); break; case 6: // 年份 YearsList = CronExpressionHelper.ParseYearExpression(item); break; default: CronExpressionValidationException.Throw("Maybe validation parsed error."); break; } } if (SecondsList == null || MinutesList == null || HoursList == null || DaysList == null || MonthsList == null || WeekdaysList == null || YearsList == null) { CronExpressionValidationException.Throw("CronExpression parsed collections null."); } // 列表内数据从小到大排序 SecondsList.Sort(); MinutesList.Sort(); HoursList.Sort(); DaysList.Sort(); MonthsList.Sort(); WeekdaysList.Sort(); YearsList.Sort(); }
private static CronExpressionCollection ParseExpression(string item, string errorParam, int rangeMin, int rangeMax) { Regex regex1 = new Regex(@"^\*$"); // * Regex regex2 = new Regex(@"^\*\/([0-9]+)$"); // */1 Regex regex3 = new Regex(@"^([0-9]+)\-([0-9]+)$"); // 1-10 Regex regex4 = new Regex(@"^([0-9]+)\-([0-9]+)\/([0-9]+)$"); // 1-10/1 Regex regex5 = new Regex(@"^([0-9]+)\,.*([0-9]+)$"); // 1,2,3,4,5 Regex regex6 = new Regex(@"^([0-9]+)$"); // 0 Match m; CronExpressionCollection list = new CronExpressionCollection(); if ((m = regex1.Match(item)).Success) { for (int t = rangeMin; t <= rangeMax; t++) { list.Add(t); } } else if ((m = regex2.Match(item)).Success) { int period = int.Parse(m.Groups[1].ToString(), CultureInfo.InvariantCulture); if (period < rangeMin || period > rangeMax) { CronExpressionValidationException.Throw(errorParam, string.Format(CultureInfo.InvariantCulture, @"The period value must in range [{0}-{1}].", rangeMin, rangeMax)); } for (int t = rangeMin; t <= rangeMax; t++) { if (t % period == 0) { list.Add(t); } } } else if ((m = regex3.Match(item)).Success) { int begin = int.Parse(m.Groups[1].ToString(), CultureInfo.InvariantCulture); int end = int.Parse(m.Groups[2].ToString(), CultureInfo.InvariantCulture); if (begin < rangeMin || begin > rangeMax || end < rangeMin || end > rangeMax) { CronExpressionValidationException.Throw(errorParam, string.Format(CultureInfo.InvariantCulture, @"The begin and end value must in range [{0}-{1}].", rangeMin, rangeMax)); } if (begin <= end) { for (int t = begin; t <= end; t++) { list.Add(t); } } else { for (int t = begin; t <= rangeMax; t++) { list.Add(t); } for (int t = rangeMin; t <= end; t++) { if (!list.Contains(t)) { list.Add(t); } } } } else if ((m = regex4.Match(item)).Success) { int begin = int.Parse(m.Groups[1].ToString(), CultureInfo.InvariantCulture); int end = int.Parse(m.Groups[2].ToString(), CultureInfo.InvariantCulture); if (begin < rangeMin || begin > rangeMax || end < rangeMin || end > rangeMax) { CronExpressionValidationException.Throw(errorParam, string.Format(CultureInfo.InvariantCulture, @"The begin and end value must in range [{0}-{1}].", rangeMin, rangeMax)); } int period = int.Parse(m.Groups[3].ToString(), CultureInfo.InvariantCulture); if (period < rangeMin || period > rangeMax) { CronExpressionValidationException.Throw(errorParam, string.Format(CultureInfo.InvariantCulture, @"The period value must in range [{0}-{1}].", rangeMin, rangeMax)); } if (begin <= end) { for (int t = begin; t <= end; t++) { if (t % period == 0) { list.Add(t); } } } else { for (int t = begin; t <= rangeMax; t++) { if (t % period == 0) { list.Add(t); } } for (int t = rangeMin; t <= end; t++) { if (t % period == 0) { if (!list.Contains(t)) { list.Add(t); } } } } } else if ((m = regex5.Match(item)).Success) { // ','特殊处理 string[] splitsComma = item.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (var t in splitsComma) { int p = int.Parse(t, CultureInfo.InvariantCulture); if (p < rangeMin || p > rangeMax) { CronExpressionValidationException.Throw(errorParam, string.Format(CultureInfo.InvariantCulture, @"The comma splitted value must in range [{0}-{1}].", rangeMin, rangeMax)); } if (!list.Contains(p)) { list.Add(p); } } } else if ((m = regex6.Match(item)).Success) { int specified = int.Parse(m.Groups[1].ToString(), CultureInfo.InvariantCulture); if (specified < rangeMin || specified > rangeMax) { CronExpressionValidationException.Throw(errorParam, string.Format(CultureInfo.InvariantCulture, @"The specified value must in range [{0}-{1}].", rangeMin, rangeMax)); } list.Add(specified); } else { CronExpressionValidationException.Throw(errorParam, "Regex match error."); } return(list); }