Skip to content

Commit

Permalink
Merge pull request #1672 from productsupcom/JENKINS-9283
Browse files Browse the repository at this point in the history
[JENKINS-9283] Timezone Support for Scheduling
  • Loading branch information
oleg-nenashev committed May 21, 2015
2 parents 5540e93 + 6b4d044 commit 73e151f
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 8 deletions.
41 changes: 35 additions & 6 deletions core/src/main/java/hudson/scheduler/CronTab.java
Expand Up @@ -27,6 +27,7 @@

import java.io.StringReader;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -58,6 +59,11 @@ public final class CronTab {
*/
private String spec;

/**
* Optional timezone string for calendar
*/
private @CheckForNull String specTimezone;

public CronTab(String format) throws ANTLRException {
this(format,null);
}
Expand All @@ -81,15 +87,29 @@ public CronTab(String format, int line) throws ANTLRException {
* of not spreading it out at all.
*/
public CronTab(String format, int line, Hash hash) throws ANTLRException {
set(format, line, hash);
this(format, line, hash, null);
}

/**
* @param timezone
* Used to schedule cron in a differnt timezone. Null to use the default system
* timezone
*/
public CronTab(String format, int line, Hash hash, String timezone) throws ANTLRException {
set(format, line, hash, timezone);
}

private void set(String format, int line, Hash hash) throws ANTLRException {
set(format, line, hash, null);
}

private void set(String format, int line, Hash hash, String timezone) throws ANTLRException {
CrontabLexer lexer = new CrontabLexer(new StringReader(format));
lexer.setLine(line);
CrontabParser parser = new CrontabParser(lexer);
parser.setHash(hash);
spec = format;
specTimezone = timezone;

parser.startRule(this);
if((dayOfWeek&(1<<7))!=0) {
Expand All @@ -103,15 +123,24 @@ private void set(String format, int line, Hash hash) throws ANTLRException {
* Returns true if the given calendar matches
*/
boolean check(Calendar cal) {
if(!checkBits(bits[0],cal.get(MINUTE)))

Calendar checkCal = cal;

if(specTimezone != null && !specTimezone.isEmpty()) {
Calendar tzCal = Calendar.getInstance(TimeZone.getTimeZone(specTimezone));
tzCal.setTime(cal.getTime());
checkCal = tzCal;
}

if(!checkBits(bits[0],checkCal.get(MINUTE)))
return false;
if(!checkBits(bits[1],cal.get(HOUR_OF_DAY)))
if(!checkBits(bits[1],checkCal.get(HOUR_OF_DAY)))
return false;
if(!checkBits(bits[2],cal.get(DAY_OF_MONTH)))
if(!checkBits(bits[2],checkCal.get(DAY_OF_MONTH)))
return false;
if(!checkBits(bits[3],cal.get(MONTH)+1))
if(!checkBits(bits[3],checkCal.get(MONTH)+1))
return false;
if(!checkBits(dayOfWeek,cal.get(Calendar.DAY_OF_WEEK)-1))
if(!checkBits(dayOfWeek,checkCal.get(Calendar.DAY_OF_WEEK)-1))
return false;

return true;
Expand Down
37 changes: 35 additions & 2 deletions core/src/main/java/hudson/scheduler/CronTabList.java
Expand Up @@ -25,12 +25,16 @@

import antlr.ANTLRException;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.Collection;
import java.util.Vector;
import javax.annotation.CheckForNull;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
* {@link CronTab} list (logically OR-ed).
*
Expand Down Expand Up @@ -71,24 +75,52 @@ public String checkSanity() {
return null;
}

/**
* Checks if given timezone string is supported by TimeZone and returns
* the same string if valid, null otherwise
*/
public static @CheckForNull String getValidTimezone(String timezone) {
String[] validIDs = TimeZone.getAvailableIDs();
for (String str : validIDs) {
if (str != null && str.equals(timezone)) {
return timezone;
}
}
return null;
}

public static CronTabList create(String format) throws ANTLRException {
return create(format,null);
}

public static CronTabList create(String format, Hash hash) throws ANTLRException {
Vector<CronTab> r = new Vector<CronTab>();
int lineNumber = 0;
String timezone = null;

for (String line : format.split("\\r?\\n")) {
lineNumber++;
line = line.trim();

if(lineNumber == 1 && line.startsWith("TZ=")) {
timezone = getValidTimezone(line.replace("TZ=",""));
if(timezone != null) {
LOGGER.log(Level.CONFIG, "cron with timezone {0}", timezone);
} else {
LOGGER.log(Level.CONFIG, "invalid timezone {0}", line);
}
continue;
}

if(line.length()==0 || line.startsWith("#"))
continue; // ignorable line
try {
r.add(new CronTab(line,lineNumber,hash));
r.add(new CronTab(line,lineNumber,hash,timezone));
} catch (ANTLRException e) {
throw new ANTLRException(Messages.CronTabList_InvalidInput(line,e.toString()),e);
}
}

return new CronTabList(r);
}

Expand All @@ -115,5 +147,6 @@ public static CronTabList create(String format, Hash hash) throws ANTLRException
}
return nearest;
}


private static final Logger LOGGER = Logger.getLogger(CronTabList.class.getName());
}
14 changes: 14 additions & 0 deletions core/src/test/java/hudson/scheduler/CronTabTest.java
Expand Up @@ -27,6 +27,7 @@
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.TimeZone;
import java.util.GregorianCalendar;
import java.util.Locale;

Expand Down Expand Up @@ -301,4 +302,17 @@ public int next(int n) {
// ok
}
}

@Issue("JENKINS-9283")
@Test public void testTimezone() throws Exception {
CronTabList tabs = CronTabList.create("TZ=Australia/Sydney\nH * * * *\nH * * * *", Hash.from("seed"));
List<Integer> times = new ArrayList<Integer>();
for (int i = 0; i < 60; i++) {
if (tabs.check(new GregorianCalendar(2013, 3, 3, 11, i, 0))) {
times.add(i);
}
}
assertEquals("[35, 56]", times.toString());
}

}

0 comments on commit 73e151f

Please sign in to comment.