基于日期和时间的轮班识别

Posted

技术标签:

【中文标题】基于日期和时间的轮班识别【英文标题】:Work shift recognition based on date and time 【发布时间】:2016-07-14 11:46:37 【问题描述】:

我有一个工作班次时间表A, B, C, D,每个班次从7:15 to 7:14开始 例如:

shift A from 7:15 to 19:14
shift C from 19:15 to 7:14
shift D from 7:15 to 19:14
shift B from 19:15 to 7:14

时间表是这样的:C, A, C, A, D, B, D, B, A, C, A, C, B, D, B, D 然后循环将重新开始。

我想创建一个程序,让我希望班次在特定日期和时间工作 例如7/14/2016 12 会给我A

知道怎么做吗?

【问题讨论】:

A 和 D、C 和 B 是相同的 - 这在一定程度上简化了您的日程安排 不一样,每个班次不同,这意味着我有 4 个班次 为什么在 B 之后有一个 C?这是否意味着两者之间存在空转? 您在哪里说明哪个班次在哪一天工作?您只给出了班次的时间,但如前所述,A 和 D,C 和 B 的时间相同。需要更多信息。 第一个班次从一周中的哪一天(或日期)开始?没有这些信息,您将无法计算结果 【参考方案1】:

这是编写 T-SQL 表达式的一种方法,该表达式将评估您正在寻找的内容。

case dateadd(minute, @baseTime, @myTime) / (1440 * 8) % 2
    when 0
        case dateadd(minute, @baseTime, @myTime) / (1440 * 4) % 2
            when 0 then
                case dateadd(minute, @baseTime, @myTime) / (1440 * 4) / 720 % 2
                    when 0 then 'C' when 1 then 'A' end
        when 1 then
            case dateadd(minute, @baseTime, @myTime) / (1440 * 4) / 720 % 2
                when 0 then 'D' when 1 then 'B' end
    when 1
        case dateadd(minute, @baseTime, @myTime) / (1440 * 4) % 2
            when 0 then
                case dateadd(minute, @baseTime, @myTime) / (1440 * 4) / 720 % 2
                    when 0 then 'A' when 1 then 'C' end
        when 1 then
            case dateadd(minute, @baseTime, @myTime) / (1440 * 4) / 720 % 2
                when 0 then 'B' when 1 then 'D' end
end

您可以使用负数来完成这项工作,但选择@baseTime 可能会更容易,因为它已经足够远了,您不必担心它。显然,您的周期每 16 天重复一次,您可以根据需要在逻辑上将其向后延长,以便进行计算。如果你在 VB.Net 中需要这个,翻译起来很简单。只需确保使用整数除法 (\) 而不是“常规”除法 (/)。

在我意识到这对班次在后半段交换之前,我开始了上面的小公式。虽然它很容易扩展,但阅读起来可能有点长。值得庆幸的是,您可以通过计算分钟数并使用每个班次的值范围来描述任何类型的调度。这很清楚,很容易修改。

declare @min int = datediff(minute, @baseTime, @myTime) % (1440 * 8)
select case
    when @min between     0 and   719 then 'A'
    when @min between   720 and  1439 then 'C'
    ...
    when @min between 10080 and 11519 then 'D'
end

主要是为了好玩,也可以使用下面的公式和从零开始数到十五的十六个周期内的“移位数”来编写这种模式的紧凑方式。作为输出,让 1 对应 A,2 对应 B,3 对应 C,4 对应 D。

1 + 2 * ((shiftNumber + shiftNumber / 8 % 2 + 1) % 2) + (shiftNumber / 4 % 2)

这里有一些 T-SQL 代码可以查看模式是否正确:

create table T (n int );
insert into T (n) values (0), (1),  (2),  (3),  (4),  (5),  (6),  (7),
                         (8), (9), (10), (11), (12), (13), (14), (15);

select
    n + 1 as num,
    substring('ABCD', 1 + 2 * ((n + n / 8 % 2) % 2) + (n / 4 % 2), 1) as shift
from T order by n;

http://rextester.com/HCHZG96643

在代码中,您可以轻松计算要使用的班次编号来代替n

declare @shiftNumber int = datediff(minute, @baseTime, @myTime) / 720 % 16;

【讨论】:

【参考方案2】:

在讨论问题的解决方案之前,我想尝试澄清对所传达内容的困惑。如果我们采用您的日程安排并将字母替换为我们得到的时间表

C, A, C, A
19:15 to 7:14, 7:15 to 19:14, 19:15 to 7:14, 7:15 to 19:14,

这个顺序看起来合乎逻辑,但你转向

D, B, D, B, A, C, A, C,
7:15 to 19:14, 19:15 to 7:14, 7:15 to 19:14, 19:15 to 7:14, 7:15 to 19:14, 19:15 to 7:14, 7:15 to 19:14, 19:15 to 7:14, 

这个序列本身很好,但是当你结合这两个序列时就会出现问题。在组合点你得到

C, A, D, B
19:15 to 7:14, 7:15 to 19:14, 7:15 to 19:14, 19:15 to 7:14,

A, C, B, D
7:15 to 19:14, 19:15 to 7:14, 19:15 to 7:14, 7:15 to 19:14,

您仍然为自己辩护,这种看似不一致的情况可能是有原因的,即整个班次似乎都被跳过了。

其次你说你有 4 个班次,如果这完全正确,那么你可以有一个 C、B、C、B 的序列并且完全有效,但我猜情况并非如此,你实际上只有有 2 个班次,但您可能还有 2 个工作人员或其他影响,我还注意到他们似乎是成对的班次,因此您的第一张桌子可能会更好地显示为:

Shift A1 from 7:15 to 19:14
Shift A2 from 19:15 to 7:14
Shift B1 from 7:15 to 19:14
Shift B2 from 19:15 to 7:14

这仍然不能解释看似缺失的 Shifts,但它有助于澄清一些你正在努力的目标。

再次为您辩护,这些都没有任何区别,因为我们实际上只使用以下模式在哪一天工作的轮班工作:

C、A、C、A、D、B、D、B、A、C、A、C、B、D、B、D

当然,当 Matt Wilko 说您需要提供第一个班次的开始日期(19:15 到 7:14 或 C)时,他确实表示了函数的一个参数。

当然,只要为最终开发人员明确定义了所需的注意事项,这一切都不会限制人们为此创建函数:

使用 C# 语法和功能编写,因为我在 C# 中编码速度更快,但同时也是一名 VBA 程序员,我知道它可以很容易地翻译成 VBA

/*
Parameters:  SeqStartDate (DateTime) - The date that the beginning of the schedule sequence falls upon (aka the date that the first C of C, A, C, A falls on) 
             RequstPeriod (DateTime) - The specific period you are wanting the schedule for.

Returns:     Shift (int) - A numeric representation of the specific shift being worked, for the requested time period
                           (0 = No Shift, 1 = Shift A, 2 = Shift C, 3 = Shift D, 4 = Shift B)

                  Error: - If an error occurs it will return a negative number corresponding to the error
                           However for demonstration purposes it will for now just return -1

Assumptions: Since there are missing shifts a zero has been added to account for these
             That only future shift schedules are desired
             RequstPeriod must occur at least a day after the SeqStartDate
             RequstPeriod must occur at least a day after this program is being run

Modification Note: This could be made more robust and eliminate some of the assumptions but
                   I think what is here should give the basic concept and I will leave the
                   modifying to fit the developers specific needs to the developer
*/
public int GetShift(SeqStartDate as DateTime, RequstPeriod as DateTime)

    // First Validate Inputs

    // Today’s Date 1 Second past Midnight
    TodayIs = ((DateTime.Now()).Date).AddTicks(1);

    TimeSpan SeqTSpan = RequstPeriod.Subtract(SeqStartDate)
    if (SeqTSpan.Minutes < 1440)
    
        return -1;
    
    TimeSpan TodayTSpan = RequstPeriod.Subtract(TodayIs)
    if (TodayTSpan.Minutes < 1440)
    
        return -1;
    

    long SchdMins = 0;
    long SeqShftr = 0;
    int SchdIndx = 0;

    // A Zero represents a missing shift within the continuous schedule sequence
    int ShftSchedSeq[] = 1,2,1,0,3,4,3,4,1,2,1,2,0,4,3,4,3,2;
    long ShftSeqStartMins[] = 57,1155,1497,2595,2937,4035,4377,5475,5817,6915,7257,8355,5697,9795,10137,11235,11577,12675
    const long FirstStartTimeMin = 57;
    const int IndxCnt = 18;  // Note C# is a 0 based array thus the adjustment later

    // The following assumes Integer division truncates and gives a number from 1 to 8 in SeqShftr
    TimeSpan SeqShftrTSpan = TodayIs.Subtract((SeqStartDate.Date).AddTicks(1))

    // Note  %  means Modulo to ease translation
    SeqShftr = ((((SeqShftrTSpan.Minutes) / 1440) % 9) * 2);

    SchdMins = SeqTSpan.Minutes % 12960

    // Is the SchdMins less than smallest value in Shift Schedule Array?
    if (SchdMins < FirstStartTimeMin)
    
       // If yes then it is part of the previous sequence shift which would be the last sequence shift
       ShftIndx = IndxCnt - 1;
    
    else
    
        // For Loop Counts Down from MaxIndx-1 to 0
        for (int Indx = (IndxCnt-1); Indx => 0; Indx—-)
        
            if (ShftSeqStartMins[Indx] <= SchdMins)
            
                ShftIndx = Indx;
                break;
            
        
    

    ShftIndx = (ShftIndx + SeqShftr);
    if (ShftIndx > (IndxCnt - 1))
    
        ShftIndx = ShftIndx - (IndxCnt - 1);
    

    return ShftSchedSeq[ShftIndx];    

最后说明:看了你的一个 cmets,我发现你似乎有点困惑——仍然要调整上述程序,你需要做的就是调整 ScheduleSequence 数组(可能还有 ShftSeqStartMins 数组)中的值一旦你弄清楚你想要它是什么,它们应该是什么。正如您在我所指的评论中所说,早班也不能是夜班,反之亦然。

【讨论】:

以上是关于基于日期和时间的轮班识别的主要内容,如果未能解决你的问题,请参考以下文章

基于 WeihanLi.Npoi 实现excel导入时纯汉字的日期转换

如何从开始日期和结束日期识别和聚合序列

算法:根据周数获取明年日期工作班次类型

从详细活动生成摘要甘特图

如何使用 tesseract.js 识别乐透彩票上的日期和数字?

基于统计的中文实体识别方法简述