CronExpression的输入输出,不符合预期问题解决

在使用org.drools.core.time.impl.CronExpression解析cron表达式时,遇到输出时间与预期不符的问题,主要原因是Java中时区设置与实际需求不一致。尽管尝试设置时区为本地时区,但因org.apache.tools.ant.util.DateUtils的format方法固定使用“GMT”时区导致问题持续。解决方案是自定义format方法,确保正确处理时区。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近需要使用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();
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值