项目需求
项目中遇到一个需求,为商品创建活动价,但活动是周期性活动,所以必须要判断当前时间是否满足活动周期
第一个版本
在第一个版本中周期很简单,按每周或每月进行周期活动,这个用时间表达式很容易实现,比如每周的星期二,星期五活动生效(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);
(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;
}