多个时间段统计

    前段时间,用户提出一个关于时间段统计的需求,要求:存在多个时间段的任务,有任务的开始时间和结束时间,任务的时间可能是交叉进行,然后统计出所有任务完成的总时间。

   一、明确需求

    需求出来之后,首先要做的就是需求分析,而该任务的主要是通过时间段来进行统计,统计连续时间段差值的总和, 例如有如下时间段:      

开始时间
结束时间
2016-9-1
2016-9-1-15
2016-9-1
2016-9-1-13
2016-9-3
2016-9-1-17
2016-9-19
2016-9-1-21
2016-9-27
2016-9-1-29
    

    分析该5个时间段,发现前三个属于连续时间段,可以合并为一个时间段:2016-9-1~2016-9-17,这样我们可以很容易看出该5个时间段的总时间为:16+2+2=20。

   

   二、解决思路及过程:

     需求确定之后,开始着手解决。首先,对于时间差值计算,存在三种情况:交叉、相离、包含。如下图:

                           

   1)数据查询处理:

    每一种情形又分成了两种情况,这样很容易给我们的程序处理增加判断条件,所以我们可以在查询数据时做一些简化工作,比如我们可以让时间以开始时间、结束时间做升序排序,这样就可以使每种情形只保留一种情况(图中第②中情况就不存在了)。

   2)数据处理:

      在三种情形中,“包含”和“相交”这两种的时间差都属于最大时间-最小时间,而“分离”属于两者各自的时间差相加。涉及到多个时间段时,可以使用分组操作,先将前两种情形处理并相加,然后把第三种情况临时存放,最后进行递归调用。

   具体代码如下:      

 public int getDays(DataTable dt) {

            /*
             * 需求说明:
             *获取多个时间段的时间差:差值是连续时间段的差值。
             *比如:2016-9-1~2016-9-1-15、2016-9-1~2016-9-1-13、2016-9-3~2016-9-1-17、
             *2016-9-19~2016-9-1-21、2016-9-27~2016-9-1-29 求该5个时间段的差值
             *前3个时间段称为连续时间段,可以合并成2016-9-1~2016-9-17
             *所以总的时间差是:16+2+2=20
             *
             */

            /*
             * 解决思路:
             * 1、首先从数据库查询数据(开始时间与结束时间是两个字段),并以开始时间、结束时间从小到大排序,例如:select * from test_date order by begin_date ,end_date 
             * 2、时间段分析:分为三种情况
             *   (1)相交
             *   (2)包含
             *   (3)相离
             *  其中“相交”和“包含”都表示时间段为连续时间段,时间差=最大结束时间-最小结束时间
             * 3、递归  使用递归法来处理“相离”的情况 
             * 
             */

            //定义并创建table,用于存放相离的数据,进行递归
            DataTable dt1 = new DataTable();
            DataRow dr ;
            //定义列,跟查询出来的table列及列名一致
            dt1.Columns.Add("ID");
            dt1.Columns.Add("BEGIN_DATE");
            dt1.Columns.Add("END_DATE");

            //取出第一个时间段(本实例采用dataTable接收查询数据)
            //开始时间
            DateTime d = Convert.ToDateTime(dt.Rows[0][1].ToString());
            //结束时间
            DateTime d1 = Convert.ToDateTime(dt.Rows[0][2].ToString());
            //假设第一个时间段为最小开始时间和最大结束时间
            DateTime max = d1;
            DateTime min = d;          
            //第一个时间段的时间差
            int days = d1.Subtract(d).Days;
            //循环求值
            for (int i = 1; i < dt.Rows.Count; i++) {
                //s表示开始时间,e表示结束时间
                DateTime s = Convert.ToDateTime(dt.Rows[i ][1]);  
                DateTime e = Convert.ToDateTime(dt.Rows[i ][2]);
                
                //相交
                if (s < max && e >max)
                {   
                    //天数=上一次天数+(本次最大结束时间-上一最大结束时间)
                    days += e.Subtract(max).Days;
                    //本次最大结束时间更换为最大结束时间
                    max = e;
                }
                //包含
                else if (s < max && e <= max)
                {
                    //此时无需增加
                    days += 0;
                }
                //相离
                else if (s >= max)
                {
                   //将相离的数据存入table
                    dr = dt1.NewRow();
                    dr["ID"] = i;
                    dr["BEGIN_DATE"] = s;
                    dr["END_DATE"] = e;
                    dt1.Rows.Add(dr);
                }
                             
            }
            //判断相离的table中是否有数据
            if (dt1.Rows.Count > 0) {
                //存在相离数据,进行递归调用
                days += getDays(dt1);
            }
            return days;
        }

  三、优化

    功能实现之后,还得去考虑性能上的事情。在上述的实现过程中,在处理“相离”数据时,使用了递归调用。这样会增加内存的消耗,占用额外资源,给性能带来一定的负担。所以,需要对其进行优化。

    优化后代码:         

 public int getDay(DataTable dt) {
            
            //开始时间
            DateTime d = Convert.ToDateTime(dt.Rows[0][1].ToString());
            //结束时间
            DateTime d1 = Convert.ToDateTime(dt.Rows[0][2].ToString());
            //假设第一个时间段为最小开始时间和最大结束时间
            DateTime max = d1;
            DateTime min = d;
            //第一个时间段的时间差
            int days = d1.Subtract(d).Days;

            //循环求值
            for (int i = 1; i < dt.Rows.Count; i++)
            {
                //s表示开始时间,e表示结束时间
                DateTime s = Convert.ToDateTime(dt.Rows[i][1]);
                DateTime e = Convert.ToDateTime(dt.Rows[i][2]);

                //相交
                if (s < max && e > max)
                {
                    //天数=上一次天数+(本次最大结束时间-上一最大结束时间)
                    days += e.Subtract(max).Days;
                    //本次最大结束时间更换为最大结束时间
                    max = e;
                }
                //包含
                else if (s < max && e <= max)
                {
                    //此时无需增加
                    days += 0;
                }
                //相离
                else if (s >= max)
                {
                    days += e.Subtract(s).Days;
                    max = e;
                }

            }

            return days;
        }

   小结:

      在解决该需求时,开始的时候有点蒙,不知道从何入手。然后找到分情况进行处理,再到对其性能进行优化。这一过程其实就是一个很重要的学习历程,从迷茫到有思路,从解决到优化,跟我们现在的学习是一样的。问题总是耐不住琢磨的,琢磨透原理之后,在实现上就简单多了。

原文地址:https://www.cnblogs.com/victor-grace/p/7253652.html