如何获取特定时间范围内的所有空闲时间段(java版)

业务场景说明:最近接到一个产品的需求,要求获取某个人,在某一天从上午9点到下午18点内超过两个小时的时间段范围列表,后面用于后继插入其他工作.....

感受:资本家的每个毛孔里都渗透着血和肮脏的东西....吐槽哈, 不要见怪,不要见外,大家都知道哈。

吐槽终究是吐槽,我还不是在被压榨的过程中缓慢前行么,不被压榨,咋生活呀。

本次实现其实是在排班系统存在的前提下,可以获取某人未来的排班信息,用于后期做分段切割的基础数据。下图就是比较直白的展示了24小时内全部空余时间和工作时间的线路图。

那应该如何实现呢?应该如何取出所有的时间段?开始和结束时间如何限制?2h又是如何让计算呢?

我觉得应该需求三个参数去实现,分别是 unitTimeList (占用的时间列表)beginTime (开始时间)endTime(结束时间)。 用开始时间和结束时间确定时间轴,占用时间列表是需要在时间轴中去除的。因此通过这三个参数就能完成上面业务了。

timeService.getIntervalTime(unitTimeList, startTime, endTime);

可是这个函数应该如何实现里面的服务呢 ?

  • 获取日期区间内每一天的开始和结束时间戳
  • 根据日期分组
  • 设置unitTimeList 到时间轴里面,如果当日无占用列表数据,那么返回一整天
  • 否则,根据开始时间排序
  • 如果当前日期的开始时间与结束时间都在范围内,说明,这天排满了,返回就成。

详细实现还是请跑下面的代码吧...不太好解释...像是算法这玩意一样,有时还真是要一步步跑,才能懂呀。

这次用到两个包分别如下:

  <dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>
    </dependencies>

 用到的实体 UnitTime、 DateTimeStamp

public class UnitTime {
    /**
     * 当天日期 YYYY-MM-dd
     */
    private String dateTime;
    /**
     * 开始时间戳
     */
    private Long startDate;
    /**
     * 结束时间戳
     */
    private Long endDate;

    public UnitTime() {
    }
}
public class DateTimeStamp {
    private Long startTimeStamp;
    private Long endTimeStamp;

    public DateTimeStamp() {
    }

    public Long getStartTimeStamp() {
        return startTimeStamp;
    }

    public void setStartTimeStamp(Long startTimeStamp) {
        this.startTimeStamp = startTimeStamp;
    }

    public Long getEndTimeStamp() {
        return endTimeStamp;
    }

    public void setEndTimeStamp(Long endTimeStamp) {
        this.endTimeStamp = endTimeStamp;
    }
}

具体的实现类 

public class TimeServiceImpl {
    /**
     * 获取开始日期到结束日期的空闲时间
     *
     * @param unitTimeList 占用的时间列表
     * @param beginTime    开始时间
     * @param endTime      结束时间
     * @return
     */
    public Map<String, List<DateTimeStamp>> getIntervalTime(List<UnitTime> unitTimeList, String beginTime, String endTime) throws ParseException {
        Map<String, List<DateTimeStamp>> resultMap = Maps.newTreeMap();

        // 获取日期区间内每一天的开始和结束时间戳
        Map<String, DateTimeStamp> timeStampMap = DateUtil.getAllTimeStamp(beginTime, endTime);
        //根据日期分组
        Map<String, List<UnitTime>> unitTimeMap = unitTimeList.stream().collect(Collectors.groupingBy(UnitTime::getDateTime));
        for (Map.Entry<String, DateTimeStamp> entry : timeStampMap.entrySet()) {
            // currentUnitTimeList 当天的占用时间
            List<UnitTime> currentUnitTimeList = unitTimeMap.get(entry.getKey());
            List<DateTimeStamp> subList = Lists.newArrayList();
            Long startTs = entry.getValue().getStartTimeStamp();
            Long endTs = entry.getValue().getEndTimeStamp();
            if (currentUnitTimeList.size() == 0) {
                // 当天没有占用时间 则设置一整天的时间
                DateTimeStamp resultTs = new DateTimeStamp();
                resultTs.setStartTimeStamp(startTs);
                resultTs.setEndTimeStamp(endTs);
                subList.add(resultTs);
                resultMap.put(entry.getKey(), subList);
                continue;
            }
            // 根据开始时间排序
            currentUnitTimeList.sort(Comparator.comparing(UnitTime::getStartDate));
            for (UnitTime u : currentUnitTimeList) {
                if (u.getStartDate().longValue() == startTs && u.getEndDate().longValue() == endTs) {
                    // 一整天全部占满
                    break;
                }
                if (u.getStartDate().longValue() > startTs) {
                    // 当前占用的开始时间戳大于当天的开始时间戳 则当天开始时间到当前的开始时间为空闲
                    DateTimeStamp resultTs = new DateTimeStamp();
                    resultTs.setStartTimeStamp(startTs);
                    resultTs.setEndTimeStamp(u.getStartDate().longValue());
                    subList.add(resultTs);
                    if (u.getEndDate().longValue() < endTs) {
                        // 当前结束时间戳小于当天结束时间戳 下一次循环的开始时间等于当前的结束时间
                        startTs = u.getEndDate().longValue();
                        if (currentUnitTimeList.get(currentUnitTimeList.size() - 1).getEndDate().longValue() == startTs) {
                            // 当前为数组最后一个元素 则当前结束时间到当天结束时间空闲
                            DateTimeStamp resultLastTs = new DateTimeStamp();
                            resultLastTs.setStartTimeStamp(startTs);
                            resultLastTs.setEndTimeStamp(endTs);
                            subList.add(resultLastTs);
                        }
                    } else {
                        break;
                    }
                } else {
                    startTs = u.getEndDate().longValue();
                }
            }
            resultMap.put(entry.getKey(), subList);
        }
        return resultMap;
    }
}

用到的工具类 

public class DateUtil {

    private static String LONGDATE_DATE = "yyyy-MM-dd";

    public static Calendar getCalendar(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    /**
     * 时间是否在本月内
     *
     * @param time    时间戳
     * @param pattern 匹配格式
     * @return
     */
    public static boolean isThisTime(long time, String pattern) {
        Date date = new Date(time);
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        String param = sdf.format(date);
        String now = sdf.format(new Date());
        if (param.equals(now)) {
            return true;
        }
        return false;
    }

    /**
     * 获取时间段每天开始和结束的时间戳
     *
     * @param beginTime
     * @param endTime
     * @return
     * @throws ParseException
     */
    public static Map<String, DateTimeStamp> getAllTimeStamp(String beginTime, String endTime) throws ParseException {
        Map<String, DateTimeStamp> allTimeStamp = Maps.newHashMap();
        SimpleDateFormat sdf = new SimpleDateFormat(LONGDATE_DATE);
        Calendar cal = getCalendar(sdf.parse(beginTime));
        // 判断是否是同一天
        if (isDay(beginTime, endTime)) {
            setTimeStampList(allTimeStamp, cal);
            return allTimeStamp;
        }

        //获取每天的开始和结束日期
        setTimeStampList(allTimeStamp, cal);
        while (true) {
            cal.add(Calendar.DAY_OF_MONTH, 1);
            if (sdf.parse(endTime).after(cal.getTime())) {
                setTimeStampList(allTimeStamp, cal);
            } else {
                break;
            }
        }
        setTimeStampList(allTimeStamp, cal);
        return allTimeStamp;
    }

    /**
     * 判断两个人日期是否为同一天
     *
     * @param beginDate
     * @param endDate
     * @return
     * @throws ParseException
     */
    public static boolean isDay(String beginDate, String endDate) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(LONGDATE_DATE);
        return DateUtils.isSameDay(sdf.parse(beginDate), sdf.parse(endDate));
    }

    private static void setTimeStampList(Map<String, DateTimeStamp> allTimeStamp, Calendar cal) {
        DateTimeStamp dateTimeStamp = new DateTimeStamp();
        long beginSt = getFirstTime(cal).getTime();
        dateTimeStamp.setStartTimeStamp(beginSt);
        long endSt = getLastTime(cal).getTime();
        dateTimeStamp.setEndTimeStamp(endSt);
        String result = new SimpleDateFormat("yyyy-MM-dd").format(new Date(beginSt));
        allTimeStamp.put(result, dateTimeStamp);
    }

    /**
     * 获取当天起始时间
     *
     * @param todayStart
     * @return
     */
    public static Date getFirstTime(Calendar todayStart) {
        todayStart.set(Calendar.HOUR_OF_DAY, 9);
        todayStart.set(Calendar.MINUTE, 0);
        todayStart.set(Calendar.SECOND, 0);
        todayStart.set(Calendar.MILLISECOND, 0);
        return todayStart.getTime();
    }

    /**
     * 获取当天结束时间
     *
     * @param todayEnd
     * @return
     */
    public static Date getLastTime(Calendar todayEnd) {
        todayEnd.set(Calendar.HOUR_OF_DAY, 18);
        todayEnd.set(Calendar.MINUTE, 0);
        todayEnd.set(Calendar.SECOND, 0);
        todayEnd.set(Calendar.MILLISECOND, 0);
        return todayEnd.getTime();
    }
}

可以用测试类测试一下 

public class TimeTest {
    public static void main(String[] args) {
        List<UnitTime> unitTimeList = Lists.newArrayList();
        UnitTime unitTime = new UnitTime();
        // 2019-11-03 09:00:00 - 2019-11-03 11:00:00
        unitTime.setDateTime("2019-11-03");
        unitTime.setStartDate(1572742800L);
        unitTime.setEndDate(1572750000L);
        unitTimeList.add(unitTime);

        // 2019-11-03 13:20:00 - 2019-11-03 13:50:00
        UnitTime unitTime2 = new UnitTime();
        unitTime2.setDateTime("2019-11-03");
        unitTime2.setStartDate(1572758400L);
        unitTime2.setEndDate(1572760200L);
        unitTimeList.add(unitTime2);

        // 2019-11-03 14:30:00 - 2019-11-03 18:00:00
        UnitTime unitTime3 = new UnitTime();
        unitTime3.setDateTime("2019-11-03");
        unitTime3.setStartDate(1572762600L);
        unitTime3.setEndDate(1572775200L);
        unitTimeList.add(unitTime3);

        TimeServiceImpl timeService = new TimeServiceImpl();

        Map<String, List<DateTimeStamp>> intervalMap = null;
        try {
            intervalMap = timeService.getIntervalTime(unitTimeList, "2019-11-03", "2019-11-03");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println(intervalMap);
    }

最终的结果: 

 

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值