如何计算时间范围内满足活动周期的日期

项目需求

项目中遇到一个需求,为商品创建活动价,但活动是周期性活动,所以必须要判断当前时间是否满足活动周期

第一个版本

在第一个版本中周期很简单,按每周或每月进行周期活动,这个用时间表达式很容易实现,比如每周的星期二,星期五活动生效(0 0 0 ? ? 2,5),或者每月的3号,5号生效(0 0 0 ? 3,5 ?),怎么看当前时间是否满足呢?代码如下

CronExpression cronExpression = new CronExpression("0 0 0 ? 3,5 ?");
boolean cron = cronExpression.isSatisfiedBy(DateUtil.getDateFormat(new Date(), "yyyy-MM-dd HH:mm:ss"));

很轻松便能判断当前时间是否在活动表达式范围内

当然cron同时也提供了获取时间范围内所有满足条件的时间

List<Date> list = new ArrayList();
CronTrigger trigger = (CronTrigger)TriggerBuilder.newTrigger().startAt(startDate).endAt(endDate).withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();

for(Date time0 = trigger.getStartTime(); time0 != null; time0 = trigger.getFireTimeAfter(time0)) {
      list.add(time0);
}

第二个版本

然而在一个迭代后需求升级了,周期变成了每隔几周或每个几月进行周期活动,例如每隔三周的星期二,四,六进行活动,或者每隔两月的三号,十号,二十号进行活动。这样一来cron表达式就满足不了了,相信我,我试了各种方法。。。

这就有点麻烦了,既然软的不行,那就来硬的吧,只好硬上代码了。

思路分析

没有了现成的工具来判断当前时间是否满足,那么只能把活动时间范围内所有满足条件的时间算出来了。

(1) 时间问题一直都是开发中最令人头疼的东西,所以在JDK1.8新特性中提供了更便捷的时间日期API
LocalDateTime提供了很多便捷的方法诸如

//获取当前时间
LocalDateTime localDateTime = LocalDateTime.now();
//获取指定时间
LocalDateTime localDateTime = LocalDateTime.of(2018, 1, 13, 9, 43, 20);
//加两周
localDateTime.plusWeeks(2);
//减两月
localDateTime.minusMonths(2);

LocalDateTime工具类更多细节

(2) 相信各位看到这里已经有思路了,但是在开发中还是有很多令人头疼的细节。

思路:获取活动开始后第一周的所有日期,然后分别加周数(周期频率)直到超过活动结束时间

坑:有时候活动第一周日期并不完全,比如每隔三周的星期二,三,五,六执行活动,而活动开始时间恰好是周四,所以这时候不仅要向后算出周五周六的日期,还要向前算出周二周三的日期,并且这两天不能算入活动日期内。

代码实现

由于不需要cron表达式,所以我自定义了表达式
每隔几周/月-周/月(1/2)-日期/星期 例:3-1-2,3,6

最后直接上代码

    public List<Date> getAllDate(String timeExpression, Date startDate, Date endDate){

        //获取具体星期数/日期
        String substring = timeExpression.substring(4, timeExpression.length());
        List<String> strings = Arrays.asList(substring.split(","));

        int monthOrWeek = timeExpression.charAt(2) - 48;
        int each = timeExpression.charAt(0) - 48;
        //存储满足条件的日期
        List<Date> list = new ArrayList<>();
        //存储第一周/第一月的所有日期
        List<Date> listcheck = new ArrayList<>();

        //开始时间转LocalDateTime
        LocalDateTime ldt = startDate.toInstant()
                .atZone( ZoneId.systemDefault() )
                .toLocalDateTime();
        LocalDateTime ldt1 = ldt.minusDays(1);
        Date afterDate = startDate;
        Date beforeDate = Date.from(ldt1.atZone( ZoneId.systemDefault()).toInstant());
        //计算第一周或第一月的日期
        while (true){

            Integer day ;
            if(monthOrWeek == 1){
                day = ldt.getDayOfWeek().getValue();
            }else {
                day = ldt.getDayOfMonth();
            }

            if(strings.contains(day.toString())){
                list.add(afterDate);
                listcheck.add(afterDate);
            }

            ldt = ldt.plusDays(1);
            afterDate = Date.from(ldt.atZone( ZoneId.systemDefault()).toInstant());
            if(strings.get(strings.size()-1).equals(day.toString())){
                break;
            }
        }
        
        if(listcheck.size() != strings.size()){
            while (true){

                Integer day ;
                if(monthOrWeek == 1){
                    day = ldt1.getDayOfWeek().getValue();
                }else {
                    day = ldt1.getDayOfMonth();
                }

                if(strings.contains(day.toString())){
                    listcheck.add(beforeDate);
                }

                ldt1 = ldt1.minusDays(1);
                beforeDate = Date.from(ldt1.atZone( ZoneId.systemDefault()).toInstant());
                if(strings.get(0).equals(day.toString())){
                    break;
                }
            }
        }

        //获取全部数据
        listcheck = listcheck.stream().sorted()
                .collect(Collectors.toList());
        List<Date> newList = new ArrayList<>();
        LocalDateTime ldt2 ;
        LocalDateTime ldt3 ;
        Date nowDate ;
        boolean flag = false ;
        while (true){

            for (int i = 0; i < listcheck.size(); i++) {
                ldt2 = listcheck.get(i).toInstant()
                        .atZone( ZoneId.systemDefault() )
                        .toLocalDateTime();
                if(monthOrWeek == 1){
                    ldt3 = ldt2.plusWeeks(each);
                }else {
                    ldt3 = ldt2.plusMonths(each);
                }
                nowDate = Date.from(ldt3.atZone( ZoneId.systemDefault()).toInstant());
                if(nowDate.after(endDate)){
                    flag = true ;
                }else {
                    listcheck.set(i,nowDate);
                    newList.add(nowDate);
                }
            }

            if(flag){
                break;
            }
            
        }

        list.addAll(newList);
        return list;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值