时间控制模块

来源:互联网 发布:淘宝手办景品 编辑:程序博客网 时间:2024/05/01 12:31

在一些周期使用的系统中往往需要判断当前时间段是否在设定的时间段内,并且要根据当前时间段判断出系统操作的具体时间。这样讲有些抽象,其中比较典型的例子就是“绩效考核系统”中对时间的管理。绩效考核往往存在考核周期,如:月度考核以月为周期,季度考核以季度为周期,年度考核以年度为周期等等。针对上面的问题,我们就以季度为周期的考核系统进行描述。比如业务规定“制定个人季度业绩考核表”的时间为上个季度最后一个月的26日开始到当季度 首月的10日。那么如果放到现在(2007-6-19),当前对应的季度为第2季度。通过业务规定,一个员工要制定其自身第2季度的业绩考核表的时间为“上季度最后一个月的26号(2007-3-26)到当季度首月的10日(2007-4-10)”。而在这个周期制定的考核表都是第2季度考核表。

由于是周期,那么就意味着一个循环,而在一个确定的时间对于同样的操作必须具有确定性,也就是知道了时间点后,而这个时间点也在某个规定的时间段内,那么我们应该明确知道对应的操作的日期。结合上面的例子,如果我们的时间是2007-4-1,那么我们可以明确的知道在“制定季度考核表”的时间段内,而且只能是制定第2季度的业绩考核表,而不可能是第1季度或者是第3 季度。这样的循环约束就以为着,如果用直线来代表时间的话,中间每个时间粒度(例子中是:季度)用一个线段来表示,用连接开始时间和结束时间的弧线来代表一个操作的区域时间,那么同样操作的时间区域就不能出现重叠的部分。因为重叠部分就意味着这个区域我们无法明确的知道是进行哪个操作,具有二义性。那么我们规定一种类型操作的时间段的最小和最大跨度分别是多少呢?最小的跨度很显然可以是0,那就是没有为一种类型的工作分配完成时间,相当于没有这种类型的工作一样。而最大时间跨度应该为时间粒度。季度考核 -- 最大的时间跨度就是季度。月度考核 -- 最大的时间跨度就是月度。为什么是这样的,我们依然可以通过上面的线段图来分析。由于是周期性的,那么时间点一定就是相同的。如果你超过了时间粒度,那么你无论如何都会存在重叠的区域。这个也就是出现了操作的二义性。注意:这里说的是一种类型的操作,而不是不同类型的操作。不同类型的操作存在时间上的重叠是没有问题的。如:制定考核表的时间和修改考核表的时间完全可以出现重叠。

在确定了时间跨度的限制后,下面我们讨论一下时间段的描述问题。由于是时间是周期性的,那么你就可能用两个绝对时间来规定一个时间段(绝对时间如:2007-6-1)。我们需要存储时间段开始和结束时间的相对方式。我们看时间段的存在有几种方式。

1. 时间段完全在同一个时间粒度内。如开始时间为2006-6-1结束时间为2006-6-10,如果从季度的时间粒度来看,那么这个时间段完全包含在第2季度内。

2. 时间段跨两个时间粒度。如开始时间:2006-3-26,结束时间:2006-4-10。开始时间点在第1季度而结束时间点在第2季度。

有没有可能出现跨3个时间粒度呢?不可能,因为前面已经讨论过了,一个操作时间段的最大值是不能超过一个时间粒度的。那么上面列举的两种情况就完全覆盖了所有时间段的情况。在设置时间段的时候我们就需要通过一定的标识来表示这两种情况。如下的例子就是用于季度绩效考核的时间段配置文件。

<?xml version="1.0" encoding="utf-8" ?>
<!--季度业绩考核各个阶段时间设置-->
<!--0:表示月末-->
<!--StartMonthSerial 开始月份在季度中的序号,一个季度包含3个月所以合法的取值为:1,2,3-->
<!--StartDate: 开始的具体日期-->
<!---EndMonthSerial 结束月份自阿季度中的序号->
<!--EndDate: 结束的具体日期
-->
<QuarterAssess>
  
<!--员工制定季度业绩考核表阶段-->
  
<SelfMakePlan>
    
<StartMonthSerial>-3</StartMonthSerial>
    
<StartDay>26</StartDay>
    
<EndMonthSerial>1</EndMonthSerial>
    
<EndDay>10</EndDay>
  
</SelfMakePlan>
</QuarterAssess>

其中我用“-”来表示跨季度,注意这里"-"表示的是跨季度, 而不是表示上个季度。这就意味着如果开始时是一个“负数”那么结束时间一定是一个“正数”。因为只有这样才是跨季度。那么可能不可能出现开始时间和结束时间都是“负数”呢?不可能,因为如果两个数都是“负数”那么说明开始时间点和结束时间点都在跨季度,那么实际上两个时间点都是在一个季度内,那么应该通过两个“正数”来表示。看到了负号(-),很自然想到会不会有正号(+),也不会存在。如果出现+号,那么如果定义为下个季度,那么这个时间段就超过了一个时间粒度,那么根据前文的讨论这个是不合法的。所以这些就是设置时间的约束,需要在手动设置和程序设置的时候进行控制。这里可能会涉及到一个特殊的值,就是月末的概念,怎么表示月末,因为月末根据大月,小月,平月(闰年29天,非闰年28天)这里在设置日期的时候无法设置成一个固定的值,我们这里可以使用一个特殊值,如0来替代。因为任何日期都不可能取到0值。然后在程序碰到0的时候就根据具体的时间将0替换成月末最后一天的日期。

当时间段在一个时间粒度内的时候,判断时间点是否在时间段内和对应的操作时间都比较的容易和直观。直接根据XML文件中配置的信息得到时间然后和当前时间进行比较就可以了。这里可以采用一种较为简单的方法来判断,就是将MonthSerial项和Day项进行字符串连接,Day项比如通过两位来表示日期,如:1 --> 01, 10 --> 10, 然后在将当前日期所在的MonthSerial和Day项进行字符串连接,看看的数值是否夹在开始时间和结束时间之间就可以了。如:我们规定开始时间为季度首月1日到季度次月的10日。那么我们可以得到的时间点描述为:101(开始时间), 210(结束时间)。那么如果用当前时间(2007-6-19),那么转化成相对时间点就是319。101 < 319 <210这个比较显然不成立,那么当前时间就会在规定的时间段内。

如果时间段跨了时间粒度,如上面XML代码表示的时间段那样,怎样进行判断呢?当然你可以通过一系列的if - else语句来进行判断。但是这样难免自己都觉得复杂不好理解。其实我们通过稍微的转化就可以将这种跨时间粒度的情况转化成在一个时间粒度内的情况,那就是拆分时间段。将跨季度的时间分成两个时间段,而两个时间段肯定都在都在各自的时间粒度内,那么就转化成了第一种情况,唯一不相同的就是这个时候需要比较两次。因为检查时间点如果出现在这两个时间段的任意一个时间段内都是在时间段内的。根据上面XML文件提供的例子,我们将时间段拆分为:-3/26 - -3/0 和 1-1 -- 1-10 两个部分。那么根据前面的讨论如果同时出现两个符号(-),那么就可以去掉。比较方法和前面是说明的一样的。但是这个-号必须做一个标记,因为它在确定操作日期的时候具有作用。还是季度绩效考核的例子,描述为“上个季度的最后一个月26日到当季度首月10日都是制定当季度的业绩考核表”,加入你在上季度的最后一个月30日登录系统,那么对应的还是上月季度,但是你操作的应该是下季度的考核表,怎样判断是否在当前季度加1还是不加呢?这个就是通过负号来判断的。

以上就是针对存在时间循环系统的时间管理模块的实现方案。非常感谢我的企业导师倪骏和主管陈峰提供的思路和帮助,我当时看了他们的代码想了好久才明白,开始总是绕不过弯来,所以写下这篇文章也算备忘吧。

如果各位朋友又更好的方法和建议,也非常希望和大家讨论:)

今天是端午节,大家节日愉快:)

原创粉丝点击