需求背景:
需求是用户要创建一个任务计划在未来执行,要求在创建任务计划的时候判断选择的时间是否符合要求,否则不允许创建,创建的任务类型有两种,一种是单次,任务只执行一次;另一种是周期,从周一到周日,任务可以重复执行。如果选择单次执行,在选择时间的时候要判断选择的时间是否大于当前时间,如果小于当前时间那么当前任务将不在当天执行而是第二天的同一时间执行,反之是在当天执行。确定好哪一天执行之后还要判断那一天是否有已经创建的任务,如果有再判断同一天的两个时间点是否相差15分钟,如果在15分钟以内则时间发生碰撞不允许创建。如果选择周期执行,也要和已经创建的单次任务或者周期任务进行时间检查,另外两个任务如果不在同一天执行,还要判断在跨天时间点上是否相差15分钟,比如周一晚上23:55和周二凌晨00:05相差10分钟,也是属于时间冲突,那么基于这个需求写一下时间冲突的逻辑判断。
最终调用函数获取结果代码:
val checkResult = mViewModel.checkTime(
DateUtil.convertTimeToMinutes(startTimeStr),
StringUtil.convertStringToList(cycleDay),
vehReservationStandListBean,
mPreBean?.keyId,
)
if (!checkResult.first) {
"任务时间冲突,任务名称: ${checkResult.second}, 请重新选择时间点".showToast()
return false
}
具体检查时间冲突逻辑:
(入参格式:newStartTime="11:30" --> 11*60+30,代表字符串格式hh:mm转成分钟数
newCycleDay = 0,1,2,3,4,5,6,7的list集合,list代表一个已经创建的任务数据体集合)
/**
* 时间和周期是否允许创建
*/
fun checkTime(
newStartTime: Int?,
newCycleDay: List<Int>?,
list: List<ReservationStandSetOperationBean>?,
): Pair<Boolean, String?> {
if (newStartTime == null || newCycleDay == null) {
return Pair(false, "")
}
// 获取新任务的时间段
val newTimeSlots = DateUtil.getTimeSlots(newCycleDay, newStartTime)
for (model in list ?: emptyList()) {
val existingStartTime = DateUtil.convertTimeToMinutes(model.carReadyTime)
val existingCycleDay = StringUtil.convertStringToList(model.cycleDay)
if (existingStartTime == null || existingCycleDay == null) {
continue
}
// 获取已存在任务的时间段
val existingTimeSlots = DateUtil.getTimeSlots(existingCycleDay, existingStartTime)
// 检查时间段是否重叠
for (newSlot in newTimeSlots) {
for (existingSlot in existingTimeSlots) {
if (DateUtil.isTimeOverlap(newSlot, existingSlot)) {
return Pair(false, model.planName)
}
}
}
}
return Pair(true, "")
}
返回需要执行时间碰撞的数据集合:
// 获取任务的执行日期和时间段
fun getTimeSlots(days: List<Int>, startTime: Int): List<Triple<Int, Int, Int>> {
val result = mutableListOf<Triple<Int, Int, Int>>()
val minutesInDay = 24 * 60
val calendar = Calendar.getInstance()
val now = Date()
val currentTime =
calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE)
val weekday = calendar.get(Calendar.DAY_OF_WEEK)
val today = if (weekday == Calendar.SUNDAY) 7 else weekday - 1
for (day in days) {
//任务执行的日期
var adjustedDay = day
// 如果是单次任务 需要判断是否需要加一天
if (days == listOf(0)) {
// 如果任务的开始时间小于当前时间,推迟到明天
if (startTime <= currentTime) {
adjustedDay = if (today == 7) 1 else today + 1
} else {
adjustedDay = today
}
}
val endTime = startTime + 15
// 如果任务跨天
if (endTime >= minutesInDay) {
// 当天的时间段 日期,起始时间,终止时间
result.add(Triple(adjustedDay, startTime, minutesInDay))
// 第二天的时间段 日期,起始时间,终止时间
result.add(
Triple(
if (adjustedDay == 7) 1 else adjustedDay + 1,
0,
endTime - minutesInDay
)
)
} else {
// 不跨天的时间段 日期,起始时间,终止时间
result.add(Triple(adjustedDay, startTime, endTime))
}
}
return result
}
判断两个数据集合是否时间段有重叠:
// 检查时间段是否重叠
fun isTimeOverlap(slot1: Triple<Int, Int, Int>, slot2: Triple<Int, Int, Int>): Boolean {
// 如果日期不同,则不重叠
if (slot1.first != slot2.first) {
return false
}
// 检查时间段是否重叠
return slot1.second < slot2.third && slot2.second < slot1.third
}
结语:实际项目中用到的算法逻辑大多不复杂,这种需要动态计算任务执行时间,还要考虑跨天的逻辑比较少见,这里做个记录方便以后有类似逻辑可以做个代码参考。