启明星新版增加系统,增加了请假时长的自动计算。本文讨论请假系统算法的实现。(如果您有好的算法,欢迎和我联系。)
在请假前,我们假定,公司的工作时间是 上午:8:00--12:00 下午13:00--17:00。 中间一个小时是午饭时间。
(1)首先考虑当天请假情况,例如今天是2014-6-25日。
假设用户请假是从 2014-6-25 9:00:00 到 2014-6-25 16:00:00
那么,如果人工算的话,应该是分为两端:
(a)计算 9:00--12:00的时段(3个小时)
(b)计算13:00--16:00的时段(3个小时)
也就是,用户其实请假是3+3 共6个小时。
但是用户请假时间是任意选择的,他可能选择从7:00--16:00,那么7:00--8:00这一个小时,是不算请假的。
如果我们把用户的请假时代用AB表示,作息上午时间用CD表示,作息下午时间用EF表示
那么一天的请假其实是计算2个时间段的交集,也就是请假时长= AB∩CD+AB∩EF
下面考虑AB∩CD也就是求2个时间段的交集,这需要考虑六种情况:(具体略,高中高数求集合交集时常用的方法。)
为此,我们可以写出一个方法:Get2TimeSpan 获取2个时间段有多长(单位为分钟。)
private double Get2TimeSpan(DateTime dt1, DateTime dt2, DateTime dt3, DateTime dt4) { //1. if (dt4 <= dt1) return 0; //2. if (dt3 >= dt2) return 0; //3 if (dt1 >= dt3 && dt1 <= dt4 && dt4 <= dt2) return (dt4 - dt1).TotalMinutes; //4 if (dt1 <= dt3 && dt3 <= dt2 && dt2 <= dt4) return (dt2 - dt3).TotalMinutes; //5 if (dt1 <= dt3 && dt4 <= dt2) return (dt4 - dt3).TotalMinutes; return (dt2 - dt1).TotalMinutes; }
上面的方法是2个时间的交集,一天其实是分上午和下午,共2个,所以我们可以写一个获取一天内,交集多长的函数
public double GetSameDayTimeSpan(DateTime dt1, DateTime dt2, DateTime base1, DateTime base2, DateTime base3, DateTime base4, DataTable table) { if (isWorkDay(dt1, table)) { return Get2TimeSpan(dt1, dt2, base1, base2) + Get2TimeSpan(dt1, dt2, base3, base4); } else { return 0; } }
这样,我们就可以获取,一天内,用户请假时长的放上。
(2)请假跨天的情况
请假跨天时,我们需要知道每一天是否是工作日。 这里,是不是工作日并不完全是否是周六或者周日决定,因为例如国庆节10.1日,不管是否是工作日,都放假的。
所以,我们需要判断一个工作日是否是工作日:
public bool isWorkDay(DateTime dt, DataTable table) { bool isworkday = false; DataView dv = table.DefaultView; dv.RowFilter = " sdate=#" + dt.ToString("yyyy-MM-dd") + "#"; if (dv.Count > 0) { isworkday = dv[0]["isworkday"].ToString() == "1" ? true : false; return isworkday; } if (dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday) { return false; } return true; }
这里,以用户自定义的日期优先级最高。如果不存在用户自定义的,则自动使用默认的日期。
在启明星请假系统里,是一天天的进行判断跨天请假的,如果是工作日,直接返回当天时长,否则返回为0
int day = (odt2 - odt1).Days; for (int i = 0; i <= day; i++) { if (i == 0) { _base = odt1.AddDays(i).ToString("yyyy-MM-dd"); base1 = DateTime.Parse(_base + " " + dtfrom1); base2 = DateTime.Parse(_base + " " + dtto1); base3 = DateTime.Parse(_base + " " + dtfrom2); base4 = DateTime.Parse(_base + " " + dtto2); tdt = base4; totalMiniter+= GetSameDayTimeSpan(odt1, tdt, base1, base2, base3, base4, table2); } else { if (i == day) { //最后一天 _base = odt1.AddDays(i).ToString("yyyy-MM-dd"); base1 = DateTime.Parse(_base + " " + dtfrom1); base2 = DateTime.Parse(_base + " " + dtto1); base3 = DateTime.Parse(_base + " " + dtfrom2); base4 = DateTime.Parse(_base + " " + dtto2); tdt = base1; totalMiniter+= GetSameDayTimeSpan(base1, odt2, base1, base2, base3, base4, table2); } else { tdt = odt1.AddDays(i); if (isWorkDay(tdt, table2)) { totalMiniter += workDayTotalMiniter; } else { totalMiniter += 0; } } } } } return totalMiniter;
到此,我们与大家分享了启明星请假系统里,工作日的算法。如果您有好的算法,欢迎与我们联系。