Skip to content

Commit

Permalink
Support years
Browse files Browse the repository at this point in the history
User can now optionally supply a year part to the cron expression in
the final position. Seconds part is also still optional so the
expression supports 5, 6, or 7 parts with heuristics to determine
whether seconds or years was supplied in the case of 6.
  • Loading branch information
Fabien Brooke committed Mar 24, 2013
1 parent 66f4aef commit f81065c
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 10 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ CronExpressionDescripter/obj
CronExpressionDescripter.Test/bin
CronExpressionDescripter.Test/obj
TestResults
*.suo
*.suo
.DS_Store
*.userprefs
30 changes: 30 additions & 0 deletions CronExpressionDescriptor.Test/TestFormats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,5 +258,35 @@ public void TestMinutesPastTheHour()
{
Assert.AreEqual("At 05 minutes past the hour", ExpressionDescriptor.GetDescription("0 5 0/1 * * ?"));
}

[TestMethod]
public void TestOneYearOnlyWithSeconds()
{
Assert.AreEqual("Every second, only in 2013", ExpressionDescriptor.GetDescription("* * * * * * 2013"));
}

[TestMethod]
public void TestOneYearOnlyWithoutSeconds()
{
Assert.AreEqual("Every minute, only in 2013", ExpressionDescriptor.GetDescription("* * * * * 2013"));
}

[TestMethod]
public void TestTwoYearsOnly()
{
Assert.AreEqual("Every minute, only in 2013 and 2014", ExpressionDescriptor.GetDescription("* * * * * 2013,2014"));
}

[TestMethod]
public void TestYearRange2()
{
Assert.AreEqual("At 12:23 PM, January through February, 2013 through 2014", ExpressionDescriptor.GetDescription("23 12 * JAN-FEB * 2013-2014"));
}

[TestMethod]
public void TestYearRange3()
{
Assert.AreEqual("At 12:23 PM, January through March, 2013 through 2015", ExpressionDescriptor.GetDescription("23 12 * JAN-MAR * 2013-2015"));
}
}
}
3 changes: 2 additions & 1 deletion CronExpressionDescriptor/DescriptionTypeEnum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum DescriptionTypeEnum
HOURS,
DAYOFWEEK,
MONTH,
DAYOFMONTH
DAYOFMONTH,
YEAR
}
}
23 changes: 20 additions & 3 deletions CronExpressionDescriptor/ExpressionDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public ExpressionDescriptor(string expression, Options options)
{
m_expression = expression;
m_options = options;
m_expressionParts = new string[6];
m_expressionParts = new string[7];
m_parsed = false;
}

Expand Down Expand Up @@ -66,6 +66,9 @@ public string GetDescription(DescriptionTypeEnum type)
case DescriptionTypeEnum.DAYOFWEEK:
description = GetDayOfWeekDescription();
break;
case DescriptionTypeEnum.YEAR:
description = GetYearDescription();
break;
default:
description = GetSecondsDescription();
break;
Expand Down Expand Up @@ -97,11 +100,13 @@ protected string GetFullDescription()
string dayOfMonthDesc = GetDayOfMonthDescription();
string monthDesc = GetMonthDescription();
string dayOfWeekDesc = GetDayOfWeekDescription();
string yearDesc = GetYearDescription();

description = string.Format("{0}{1}{2}",
description = string.Format("{0}{1}{2}{3}",
timeSegment,
(m_expressionParts[3] == "*" ? dayOfWeekDesc : dayOfMonthDesc),
monthDesc);
monthDesc,
yearDesc);

description = TransformVerbosity(description);
description = TransformCase(description);
Expand Down Expand Up @@ -345,6 +350,18 @@ protected string GetDayOfMonthDescription()
return description;
}

private string GetYearDescription()
{
string description = GetSegmentDescription(m_expressionParts[6],
string.Empty,
(s => new DateTime(Convert.ToInt32(s), 1, 1).ToString("yyyy")),
(s => string.Format(", every {0} years", s)),
(s => ", {0} through {1}"),
(s => ", only in {0}"));

return description;
}

protected string GetSegmentDescription(string expression,
string allDescription,
Func<string, string> getSingleItemDescription,
Expand Down
27 changes: 23 additions & 4 deletions CronExpressionDescriptor/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace CronExpressionDescriptor
{
Expand All @@ -18,7 +19,8 @@ public ExpressionParser(string expression, Options options)

public string[] Parse()
{
string[] parsed = new string[6];
// Initialize all elements of parsed array to empty strings
string[] parsed = new string[7].Select(el => "").ToArray();

if (string.IsNullOrEmpty(m_expression))
{
Expand All @@ -38,14 +40,30 @@ public string[] Parse()
}
else if (expressionPartsTemp.Length == 5)
{
//5 part cron so defualt seconds to empty and shift array
parsed[0] = string.Empty;
//5 part cron so shift array past seconds element
Array.Copy(expressionPartsTemp, 0, parsed, 1, 5);
}
else if (expressionPartsTemp.Length == 6)
{
//If last element ends with 4 digits, a year element has been supplied and no seconds element
Regex yearRegex = new Regex("\\d{4}$");
if (yearRegex.IsMatch(expressionPartsTemp[5]))
{
Array.Copy(expressionPartsTemp, 0, parsed, 1, 6);
}
else
{
Array.Copy(expressionPartsTemp, 0, parsed, 0, 6);
}
}
else if (expressionPartsTemp.Length == 7)
{
parsed = expressionPartsTemp;
}
else
{
throw new FormatException(string.Format("Error: Expression has too many parts ({0}). Expression must not have more than 7 parts.", expressionPartsTemp.Length));
}
}

NormalizeExpression(parsed);
Expand All @@ -68,7 +86,8 @@ private void NormalizeExpression(string[] expressionParts)
expressionParts[5] = expressionParts[5].Replace("1/", "*/"); //DOW

//convert */1 to *
for (int i = 0; i <= 5; i++)
int len = expressionParts.Length;
for (int i = 0; i < len; i++)
{
if (expressionParts[i] == "*/1")
{
Expand Down
32 changes: 31 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ License: MIT
**Features**

* Supports all cron expression special characters including * / , - ? L W, #.
* Supports 5 or 6 (w/ seconds) part cron expressions. Does NOT support Year in cron expression.
* Supports 5, 6 (w/ seconds or year), or 7 (w/ seconds and year) part cron expressions.
* Provides casing options (Sentence, Title, Lower, etc.)


Expand Down Expand Up @@ -231,4 +231,34 @@ If you want to get up and running quickly and just want the library, [visit the
public void TestMinutesPastTheHour()
{
Assert.AreEqual("At 05 minutes past the hour", ExpressionDescriptor.GetDescription("0 5 0/1 * * ?"));
}

[TestMethod]
public void TestOneYearOnlyWithSeconds()
{
Assert.AreEqual("Every second, only in 2013", ExpressionDescriptor.GetDescription("* * * * * * 2013"));
}
[TestMethod]
public void TestOneYearOnlyWithoutSeconds()
{
Assert.AreEqual("Every minute, only in 2013", ExpressionDescriptor.GetDescription("* * * * * 2013"));
}

[TestMethod]
public void TestTwoYearsOnly()
{
Assert.AreEqual("Every minute, only in 2013 and 2014", ExpressionDescriptor.GetDescription("* * * * * 2013,2014"));
}

[TestMethod]
public void TestYearRange2()
{
Assert.AreEqual("At 12:23 PM, January through February, 2013 through 2014", ExpressionDescriptor.GetDescription("23 12 * JAN-FEB * 2013-2014"));
}

[TestMethod]
public void TestYearRange3()
{
Assert.AreEqual("At 12:23 PM, January through March, 2013 through 2015", ExpressionDescriptor.GetDescription("23 12 * JAN-MAR * 2013-2015"));
}

0 comments on commit f81065c

Please sign in to comment.