实现思路
/**
* 是否连续n天出现,实现逻辑:指定时间段,获取该时间段内检索结果,根据时间段判断是否连续出现
* @param results
* [
* {
* "record": {
* "createTime": "2020-06-17T09:13:00.051Z",
* "id": "xxxxxxxxx",
* },
* "money": 22
* }
* ]
* 计算逻辑解释:假设有4条分别在前1,2,3,3天出现,则每天命中值为1<<1, 1<<2, 1<<3, 1<<3, 判断是否在前3天连续出现,
* 只需将每条记录的命中值联合取|,即2,4,8,8一起取| => 2|4|8|8,判断其结果与(2^3)-1是否相等即可。相等则连续,不等则非连续
*
* @return
*/
private boolean checkContinuousAppear(List<User> results) {
if (CollectionUtils.isEmpty(results)) {
throw new IllegalArgumentException("user results can't be null or empty!");
}
List<Integer> hitsCollection = results.stream().map(e -> hitBit(e.getRecord().getCreateTime())).collect(Collectors.toList());
Integer result = hitsCollection.stream().reduce((x1, x2) -> x1 | x2).get();
if (result.equals((1 << bizConfig.getContinuousOccurDays()) - 1)) {
return true;
}
return false;
}
/**
* 使用移位记录每个时间点是否命中
* 1 2 4 8 16 32 .....
*
* @param time
* @return
*/
private int hitBit(Instant time) {
LocalDate beforeNDate = LocalDate.now().minusDays(bizConfig.getContinuousOccurDays());
for (int i = 0; i < bizConfig.getContinuousOccurDays(); i++) {
Instant start, end;
if (i == 0) {
start = beforeNDate.atStartOfDay().toInstant(ZoneOffset.UTC);
} else {
start = beforeNDate.plusDays(i).atStartOfDay().toInstant(ZoneOffset.UTC);
}
end = beforeNDate.plusDays(i + 1).atStartOfDay().toInstant(ZoneOffset.UTC);
if (hitTime(time, start, end)) {
return 1<< i;
}
}
return 1;
}
/**
* 判断给定时间是否在时间区间内
* @param target
* @param start
* @param end
* @return
*/
private boolean hitTime(Instant target, Instant start, Instant end) {
if (target.toEpochMilli() >= start.toEpochMilli() && target.toEpochMilli() <= end.toEpochMilli()) {
return true;
}
return false;
}
此逻辑同样可推广至最近n小时,n分钟,n秒之类的判断