【已解决】关于排班表的算法问题
本帖最后由 xms77 于 2013-3-29 12:44 编辑这个是公司的排班表,我们公司是4班2运转,4个班分别是:Team1,Team2,Team3,Team4, 每4个星期白晚班会轮换一次,比如说:Team1
第1周(FW1)是从星期天到星期二上白班,星期三到星期六休息
第2周(FW2)是从星期天到星期三上白班,星期四到星期六休息
第3周(FW3)是从星期天到星期二上白班,星期三到星期六休息
第4周(FW4)是从星期天到星期三上白班,星期四到星期六休息
第5周(FW5)倒班了,是从星期天到星期二上晚班班,星期三到星期六休息
第6周(FW6)是从星期天到星期三上晚班班,星期四到星期六休息
。。。
。。。
我想要的结果就是可以从某个日期和时间来推断是哪个班在上班?
刚刚开始思考,脑子里面很乱,没有什么头绪,不知道大家有没有好的idea?
几乎废了一夜,总算完成了自己的代码,代码很冗长,不如5楼大师的算法简洁。
#include <date.au3>
#include <array.au3>
Local $return = _Date2WeekYearTeamDayNight("2013-02-13 20:32:29")
If IsArray($return) Then _ArrayDisplay($return)
;偶数周,Team1,3是日,一,二,三上班; Team2,4是四,五,六上班
;奇数周,Team1,3是日,一,二上班; Team2,4是三,四,五,六上班
;Weeks4Turn的值是偶数:Team1夜班,Team3白班, Team2夜班,Team4白班
;Weeks4Turn的值是奇数:Team1白班,Team3夜班,Team2白班,Team4夜班
Func _Date2WeekYearTeamDayNight($iDateTime) ;日期时间格式:'2013-03-17 08:32:29'
Local $AllInfo
$iDateTime = StringStripWS($iDateTime,3)
if Not StringRegExp($iDateTime,"\d{4}[-/]\d{1,2}[-/]\d{1,2} \d{1,2}:\d{2}:\d{2}",0) Then Return SetError(-1);返回日期格式错误
Local $temp = StringRegExp($iDateTime,'(\d{4}[-/]\d{1,2}[-/]\d{1,2})',1)
Local $date = $temp
Local $temp = StringRegExp($iDateTime,'(\d{1,2}:\d{2}:\d{2})',1)
Local $time = $temp
Local $DayNight = _JudgeDayNightShift($time)
If @error Then Return SetError(-1);返回日期格式错误
If $DayNight = "N-" Then ;把日期转换为前一天的晚上
$iDateTime = _DatetimeBack8Hours($iDateTime)
MsgBox(0,"new datetime is ",$iDateTime)
$temp = StringRegExp($iDateTime,'(\d{4}[-/]\d{1,2}[-/]\d{1,2})',1)
$date = $temp
$temp = StringRegExp($iDateTime,'(\d{1,2}:\d{2}:\d{2})',1)
$time = $temp
EndIf
$DayNight = _JudgeDayNightShift($time)
$FiscalYear = _Date2FiscalYear($date)
$WeekName = _Date2WeekName($date)
$weeks = _Data2Weeks($date)
$Weeks4Turn = _DateCheck4WeeksTurn($date)
$AllInfo = "WW" & $weeks & $FiscalYear
Local $DayNight = _JudgeDayNightShift($time)
If Not @error Then
Select
Case $DayNight = "D"
$AllInfo = "D"
Select
Case $Weeks4Turn = 0;偶数
If $WeekName = 7 Or $WeekName = 1 Or $WeekName = 2 Or $WeekName = 4 Then ;如果是星期日,一,二,三
$AllInfo = "T3"
Else
$AllInfo = "T4"
EndIf
Case $Weeks4Turn = 1 ;奇数
If $WeekName = 7 Or $WeekName = 1 Or $WeekName = 2Then ;如果是星期日,一,二,三
$AllInfo = "T1"
Else
$AllInfo = "T2"
EndIf
EndSelect
Case $DayNight = "N"
$AllInfo = "N"
Select
Case $Weeks4Turn = 0;偶数周
If $WeekName = 7 Or $WeekName = 1 Or $WeekName = 2 Or $WeekName = 4 Then ;如果是星期日,一,二,三
$AllInfo = "T1"
Else
$AllInfo = "T2"
EndIf
Case $Weeks4Turn = 1 ;奇数周
If $WeekName = 7 Or $WeekName = 1 Or $WeekName = 2Then ;如果是星期日,一,二,三
$AllInfo = "T3"
Else
$AllInfo = "T4"
EndIf
EndSelect
EndSelect
EndIf
Return $AllInfo
EndFunc
Func _JudgeDayNightShift($itime)
$itime = StringStripWS($itime,3)
If Not StringRegExp($itime,'\d{1,2}:\d{2}:\d{2}',0) Then Return SetError(-1)
Local $splitime = StringSplit($itime,':')
Select
Case Int($splitime) >= 7 And Int($splitime) < 19
Return "D"
Case Int($splitime) >= 19
Return "N"
Case Int($splitime) < 7
Return "N-"
EndSelect
EndFunc
Func _Date2WeekName($idate)
$idate= StringStripWS($idate,3)
if Not StringRegExp($idate,"\d{4}[-/]\d{1,2}[-/]\d{1,2}",0) Then Return SetError(-1);返回日期格式错误
Local $splitdate = StringSplit($idate,"-")
Local $WeekName = _DateToDayOfWeekISO($splitdate,$splitdate,$splitdate)
If $WeekName = 0 Then Return SetError(-2) ;转换日期出错
Return $WeekName
EndFunc
Func _Date2FiscalYear($idate)
$idate= StringStripWS($idate,3)
if Not StringRegExp($idate,"\d{4}[-/]\d{1,2}[-/]\d{1,2}",0) Then Return SetError(-1);返回日期格式错误
Local $splitdate = StringSplit($idate,"-")
Local $year = Int(StringRight($splitdate,2))
if Int($splitdate) < 7 then
Return "FY"& $year
Else
Return "FY"& $year +1
EndIf
EndFunc
Func _Data2Weeks($idate)
$idate= StringStripWS($idate,3)
if Not StringRegExp($idate,"\d{4}[-/]\d{1,2}[-/]\d{1,2}",0) Then Return SetError(-1);返回日期格式错误
Local $iDateCalc = _DateDiff('d',"2012-6-30", $idate)
Local $Weeks = Mod(Int($iDateCalc/7)+1,52)
If $Weeks = 0 Then $Weeks = 52
Return $Weeks
EndFunc
Func _DateCheck4WeeksTurn($idate)
$idate= StringStripWS($idate,3)
if Not StringRegExp($idate,"\d{4}[-/]\d{1,2}[-/]\d{1,2}",0) Then Return SetError(-1);返回日期格式错误
Local $iDateCalc2 = _DateDiff('d',"2012-7-15", "2013-3-24")
Local $iWeeks4Turn = Mod(Int(Int($iDateCalc2/7)/4),2)
Return $iWeeks4Turn
EndFunc
Func _DatetimeBack8Hours($iDateTime)
$iDateTime = StringStripWS($iDateTime,3)
if Not StringRegExp($iDateTime,"\d{4}[-/]\d{1,2}[-/]\d{1,2} \d{1,2}:\d{2}:\d{2}",0) Then Return SetError(-1);返回日期格式错误
Local $temp = StringRegExp($iDateTime,'(\d{4}[-/]\d{1,2}[-/]\d{1,2})',1)
Local $date = $temp
Local $temp = StringRegExp($iDateTime,'(\d{1,2}:\d{2}:\d{2})',1)
Local $time = $temp
Local $newDate = _DateAdd('D',-1,$date)
Local $newTime = StringRegExpReplace($time,'\d{1,2}(:\d{2}:\d{2})','20$1')
Return $newDate & ' ' & $newTime
EndFunc 如果你有上面这个照片的电子版,可以考虑用图片分割然后识别颜色的方法把全年的数据识别出来,然后用软件读取指定天的指定班次。 回复 2# sanmoking
想到先用_DateToDayOfWeekISO($iYear, $iMonth, $iDay)函数把日期转换为星期,然后怎么来区分周。继续开动脑筋~~~ 本帖最后由 sanmoking 于 2013-3-28 23:36 编辑
回复 3# xms77
其实用我的那个排班表就能给你的班排出来,白天没仔细看你的轮班周期,晚上到家帮你排了一下,一共是8周一轮,共56天,所以只需要制作一个56天一轮的班表即可:
T1日T3晚T2T4休 T1日T3晚T2T4休 T1日T3晚T2T4休 T2日T4晚T1T3休 T2日T4晚T1T3休 T2日T4晚T1T3休 T2日T4晚T1T3休T1日T3晚T2T4休 T1日T3晚T2T4休 T1日T3晚T2T4休 T1日T3晚T2T4休 T2日T4晚T1T3休 T2日T4晚T1T3休 T2日T4晚T1T3休T1日T3晚T2T4休 T1日T3晚T2T4休 T1日T3晚T2T4休 T2日T4晚T1T3休 T2日T4晚T1T3休 T2日T4晚T1T3休 T2日T4晚T1T3休T1日T3晚T2T4休 T1日T3晚T2T4休 T1日T3晚T2T4休 T1日T3晚T2T4休 T2日T4晚T1T3休 T2日T4晚T1T3休 T2日T4晚T1T3休T3日T1晚T2T4休 T3日T1晚T2T4休 T3日T1晚T2T4休 T4日T2晚T1T3休 T4日T2晚T1T3休 T4日T2晚T1T3休 T4日T2晚T1T3休T3日T1晚T2T4休 T3日T1晚T2T4休 T3日T1晚T2T4休 T3日T1晚T2T4休 T4日T2晚T1T3休 T4日T2晚T1T3休 T4日T2晚T1T3休T3日T1晚T2T4休 T3日T1晚T2T4休 T3日T1晚T2T4休 T4日T2晚T1T3休 T4日T2晚T1T3休 T4日T2晚T1T3休 T4日T2晚T1T3休T3日T1晚T2T4休 T3日T1晚T2T4休 T3日T1晚T2T4休 T3日T1晚T2T4休 T4日T2晚T1T3休 T4日T2晚T1T3休 T4日T2晚T1T3休
如果是四个team1的轮班都放一起的话,配置文件如下,因为界面位置太小,所以+代表白班,-代表晚班,没有的就是休息:
默认班次=xms77的班表
轮班序列=|1+3-|1+3-|1+3-|2+4-|2+4-|2+4-|2+4-|1+3-|1+3-|1+3-|1+3-|2+4-|2+4-|2+4-|1+3-|1+3-|1+3-|2+4-|2+4-|2+4-|2+4-|1+3-|1+3-|1+3-|1+3-|2+4-|2+4-|2+4-|3+1-|3+1-|3+1-|4+2-|4+2-|4+2-|4+2-|3+1-|3+1-|3+1-|3+1-|4+2-|4+2-|4+2-|3+1-|3+1-|3+1-|4+2-|4+2-|4+2-|4+2-|3+1-|3+1-|3+1-|3+1-|4+2-|4+2-|4+2-
起始日期=2013/01/27
如果只是需要提取出TEAM1的班表的话,配置文件如下:
默认班次=xms77的班表-TEAM1
轮班序列=|日班|日班|日班|休息|休息|休息|休息|日班|日班|日班|日班|休息|休息|休息|日班|日班|日班|休息|休息|休息|休息|日班|日班|日班|日班|休息|休息|休息|晚班|晚班|晚班|休息|休息|休息|休息|晚班|晚班|晚班|晚班|休息|休息|休息|晚班|晚班|晚班|休息|休息|休息|休息|晚班|晚班|晚班|晚班|休息|休息|休息
起始日期=2013/01/27
实际上附件里的配置文件都是像上图里面的一样,实际上是有备注的,上面帖子里面的为了美观,我把备注都去掉了。
有备注的轮班序列太长了,如下所示:
[示例班表]
轮班序列=|日班@@备注#crlf换了一行|晚班@@#crlf第一行空,这是第二行#crlf第三行|休息@@休息就不用备注了吧#crlf好吧又换了一行
起始日期=2013/01/27
本帖最后由 zldfsz 于 2013-3-29 01:03 编辑
#include <Date.au3>
$day = InputBox("输入要查询的日期", "输入要查询的日期,格式为YYYY/MM/DD", _NowCalcDate())
Dim $FirstDay = '2012/06/30';第一周第一天的日期
;以下是 用1代表Team1白班,Team3夜班;
; 用-1代表Team3白班,Team1夜班
; 用2代表Team2白班,Team4夜班
; 用-2代表Team4白班,Team2夜班
Dim $s=[ 2, 1, 1, 1, 2, 2, 2, _;第一周的排班表
2, 1, 1, 1, 1, 2, 2, _;第二周的排班表
2,-1,-1,-1,-2,-2,-2, _;第三周的排班表
-2,-1,-1,-1,-1,-2,-2];第四周的排班表
Dim $ss
;由前4周转换为前8周的排班表
For $i = 0 To 55
If $i > 27 Then
$ss[$i] = -1 * $s[$i - 28]
Else
$ss[$i] = $s[$i]
EndIf
Next
$iDateCalc = _DateDiff('D', $FirstDay, $day)
$j = $ss
Switch $j
Case 1
$result = $day & " Team1白班,Team3夜班,Team2、Team4休息"
Case -1
$result = $day & " Team3白班,Team1夜班,Team2、Team4休息"
Case 2
$result = $day & " Team2白班,Team4夜班,Team1、Team3休息"
Case -2
$result = $day & " Team4白班,Team2夜班,Team1、Team3休息"
EndSwitch
MsgBox(4096, "", $result)
回复 5# zldfsz
谢谢大师,很简洁的代码。 回复 4# sanmoking
总算完成了,我的算法比较复杂,废了一夜的脑细胞! 回复 5# zldfsz
我的算法太复杂了,但是功能还是可以的,真他妈累人啊{:face (229):} 回复 4# sanmoking
大哥能介绍是怎么写的吗? 回复 9# sniperone
我那个日历是适用于任何有循环的轮班表。
类似于计算某一天是星期几的方法(相当于一轮7天)。 回复 8# xms77
呵呵,这种问题找到规律就好办了,才发现我第一的单词都写错了,英语不好就这样,发现还不能编辑,如果A版看到了帮忙改一下 很不错的东西,顺便学习下各种算法
页:
[1]