最近需要使用cron表达式定期执行任务,因此使用到了CronExpression,对cron表达式进行解析,来获取下一次的执行时间。此处我使用的是org.drools.core.time.impl.CronExpression。
结果遇到了一个很坑的问题:
我想写一个解析demo,调试解析情况,但是发现输入的cron表达式,与输出的解析后的时间不符。。。。。。>_<
public static void main(String[] args) {
try {
long now = System.currentTimeMillis();
CronExpression cronExpression = new CronExpression("0 0 12 * * ?");
System.out.println(DateUtils
.format(cronExpression.getNextValidTimeAfter(new Date(now)), "yyyy-MM-dd hh:mm:ss"));
} catch (ParseException e) {
e.printStackTrace();
}
}
按照上面的cron表达式:“0 0 12 * * ?” 来说,由于执行时已经过了当天的12点了,应该输出的时间是:下一天的12点整,结果:
这件事情很莫名其妙。。。。。。但是看到时间会发现,和预期的时间正好差了8小时,这让人容易联想到TimeZone,看了下内部源码:
public Date getNextValidTimeAfter(Date date) {
return this.getTimeAfter(date);
}
......
protected Date getTimeAfter(Date afterTime) {
Calendar cl = Calendar.getInstance(this.getTimeZone());
afterTime = new Date(afterTime.getTime() + 1000L);
cl.setTime(afterTime);
cl.set(14, 0);
......
}
......
可以看到在解析时间时,会创建Calendar对象,其中会去尝试获取时区。
JAVA中的时区设置有3种方式:手动指定;jvm参数指定;环境变量指定。
假如都获取不到,就会使用默认的时区:“GMT”,也就是0时区(格林威治标准时间),而我所处的北京所在时区为东8区,因此解析出来的时间正好与期望时间相差8小时。
得知这一点以后,事情也就好解决了,只要将时区设置为当前所在地区的时区即可:
public static void main(String[] args) {
try {
long now = System.currentTimeMillis();
CronExpression cronExpression = new CronExpression("0 0 12 * * ?");
cronExpression.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
System.out.println(DateUtils
.format(cronExpression.getNextValidTimeAfter(new Date(now)), "yyyy-MM-dd hh:mm:ss"));
} catch (ParseException e) {
e.printStackTrace();
}
}
但修改后结果依然没有改变。。。。。。而且单独打印了jvm参数,我的时区其是没有问题的:
public static void main(String[] args) {
System.out.println(System.getProperty("user.timezone"));
}
苦思冥想,最后发现了真相。。。。。。我使用了org.apache.tools.ant.util.DateUtils将时间戳转换成时间字符串,这个DateUtils的format方法写死了时区为:“GMT”
public static String format(long date, String pattern) {
return format(new Date(date), pattern);
}
public static String format(Date date, String pattern) {
DateFormat df = createDateFormat(pattern);
return df.format(date);
}
......
private static DateFormat createDateFormat(String pattern) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
TimeZone gmt = TimeZone.getTimeZone("GMT");
sdf.setTimeZone(gmt);
sdf.setLenient(true);
return sdf;
}
......
老老实实自己写个format方法的话,就啥事也没有了。。。。。。
public static void main(String[] args) {
try {
long now = System.currentTimeMillis();
CronExpression cronExpression = new CronExpression("0 0 12 * * ?");
cronExpression.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
Date validTime = cronExpression.getNextValidTimeAfter(new Date(now));
System.out.println(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").print(validTime.getTime()));
} catch (ParseException e) {
e.printStackTrace();
}
}