zoukankan      html  css  js  c++  java
  • 多个时间段统计

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

       一、明确需求

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

    开始时间
    结束时间
    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;
            }

       小结:

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

  • 相关阅读:
    如何去除ecshop标题和网站底部的Powered by ECShop
    ecshop标签
    安装Wamp后 Apache无法启动的解决方法
    wamp5中的apache不能启动,80端口被占用
    iOS UI-AlertView(警示框)和ActionSheet(选择框、操作表单)
    iOS UI-三种简单的动画设置
    iOS UI-IOS开发中Xcode的一些使用技巧
    iOS UI-九宫格
    iOS开发-开发文档安装
    iOS UI-创建空项目
  • 原文地址:https://www.cnblogs.com/victor-grace/p/7253652.html
Copyright © 2011-2022 走看看