Publish to github

This commit is contained in:
frodecarlsen 2014-05-30 10:34:35 +02:00
commit 67c88d9b62
5 changed files with 1191 additions and 0 deletions

22
.gitattributes vendored Normal file
View File

@ -0,0 +1,22 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

218
.gitignore vendored Normal file
View File

@ -0,0 +1,218 @@
#################
## Eclipse
#################
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
# Maven
target/
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#############
## Windows detritus
#############
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac crap
.DS_Store
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist/
build/
eggs/
parts/
var/
sdist/
develop-eggs/
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
*.class

59
pom.xml Normal file
View File

@ -0,0 +1,59 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>fc.cron</groupId>
<artifactId>cron</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<name>cron</name>
<url>https://github.com/frode-carlsen/cron</url>
<licenses>
<license>
<name>Apache License Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
</license>
</licenses>
<dependencies>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<fork>true</fork>
<source>1.7</source>
<target>1.7</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
</project>

View File

@ -0,0 +1,447 @@
/*
* Copyright (C) 2012 Frode Carlsen.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fc.cron;
import static org.joda.time.DateTimeConstants.DAYS_PER_WEEK;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.MutableDateTime;
/**
* Parser for unix-like cron expressions: Cron expressions allow specifying combinations of criteria for time
* such as: &quot;Each Monday-Friday at 08:00&quot; or &quot;Every last friday of the month at 01:30&quot;
* <p>
* A cron expressions consists of 6 mandatory fields separated by space. <br>
* These are:
*
* <table cellspacing="8">
* <tr>
* <th align="left">Field</th>
* <th align="left">&nbsp;</th>
* <th align="left">Allowable values</th>
* <th align="left">&nbsp;</th>
* <th align="left">Special Characters</th>
* </tr>
* <tr>
* <td align="left"><code>Seconds</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>0-59</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>, - * /</code></td>
* </tr>
* <tr>
* <td align="left"><code>Minutes</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>0-59</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>, - * /</code></td>
* </tr>
* <tr>
* <td align="left"><code>Hours</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>0-23</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>, - * /</code></td>
* </tr>
* <tr>
* <td align="left"><code>Day of month</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>1-31</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>, - * ? / L W</code></td>
* </tr>
* <tr>
* <td align="left"><code>Month</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>1-12 or JAN-DEC (note: english abbreviations)</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>, - * /</code></td>
* </tr>
* <tr>
* <td align="left"><code>Day of week</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>1-7 or MON-SUN (note: english abbreviations)</code></td>
* <td align="left">&nbsp;</th>
* <td align="left"><code>, - * ? / L #</code></td>
* </tr>
* </table>
*
* <P>
* '*' Can be used in all fields and means 'for all values'. E.g. &quot;*&quot; in minutes, means 'for all minutes'
* <P>
* '?' Ca be used in Day-of-month and Day-of-week fields. Used to signify 'no special value'. It is used when one want
* to specify something for one of those two fields, but not the other.
* <P>
* '-' Used to specify a time interval. E.g. &quot;10-12&quot; in Hours field means 'for hours 10, 11 and 12'
* <P>
* ',' Used to specify multiple values for a field. E.g. &quot;MON,WED,FRI&quot; in Day-of-week field means &quot;for
* monday, wednesday and friday&quot;
* <P>
* '/' Used to specify increments. E.g. &quot;0/15&quot; in Seconds field means &quot;for seconds 0, 15, 30, ad
* 45&quot;. And &quot;5/15&quot; in seconds field means &quot;for seconds 5, 20, 35, and 50&quot;. If '*' s specified
* before '/' it is the same as saying it starts at 0. For every field there's a list of values that can be turned on or
* off. For Seconds and Minutes these range from 0-59. For Hours from 0 to 23, For Day-of-month it's 1 to 31, For Months
* 1 to 12. &quot;/&quot; character helsp turn some of these values back on. Thus &quot;7/6&quot; in Months field
* specify just Month 7. It doesn't turn on every 6 month following, since cron fields never roll over
* <P>
* 'L' Can be used on Day-of-month and Day-of-week fields. It signifies last day of the set of allowed values. In
* Day-of-month field it's the last day of the month (e.g.. 31 jan, 28 feb (29 in leap years), 31 march, etc.). In
* Day-of-week field it's Sunday. If there's a prefix, this will be subtracted (5L in Day-of-month means 5 days before
* last day of Month: 26 jan, 23 feb, etc.)
* <P>
* 'W' Can be specified in Day-of-Month field. It specifies closest weekday (monday-friday). Holidays are not accounted
* for. &quot;15W&quot; in Day-of-Month field means 'closest weekday to 15 i in given month'. If the 15th is a Saturday,
* it gives Friday. If 15th is a Sunday, the it gives following Monday.
* <P>
* '#' Can be used in Day-of-Week field. For example: &quot;5#3&quot; means 'third friday in month' (day 5 = friday, #3
* - the third). If the day does not exist (e.g. &quot;5#5&quot; - 5th friday of month) and there aren't 5 fridays in
* the month, then it won't match until the next month with 5 fridays.
* <P>
* <b>Case-sensitivt</b> No fields are case-sensitive
* <P>
* <b>Dependencies between fields</b> Fields are always evaluated independently, but the expression doesn't match until
* the constraints of each field are met.Feltene evalueres Overlap of intervals are not allowed. That is: for
* Day-of-week field &quot;FRI-MON&quot; is invalid,but &quot;FRI-SUN,MON&quot; is valid
*
*/
public class CronExpression {
enum CronFieldType {
SECOND(0, 59, null),
MINUTE(0, 59, null),
HOUR(0, 23, null),
DAY_OF_MONTH(1, 31, null),
MONTH(1, 12, Arrays.asList("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC")),
DAY_OF_WEEK(1, 7, Arrays.asList("MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"));
final int from, to;
final List<String> names;
CronFieldType(int from, int to, List<String> names) {
this.from = from;
this.to = to;
this.names = names;
}
}
private final String expr;
private final SimpleField secondField;
private final SimpleField minuteField;
private final SimpleField hourField;
private final DayOfWeekField dayOfWeekField;
private final SimpleField monthField;
private final DayOfMonthField dayOfMonthField;
public CronExpression(String expr) {
if (expr == null) {
throw new IllegalArgumentException("expr is null");
}
this.expr = expr;
String[] parts = expr.split("\\s+");
if (parts.length != 6) {
throw new IllegalArgumentException(String.format("Invalid cronexpression [%s], expected %s felt, got %s"
, expr, CronFieldType.values().length, parts.length));
}
this.secondField = new SimpleField(CronFieldType.SECOND, parts[0]);
this.minuteField = new SimpleField(CronFieldType.MINUTE, parts[1]);
this.hourField = new SimpleField(CronFieldType.HOUR, parts[2]);
this.dayOfMonthField = new DayOfMonthField(parts[3]);
this.monthField = new SimpleField(CronFieldType.MONTH, parts[4]);
this.dayOfWeekField = new DayOfWeekField(parts[5]);
}
public DateTime nextTimeAfter(DateTime afterTime) {
MutableDateTime nextTime = new MutableDateTime(afterTime);
nextTime.setMillisOfSecond(0);
nextTime.secondOfDay().add(1);
while (true) { // day of week
while (true) { // month
while (true) { // day of month
while (true) { // hour
while (true) { // minute
while (true) { // second
if (secondField.matches(nextTime.getSecondOfMinute())) {
break;
}
nextTime.secondOfDay().add(1);
}
if (minuteField.matches(nextTime.getMinuteOfHour())) {
break;
}
nextTime.minuteOfDay().add(1);
nextTime.secondOfMinute().set(0);
}
if (hourField.matches(nextTime.getHourOfDay())) {
break;
}
nextTime.hourOfDay().add(1);
nextTime.minuteOfHour().set(0);
nextTime.secondOfMinute().set(0);
}
if (dayOfMonthField.matches(new LocalDate(nextTime))) {
break;
}
nextTime.addDays(1);
nextTime.setTime(0, 0, 0, 0);
}
if (monthField.matches(nextTime.getMonthOfYear())) {
break;
}
nextTime.addMonths(1);
nextTime.setDayOfMonth(1);
nextTime.setTime(0, 0, 0, 0);
}
if (dayOfWeekField.matches(new LocalDate(nextTime))) {
break;
}
nextTime.addDays(1);
nextTime.setTime(0, 0, 0, 0);
}
return nextTime.toDateTime();
}
@Override
public String toString() {
return getClass().getSimpleName() + "<" + expr + ">";
}
static class FieldPart {
private Integer from, to, increment;
private String modifier, incrementModifier;
}
abstract static class BasicField {
private static final Pattern CRON_FELT_REGEXP = Pattern
.compile("(?: # start of group 1\n"
+ " (?:(?<all>\\*)|(?<ignorer>\\?)|(?<last>L)) # globalt flag (L, ?, *)\n"
+ " | (?<start>[0-9]{1,2}|[a-z]{3,3}) # or start number or symbol\n"
+ " (?: # start of group 2\n"
+ " (?<mod>L|W) # modifier (L,W)\n"
+ " | -(?<end>[0-9]{1,2}|[a-z]{3,3}) # or end nummer or symbol (in range)\n"
+ " )? # end of group 2\n"
+ ") # end of group 1\n"
+ "(?:(?<inkmod>/|\\#)(?<ink>[0-9]{1,7}))? # increment and increment modifier (/ or \\#)\n"
, Pattern.CASE_INSENSITIVE | Pattern.COMMENTS);
final CronFieldType fieldType;
final List<FieldPart> parts = new ArrayList<>();
private BasicField(CronFieldType fieldType, String fieldExpr) {
this.fieldType = fieldType;
parse(fieldExpr);
}
private void parse(String fieldExpr) { // NOSONAR
String[] rangeParts = fieldExpr.split(",");
for (String rangePart : rangeParts) {
Matcher m = CRON_FELT_REGEXP.matcher(rangePart);
if (!m.matches()) {
throw new IllegalArgumentException("Invalid cron field '" + rangePart + "' for field [" + fieldType + "]");
}
String startNummer = m.group("start");
String modifier = m.group("mod");
String sluttNummer = m.group("end");
String inkrementModifier = m.group("inkmod");
String inkrement = m.group("ink");
FieldPart part = new FieldPart();
part.increment = 999;
if (startNummer != null) {
part.from = mapValue(startNummer);
part.modifier = modifier;
if (sluttNummer != null) {
part.to = mapValue(sluttNummer);
part.increment = 1;
} else if (inkrement != null) {
part.to = fieldType.to;
} else {
part.to = part.from;
}
} else if (m.group("all") != null) {
part.from = fieldType.from;
part.to = fieldType.to;
part.increment = 1;
} else if (m.group("ignorer") != null) {
part.modifier = m.group("ignorer");
} else if (m.group("last") != null) {
part.modifier = m.group("last");
} else {
throw new IllegalArgumentException("Invalid cron part: " + rangePart);
}
if (inkrement != null) {
part.incrementModifier = inkrementModifier;
part.increment = Integer.valueOf(inkrement);
}
validateRange(part);
validatePart(part);
parts.add(part);
}
}
protected void validatePart(FieldPart part) {
if (part.modifier != null) {
throw new IllegalArgumentException(String.format("Invalid modifier [%s]", part.modifier));
} else if (part.incrementModifier != null && !"/".equals(part.incrementModifier)) {
throw new IllegalArgumentException(String.format("Invalid increment modifier [%s]", part.incrementModifier));
}
}
private void validateRange(FieldPart part) {
if ((part.from != null && part.from < fieldType.from) || (part.to != null && part.to > fieldType.to)) {
throw new IllegalArgumentException(String.format("Invalid interval [%s-%s], must be %s<=_<=%s", part.from, part.to, fieldType.from,
fieldType.to));
} else if (part.from != null && part.to != null && part.from > part.to) {
throw new IllegalArgumentException(
String.format(
"Invalid interval [%s-%s]. Rolling periods are not supported (ex. 5-1, only 1-5) since this won't give a deterministic result. Must be %s<=_<=%s",
part.from, part.to, fieldType.from, fieldType.to));
}
}
protected Integer mapValue(String value) {
Integer idx;
if (fieldType.names != null && (idx = fieldType.names.indexOf(value.toUpperCase(Locale.getDefault()))) >= 0) {
return idx + 1;
}
return Integer.valueOf(value);
}
protected boolean matches(int val, FieldPart part) {
if (val >= part.from && val <= part.to && (val - part.from) % part.increment == 0) {
return true;
}
return false;
}
}
static class SimpleField extends BasicField {
SimpleField(CronFieldType fieldType, String fieldExpr) {
super(fieldType, fieldExpr);
}
public boolean matches(int val) {
if (val >= fieldType.from && val <= fieldType.to) {
for (FieldPart part : parts) {
if (matches(val, part)) {
return true;
}
}
}
return false;
}
}
static class DayOfWeekField extends BasicField {
DayOfWeekField(String fieldExpr) {
super(CronFieldType.DAY_OF_WEEK, fieldExpr);
}
boolean matches(LocalDate dato) {
for (FieldPart part : parts) {
if ("L".equals(part.modifier)) {
return dato.getDayOfWeek() == part.from && dato.getDayOfMonth() > (dato.dayOfMonth().getMaximumValue() - DAYS_PER_WEEK);
} else if ("#".equals(part.incrementModifier)) {
if (dato.getDayOfWeek() == part.from) {
int num = dato.getDayOfMonth() / 7;
return part.increment == (dato.getDayOfMonth() % 7 == 0 ? num : num + 1);
}
return false;
} else if (matches(dato.getDayOfWeek(), part)) {
return true;
}
}
return false;
}
@Override
protected Integer mapValue(String value) {
// Use 1-7 for weedays, but 0 will also represent sunday (linux practice)
return "0".equals(value) ? Integer.valueOf(7) : super.mapValue(value);
}
@Override
protected boolean matches(int val, FieldPart part) {
return "?".equals(part.modifier) || super.matches(val, part);
}
@Override
protected void validatePart(FieldPart part) {
if (part.modifier != null && Arrays.asList("L", "?").indexOf(part.modifier) == -1) {
throw new IllegalArgumentException(String.format("Invalid modifier [%s]", part.modifier));
} else if (part.incrementModifier != null && Arrays.asList("/", "#").indexOf(part.incrementModifier) == -1) {
throw new IllegalArgumentException(String.format("Invalid increment modifier [%s]", part.incrementModifier));
}
}
}
static class DayOfMonthField extends BasicField {
DayOfMonthField(String fieldExpr) {
super(CronFieldType.DAY_OF_MONTH, fieldExpr);
}
boolean matches(LocalDate dato) {
for (FieldPart part : parts) {
if ("L".equals(part.modifier)) {
return dato.getDayOfMonth() == (dato.dayOfMonth().getMaximumValue() - (part.from == null ? 0 : part.from));
} else if ("W".equals(part.modifier)) {
if (dato.getDayOfWeek() <= 5) {
if (dato.getDayOfMonth() == part.from) {
return true;
} else if (dato.getDayOfWeek() == 5) {
return dato.plusDays(1).getDayOfMonth() == part.from;
} else if (dato.getDayOfWeek() == 1) {
return dato.minusDays(1).getDayOfMonth() == part.from;
}
}
} else if (matches(dato.getDayOfMonth(), part)) {
return true;
}
}
return false;
}
@Override
protected void validatePart(FieldPart part) {
if (part.modifier != null && Arrays.asList("L", "W", "?").indexOf(part.modifier) == -1) {
throw new IllegalArgumentException(String.format("Invalid modifier [%s]", part.modifier));
} else if (part.incrementModifier != null && !"/".equals(part.incrementModifier)) {
throw new IllegalArgumentException(String.format("Invalid increment modifier [%s]", part.incrementModifier));
}
}
@Override
protected boolean matches(int val, FieldPart part) {
return "?".equals(part.modifier) || super.matches(val, part);
}
}
}

View File

@ -0,0 +1,445 @@
/*
* Copyright (C) 2012 Frode Carlsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fc.cron;
import static org.fest.assertions.Assertions.assertThat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import fc.cron.CronExpression;
import fc.cron.CronExpression.CronFieldType;
import fc.cron.CronExpression.DayOfMonthField;
import fc.cron.CronExpression.DayOfWeekField;
import fc.cron.CronExpression.SimpleField;
import org.joda.time.DateTime;
import org.joda.time.Hours;
import org.joda.time.LocalDate;
import org.junit.Test;
public class CronExpressionTest {
@Test
public void shall_parse_number() throws Exception {
SimpleField field = new SimpleField(CronFieldType.MINUTE, "5");
assertPossibleValues(field, 5);
}
private void assertPossibleValues(SimpleField field, Integer... values) {
Set<Integer> valid = values == null ? new HashSet<Integer>() : new HashSet<>(Arrays.asList(values));
for (int i = field.fieldType.from; i <= field.fieldType.to; i++) {
String errorText = i + ":" + valid;
if (valid.contains(i)) {
assertThat(field.matches(i)).as(errorText).isTrue();
} else {
assertThat(field.matches(i)).as(errorText).isFalse();
}
}
}
@Test
public void shall_parse_number_with_increment() throws Exception {
SimpleField field = new SimpleField(CronFieldType.MINUTE, "0/15");
assertPossibleValues(field, 0, 15, 30, 45);
}
@Test
public void shall_parse_range() throws Exception {
SimpleField field = new SimpleField(CronFieldType.MINUTE, "5-10");
assertPossibleValues(field, 5, 6, 7, 8, 9, 10);
}
@Test
public void shall_parse_range_with_increment() throws Exception {
SimpleField field = new SimpleField(CronFieldType.MINUTE, "20-30/2");
assertPossibleValues(field, 20, 22, 24, 26, 28, 30);
}
@Test
public void shall_parse_asterix() throws Exception {
SimpleField field = new SimpleField(CronFieldType.DAY_OF_WEEK, "*");
assertPossibleValues(field, 1, 2, 3, 4, 5, 6, 7);
}
@Test
public void shall_parse_asterix_with_increment() throws Exception {
SimpleField field = new SimpleField(CronFieldType.DAY_OF_WEEK, "*/2");
assertPossibleValues(field, 1, 3, 5, 7);
}
@Test
public void shall_ignore_field_in_day_of_week() throws Exception {
DayOfWeekField field = new DayOfWeekField("?");
assertThat(field.matches(new LocalDate())).isTrue();
}
@Test
public void shall_ignore_field_in_day_of_month() throws Exception {
DayOfMonthField field = new DayOfMonthField("?");
assertThat(field.matches(new LocalDate())).isTrue();
}
@Test(expected = IllegalArgumentException.class)
public void shall_give_error_if_invalid_count_field() throws Exception {
new CronExpression("* 3 *");
}
@Test(expected = IllegalArgumentException.class)
public void shall_give_error_if_minute_field_ignored() throws Exception {
SimpleField field = new SimpleField(CronFieldType.MINUTE, "?");
field.matches(1);
}
@Test(expected = IllegalArgumentException.class)
public void shall_give_error_if_hour_field_ignored() throws Exception {
SimpleField field = new SimpleField(CronFieldType.HOUR, "?");
field.matches(1);
}
@Test(expected = IllegalArgumentException.class)
public void shall_give_error_if_month_field_ignored() throws Exception {
SimpleField field = new SimpleField(CronFieldType.MONTH, "?");
field.matches(1);
}
@Test
public void shall_give_last_day_of_month_in_leapyear() throws Exception {
CronExpression.DayOfMonthField field = new DayOfMonthField("L");
assertThat(field.matches(new LocalDate(2012, 02, 29))).isTrue();
}
@Test
public void shall_give_last_day_of_month() throws Exception {
CronExpression.DayOfMonthField field = new DayOfMonthField("L");
assertThat(field.matches(new LocalDate().withDayOfMonth(new LocalDate().dayOfMonth().getMaximumValue()))).isTrue();
}
@Test
public void check_all() throws Exception {
assertThat(new CronExpression("* * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 01))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 02));
assertThat(new CronExpression("* * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 02))).isEqualTo(new DateTime(2012, 4, 10, 13, 02, 01));
assertThat(new CronExpression("* * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 59, 59))).isEqualTo(new DateTime(2012, 4, 10, 14, 00));
}
@Test(expected = IllegalArgumentException.class)
public void check_invalid_input() throws Exception {
new CronExpression(null);
}
@Test
public void check_second_number() throws Exception {
assertThat(new CronExpression("3 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 01))).isEqualTo(new DateTime(2012, 4, 10, 13, 01, 03));
assertThat(new CronExpression("3 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 01, 03))).isEqualTo(new DateTime(2012, 4, 10, 13, 02, 03));
assertThat(new CronExpression("3 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 59, 03))).isEqualTo(new DateTime(2012, 4, 10, 14, 00, 03));
assertThat(new CronExpression("3 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 23, 59, 03))).isEqualTo(new DateTime(2012, 4, 11, 00, 00, 03));
assertThat(new CronExpression("3 * * * * *").nextTimeAfter(new DateTime(2012, 4, 30, 23, 59, 03))).isEqualTo(new DateTime(2012, 5, 01, 00, 00, 03));
}
@Test
public void check_second_increment() throws Exception {
assertThat(new CronExpression("5/15 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 05));
assertThat(new CronExpression("5/15 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 05))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 20));
assertThat(new CronExpression("5/15 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 20))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 35));
assertThat(new CronExpression("5/15 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 35))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 50));
assertThat(new CronExpression("5/15 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 50))).isEqualTo(new DateTime(2012, 4, 10, 13, 01, 05));
// if rolling over minute then reset second (cron rules - increment affects only values in own field)
assertThat(new CronExpression("10/100 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 50)))
.isEqualTo(new DateTime(2012, 4, 10, 13, 01, 10));
assertThat(new CronExpression("10/100 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 01, 10)))
.isEqualTo(new DateTime(2012, 4, 10, 13, 02, 10));
}
@Test
public void check_second_list() throws Exception {
assertThat(new CronExpression("7,19 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 07));
assertThat(new CronExpression("7,19 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 07))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 19));
assertThat(new CronExpression("7,19 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 19))).isEqualTo(new DateTime(2012, 4, 10, 13, 01, 07));
}
@Test
public void check_second_range() throws Exception {
assertThat(new CronExpression("42-45 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 42));
assertThat(new CronExpression("42-45 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 42))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 43));
assertThat(new CronExpression("42-45 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 43))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 44));
assertThat(new CronExpression("42-45 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 44))).isEqualTo(new DateTime(2012, 4, 10, 13, 00, 45));
assertThat(new CronExpression("42-45 * * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00, 45))).isEqualTo(new DateTime(2012, 4, 10, 13, 01, 42));
}
@Test(expected = IllegalArgumentException.class)
public void check_second_invalid_range() throws Exception {
new CronExpression("42-63 * * * * *");
}
@Test(expected = IllegalArgumentException.class)
public void check_second_invalid_increment_modifier() throws Exception {
new CronExpression("42#3 * * * * *");
}
@Test
public void check_minute_number() throws Exception {
assertThat(new CronExpression("0 3 * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 01))).isEqualTo(new DateTime(2012, 4, 10, 13, 03));
assertThat(new CronExpression("0 3 * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 03))).isEqualTo(new DateTime(2012, 4, 10, 14, 03));
}
@Test
public void check_minute_increment() throws Exception {
assertThat(new CronExpression("0 0/15 * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 10, 13, 15));
assertThat(new CronExpression("0 0/15 * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 15))).isEqualTo(new DateTime(2012, 4, 10, 13, 30));
assertThat(new CronExpression("0 0/15 * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 30))).isEqualTo(new DateTime(2012, 4, 10, 13, 45));
assertThat(new CronExpression("0 0/15 * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 45))).isEqualTo(new DateTime(2012, 4, 10, 14, 00));
}
@Test
public void check_minute_list() throws Exception {
assertThat(new CronExpression("0 7,19 * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 10, 13, 07));
assertThat(new CronExpression("0 7,19 * * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 07))).isEqualTo(new DateTime(2012, 4, 10, 13, 19));
}
@Test
public void check_hour_number() throws Exception {
assertThat(new CronExpression("0 * 3 * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 01))).isEqualTo(new DateTime(2012, 4, 11, 03, 00));
assertThat(new CronExpression("0 * 3 * * *").nextTimeAfter(new DateTime(2012, 4, 11, 03, 00))).isEqualTo(new DateTime(2012, 4, 11, 03, 01));
assertThat(new CronExpression("0 * 3 * * *").nextTimeAfter(new DateTime(2012, 4, 11, 03, 59))).isEqualTo(new DateTime(2012, 4, 12, 03, 00));
}
@Test
public void check_hour_increment() throws Exception {
assertThat(new CronExpression("0 * 0/15 * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 10, 15, 00));
assertThat(new CronExpression("0 * 0/15 * * *").nextTimeAfter(new DateTime(2012, 4, 10, 15, 00))).isEqualTo(new DateTime(2012, 4, 10, 15, 01));
assertThat(new CronExpression("0 * 0/15 * * *").nextTimeAfter(new DateTime(2012, 4, 10, 15, 59))).isEqualTo(new DateTime(2012, 4, 11, 00, 00));
assertThat(new CronExpression("0 * 0/15 * * *").nextTimeAfter(new DateTime(2012, 4, 11, 00, 00))).isEqualTo(new DateTime(2012, 4, 11, 00, 01));
assertThat(new CronExpression("0 * 0/15 * * *").nextTimeAfter(new DateTime(2012, 4, 11, 15, 00))).isEqualTo(new DateTime(2012, 4, 11, 15, 01));
}
@Test
public void check_hour_list() throws Exception {
assertThat(new CronExpression("0 * 7,19 * * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 10, 19, 00));
assertThat(new CronExpression("0 * 7,19 * * *").nextTimeAfter(new DateTime(2012, 4, 10, 19, 00))).isEqualTo(new DateTime(2012, 4, 10, 19, 01));
assertThat(new CronExpression("0 * 7,19 * * *").nextTimeAfter(new DateTime(2012, 4, 10, 19, 59))).isEqualTo(new DateTime(2012, 4, 11, 07, 00));
}
@Test
public void check_hour_shall_run_25_times_in_DST_change_to_wintertime() throws Exception {
CronExpression cron = new CronExpression("0 1 * * * *");
DateTime start = new DateTime(2011, 10, 30, 0, 0, 0, 0);
DateTime slutt = start.toLocalDate().plusDays(1).toDateTimeAtStartOfDay();
DateTime tid = start;
assertThat(Hours.hoursBetween(start, slutt).getHours()).isEqualTo(25);
int count=0;
DateTime lastTime = tid;
while(tid.isBefore(slutt)){
DateTime nextTime = cron.nextTimeAfter(tid);
assertThat(nextTime.isAfter(lastTime)).isTrue();
lastTime = nextTime;
tid = tid.plusHours(1);
count++;
}
assertThat(count).isEqualTo(25);
}
@Test
public void check_hour_shall_run_23_times_in_DST_change_to_summertime() throws Exception {
CronExpression cron = new CronExpression("0 0 * * * *");
DateTime start = new DateTime(2011, 03, 27, 0, 0, 0, 0);
DateTime slutt = start.toLocalDate().plusDays(1).toDateTimeAtStartOfDay();
DateTime tid = start;
assertThat(Hours.hoursBetween(start, slutt).getHours()).isEqualTo(23);
int count=0;
DateTime lastTime = tid;
while(tid.isBefore(slutt)){
DateTime nextTime = cron.nextTimeAfter(tid);
assertThat(nextTime.isAfter(lastTime)).isTrue();
lastTime = nextTime;
tid = tid.plusHours(1);
count++;
}
assertThat(count).isEqualTo(23);
}
@Test
public void check_dayOfMonth_number() throws Exception {
assertThat(new CronExpression("0 * * 3 * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 5, 03, 00, 00));
assertThat(new CronExpression("0 * * 3 * *").nextTimeAfter(new DateTime(2012, 5, 03, 00, 00))).isEqualTo(new DateTime(2012, 5, 03, 00, 01));
assertThat(new CronExpression("0 * * 3 * *").nextTimeAfter(new DateTime(2012, 5, 03, 00, 59))).isEqualTo(new DateTime(2012, 5, 03, 01, 00));
assertThat(new CronExpression("0 * * 3 * *").nextTimeAfter(new DateTime(2012, 5, 03, 23, 59))).isEqualTo(new DateTime(2012, 6, 03, 00, 00));
}
@Test
public void check_dayOfMonth_increment() throws Exception {
assertThat(new CronExpression("0 0 0 1/15 * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 16, 00, 00));
assertThat(new CronExpression("0 0 0 1/15 * *").nextTimeAfter(new DateTime(2012, 4, 16, 00, 00))).isEqualTo(new DateTime(2012, 5, 01, 00, 00));
assertThat(new CronExpression("0 0 0 1/15 * *").nextTimeAfter(new DateTime(2012, 4, 30, 00, 00))).isEqualTo(new DateTime(2012, 5, 01, 00, 00));
assertThat(new CronExpression("0 0 0 1/15 * *").nextTimeAfter(new DateTime(2012, 5, 01, 00, 00))).isEqualTo(new DateTime(2012, 5, 16, 00, 00));
}
@Test
public void check_dayOfMonth_list() throws Exception {
assertThat(new CronExpression("0 0 0 7,19 * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 19, 00, 00));
assertThat(new CronExpression("0 0 0 7,19 * *").nextTimeAfter(new DateTime(2012, 4, 19, 00, 00))).isEqualTo(new DateTime(2012, 5, 07, 00, 00));
assertThat(new CronExpression("0 0 0 7,19 * *").nextTimeAfter(new DateTime(2012, 5, 07, 00, 00))).isEqualTo(new DateTime(2012, 5, 19, 00, 00));
assertThat(new CronExpression("0 0 0 7,19 * *").nextTimeAfter(new DateTime(2012, 5, 30, 00, 00))).isEqualTo(new DateTime(2012, 6, 07, 00, 00));
}
@Test
public void check_dayOfMonth_last() throws Exception {
assertThat(new CronExpression("0 0 0 L * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 30, 00, 00));
assertThat(new CronExpression("0 0 0 L * *").nextTimeAfter(new DateTime(2012, 2, 12, 00, 00))).isEqualTo(new DateTime(2012, 2, 29, 00, 00));
}
@Test
public void check_dayOfMonth_number_last_L() throws Exception {
assertThat(new CronExpression("0 0 0 3L * *").nextTimeAfter(new DateTime(2012, 4, 10, 13, 00))).isEqualTo(new DateTime(2012, 4, 30 - 3, 00, 00));
assertThat(new CronExpression("0 0 0 3L * *").nextTimeAfter(new DateTime(2012, 2, 12, 00, 00))).isEqualTo(new DateTime(2012, 2, 29 - 3, 00, 00));
}
@Test
public void check_dayOfMonth_closest_weekday_W() throws Exception {
// 9 - is weekday in may
assertThat(new CronExpression("0 0 0 9W * *").nextTimeAfter(new DateTime(2012, 5, 2, 00, 00))).isEqualTo(new DateTime(2012, 5, 9, 00, 00));
// 9 - is weekday in may
assertThat(new CronExpression("0 0 0 9W * *").nextTimeAfter(new DateTime(2012, 5, 8, 00, 00))).isEqualTo(new DateTime(2012, 5, 9, 00, 00));
// 9 - saturday, friday closest weekday in june
assertThat(new CronExpression("0 0 0 9W * *").nextTimeAfter(new DateTime(2012, 5, 9, 00, 00))).isEqualTo(new DateTime(2012, 6, 8, 00, 00));
// 9 - sunday, monday closest weekday in september
assertThat(new CronExpression("0 0 0 9W * *").nextTimeAfter(new DateTime(2012, 9, 1, 00, 00))).isEqualTo(new DateTime(2012, 9, 10, 00, 00));
}
@Test(expected = IllegalArgumentException.class)
public void check_dayOfMonth_invalid_modifier() throws Exception {
new CronExpression("0 0 0 9X * *");
}
@Test(expected = IllegalArgumentException.class)
public void check_dayOfMonth_invalid_increment_modifier() throws Exception {
new CronExpression("0 0 0 9#2 * *");
}
@Test
public void check_month_number() throws Exception {
assertThat(new CronExpression("0 0 0 1 5 *").nextTimeAfter(new DateTime(2012, 2, 12, 00, 00))).isEqualTo(new DateTime(2012, 5, 1, 00, 00));
}
@Test
public void check_month_increment() throws Exception {
assertThat(new CronExpression("0 0 0 1 5/2 *").nextTimeAfter(new DateTime(2012, 2, 12, 00, 00))).isEqualTo(new DateTime(2012, 5, 1, 00, 00));
assertThat(new CronExpression("0 0 0 1 5/2 *").nextTimeAfter(new DateTime(2012, 5, 1, 00, 00))).isEqualTo(new DateTime(2012, 7, 1, 00, 00));
// if rolling over year then reset month field (cron rules - increments only affect own field)
assertThat(new CronExpression("0 0 0 1 5/10 *").nextTimeAfter(new DateTime(2012, 5, 1, 00, 00))).isEqualTo(new DateTime(2013, 5, 1, 00, 00));
}
@Test
public void check_month_list() throws Exception {
assertThat(new CronExpression("0 0 0 1 3,7,12 *").nextTimeAfter(new DateTime(2012, 2, 12, 00, 00))).isEqualTo(new DateTime(2012, 3, 1, 00, 00));
assertThat(new CronExpression("0 0 0 1 3,7,12 *").nextTimeAfter(new DateTime(2012, 3, 1, 00, 00))).isEqualTo(new DateTime(2012, 7, 1, 00, 00));
assertThat(new CronExpression("0 0 0 1 3,7,12 *").nextTimeAfter(new DateTime(2012, 7, 1, 00, 00))).isEqualTo(new DateTime(2012, 12, 1, 00, 00));
}
@Test
public void check_month_list_by_name() throws Exception {
assertThat(new CronExpression("0 0 0 1 MAR,JUL,DEC *").nextTimeAfter(new DateTime(2012, 2, 12, 00, 00))).isEqualTo(new DateTime(2012, 3, 1, 00, 00));
assertThat(new CronExpression("0 0 0 1 MAR,JUL,DEC *").nextTimeAfter(new DateTime(2012, 3, 1, 00, 00))).isEqualTo(new DateTime(2012, 7, 1, 00, 00));
assertThat(new CronExpression("0 0 0 1 MAR,JUL,DEC *").nextTimeAfter(new DateTime(2012, 7, 1, 00, 00))).isEqualTo(new DateTime(2012, 12, 1, 00, 00));
}
@Test(expected = IllegalArgumentException.class)
public void check_month_invalid_modifier() throws Exception {
new CronExpression("0 0 0 1 ? *");
}
@Test
public void check_dayOfWeek_number() throws Exception {
assertThat(new CronExpression("0 0 0 * * 3").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 4, 00, 00));
assertThat(new CronExpression("0 0 0 * * 3").nextTimeAfter(new DateTime(2012, 4, 4, 00, 00))).isEqualTo(new DateTime(2012, 4, 11, 00, 00));
assertThat(new CronExpression("0 0 0 * * 3").nextTimeAfter(new DateTime(2012, 4, 12, 00, 00))).isEqualTo(new DateTime(2012, 4, 18, 00, 00));
assertThat(new CronExpression("0 0 0 * * 3").nextTimeAfter(new DateTime(2012, 4, 18, 00, 00))).isEqualTo(new DateTime(2012, 4, 25, 00, 00));
}
@Test
public void check_dayOfWeek_increment() throws Exception {
assertThat(new CronExpression("0 0 0 * * 3/2").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 4, 00, 00));
assertThat(new CronExpression("0 0 0 * * 3/2").nextTimeAfter(new DateTime(2012, 4, 4, 00, 00))).isEqualTo(new DateTime(2012, 4, 6, 00, 00));
assertThat(new CronExpression("0 0 0 * * 3/2").nextTimeAfter(new DateTime(2012, 4, 6, 00, 00))).isEqualTo(new DateTime(2012, 4, 8, 00, 00));
assertThat(new CronExpression("0 0 0 * * 3/2").nextTimeAfter(new DateTime(2012, 4, 8, 00, 00))).isEqualTo(new DateTime(2012, 4, 11, 00, 00));
}
@Test
public void check_dayOfWeek_list() throws Exception {
assertThat(new CronExpression("0 0 0 * * 1,5,7").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 2, 00, 00));
assertThat(new CronExpression("0 0 0 * * 1,5,7").nextTimeAfter(new DateTime(2012, 4, 2, 00, 00))).isEqualTo(new DateTime(2012, 4, 6, 00, 00));
assertThat(new CronExpression("0 0 0 * * 1,5,7").nextTimeAfter(new DateTime(2012, 4, 6, 00, 00))).isEqualTo(new DateTime(2012, 4, 8, 00, 00));
}
@Test
public void check_dayOfWeek_list_by_name() throws Exception {
assertThat(new CronExpression("0 0 0 * * MON,FRI,SUN").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 2, 00, 00));
assertThat(new CronExpression("0 0 0 * * MON,FRI,SUN").nextTimeAfter(new DateTime(2012, 4, 2, 00, 00))).isEqualTo(new DateTime(2012, 4, 6, 00, 00));
assertThat(new CronExpression("0 0 0 * * MON,FRI,SUN").nextTimeAfter(new DateTime(2012, 4, 6, 00, 00))).isEqualTo(new DateTime(2012, 4, 8, 00, 00));
}
@Test
public void check_dayOfWeek_last_friday_in_month() throws Exception {
assertThat(new CronExpression("0 0 0 * * 5L").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 27, 00, 00));
assertThat(new CronExpression("0 0 0 * * 5L").nextTimeAfter(new DateTime(2012, 4, 27, 00, 00))).isEqualTo(new DateTime(2012, 5, 25, 00, 00));
assertThat(new CronExpression("0 0 0 * * 5L").nextTimeAfter(new DateTime(2012, 2, 6, 00, 00))).isEqualTo(new DateTime(2012, 2, 24, 00, 00));
assertThat(new CronExpression("0 0 0 * * FRIL").nextTimeAfter(new DateTime(2012, 2, 6, 00, 00))).isEqualTo(new DateTime(2012, 2, 24, 00, 00));
}
@Test(expected = IllegalArgumentException.class)
public void check_dayOfWeek_invalid_modifier() throws Exception {
new CronExpression("0 0 0 * * 5W");
}
@Test(expected = IllegalArgumentException.class)
public void check_dayOfWeek_invalid_increment_modifier() throws Exception {
new CronExpression("0 0 0 * * 5?3");
}
@Test
public void check_dayOfWeek_shall_interpret_0_as_sunday() throws Exception {
assertThat(new CronExpression("0 0 0 * * 0").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 8, 00, 00));
assertThat(new CronExpression("0 0 0 * * 0L").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 29, 00, 00));
assertThat(new CronExpression("0 0 0 * * 0#2").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 8, 00, 00));
}
@Test
public void check_dayOfWeek_shall_interpret_7_as_sunday() throws Exception {
assertThat(new CronExpression("0 0 0 * * 7").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 8, 00, 00));
assertThat(new CronExpression("0 0 0 * * 7L").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 29, 00, 00));
assertThat(new CronExpression("0 0 0 * * 7#2").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 8, 00, 00));
}
@Test
public void check_dayOfWeek_nth_friday_in_month() throws Exception {
assertThat(new CronExpression("0 0 0 * * 5#3").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 4, 20, 00, 00));
assertThat(new CronExpression("0 0 0 * * 5#3").nextTimeAfter(new DateTime(2012, 4, 20, 00, 00))).isEqualTo(new DateTime(2012, 5, 18, 00, 00));
assertThat(new CronExpression("0 0 0 * * 7#1").nextTimeAfter(new DateTime(2012, 3, 30, 00, 00))).isEqualTo(new DateTime(2012, 4, 1, 00, 00));
assertThat(new CronExpression("0 0 0 * * 7#1").nextTimeAfter(new DateTime(2012, 4, 1, 00, 00))).isEqualTo(new DateTime(2012, 5, 6, 00, 00));
assertThat(new CronExpression("0 0 0 * * 3#5").nextTimeAfter(new DateTime(2012, 2, 6, 00, 00))).isEqualTo(new DateTime(2012, 2, 29, 00, 00)); // leapday
assertThat(new CronExpression("0 0 0 * * WED#5").nextTimeAfter(new DateTime(2012, 2, 6, 00, 00))).isEqualTo(new DateTime(2012, 2, 29, 00, 00)); // leapday
}
@Test(expected = IllegalArgumentException.class)
public void shall_not_not_support_rolling_period() throws Exception {
new CronExpression("* * 5-1 * * *");
}
}