为了账号安全,请及时绑定邮箱和手机立即绑定

计算跳过假期和周末的工作日

计算跳过假期和周末的工作日

Go
慕虎7371278 2022-12-05 17:14:27
我有这个问题,流程从初始日期开始,比方说 2022-04-14,我必须将此日期添加十天,但我必须考虑周末和假期,所以也许如果在初始日期和最终日期之间我们有一个周末和一个假期的日期最终日期是 2022-04-27。如果初次约会是在周末或节假日开始的,也是必要的考虑。这就是问题所在。我的第一个方法是创建一个循环,每天检查初始日期加十天和每个星期六、星期日和节假日总和一天,所以我将有十天加三天,这个结果将添加到我的初始日期到最后计算最终日期。我的问题是,是否有其他解决方案或实施方式可以更有效?因为这可能在未来会被很多人使用。
查看完整描述

2 回答

?
动漫人物

TA贡献1815条经验 获得超10个赞

另外不要忘记在添加累积的额外天数(周末和假期)时,这些天数可能会涵盖新的周末和假期,因此您必须“递归”执行此操作。


最简单的解决方案

最简单的解决方案可以从初始日期开始,将其增加一天,然后检查每一天是否是可跳过的(周末或假期)。如果不是,请减少天数,然后重复直到添加所需的天数。


这就是它的样子:


func addDays(start time.Time, days int) (end time.Time) {

    for end = start; days > 0; {

        end = end.AddDate(0, 0, 1)

        if !skippable(end) {

            days--

        }

    }

    return end

}


func skippable(day time.Time) bool {

    if wd := day.Weekday(); wd == time.Saturday || wd == time.Sunday {

        return true

    }

    if isHoliday(day) {

        return true

    }

    return false

}


func isHoliday(day time.Time) bool {

    return false // TODO

}

测试它:


d := time.Date(2022, time.April, 14, 0, 0, 0, 0, time.UTC)

fmt.Println(addDays(d, 0))

fmt.Println(addDays(d, 1))

fmt.Println(addDays(d, 10))

哪些输出(在Go Playground上尝试):


2022-04-14 00:00:00 +0000 UTC

2022-04-15 00:00:00 +0000 UTC

2022-04-28 00:00:00 +0000 UTC

更快的解决方案

更快的解决方案可以避免循环日复一日。


计算周末天数:知道初始日期是哪一天,知道你要步进多少天,我们就可以计算出中间的周末天数。例如,如果我们必须步进 14 天,即整整 2 周,那肯定正好包括 4 个周末。如果我们必须多走一步,例如 16 天,那也包括 2 个完整的星期(4 个周末),以及可选的 1 或 2 天,我们可以轻松检查。


计算假期:我们可能会使用一个技巧来在排序切片(按日期排序)中列出假期,因此我们可以轻松/快速地找到 2 个日期之间的天数。我们可以在一个排序的切片中对某个时间段的开始和结束日期进行二分查找,一个时间段中的假期数就是这两个索引之间的元素数。注意:周末假期不得包含在此切片中(否则会被计算两次)。


让我们看看这个实现的样子:


// holidays is a sorted list of holidays

var holidays = []time.Time{

    time.Date(2022, time.April, 15, 0, 0, 0, 0, time.UTC),

}


func addDaysFast(start time.Time, days int) (end time.Time) {

    weekendDays := days / 7 * 2 // Full weeks

    // Account for weekends if there's fraction week:

    for day, fraction := start.AddDate(0, 0, 1), days%7; fraction > 0; day, fraction = day.AddDate(0, 0, 1), fraction-1 {

        if wd := day.Weekday(); wd == time.Saturday || wd == time.Sunday {

            weekendDays++

        }

    }


    end = start.AddDate(0, 0, days+weekendDays)


    first := sort.Search(len(holidays), func(i int) bool {

        return !holidays[i].Before(start)

    })

    last := sort.Search(len(holidays), func(i int) bool {

        return !holidays[i].Before(end)

    })


    // There are last - first holidays  in the range [start..end]

    numHolidays := last - first

    if last < len(holidays) && holidays[last].Equal(end) {

        numHolidays++ // end is exactly a holiday

    }


    if numHolidays == 0 {

        return end // We're done

    }


    // We have to add numHolidays, using the same "rules" above:

    return addDaysFast(end, numHolidays)

}

测试它:


d := time.Date(2022, time.April, 14, 0, 0, 0, 0, time.UTC)

fmt.Println(addDaysFast(d, 0))

fmt.Println(addDaysFast(d, 1))

fmt.Println(addDaysFast(d, 10))

输出(在Go Playground上尝试):


2022-04-14 00:00:00 +0000 UTC

2022-04-18 00:00:00 +0000 UTC

2022-04-29 00:00:00 +0000 UTC

改善addDaysFast()

仍然有改进的方法addDaysFast()

  • 检查小数周中周末天数的初始循环可以用算术计算代替(参见示例

  • 递归可以用迭代解决方案代替

  • 另一种解决方案可以将周末列为假期,因此可以消除计算周末的第一部分(不得包括重复项)



查看完整回答
反对 回复 2022-12-05
?
RISEBY

TA贡献1856条经验 获得超5个赞

我会像这样计算工作日。除了假日查找之外,它没有循环并且具有 O(1) 时间和空间复杂度:

  • 计算范围的开始日期和结束日期之间的天数

  • 将其除以 7。商是范围内的周数;余下的是整日剩余的小数周。

  • 范围内的基本工作日数是范围内的周数乘以 5,因为每 7 天的时间段,无论何时开始,都包含 2 个周末。

  • 末尾的小数周,在整天中,必须进行调整以删除任何周末。这是小数周中的整数天数和范围结束日期的星期几的函数。

  • 假期查询留给读者作为练习,因为这太过依赖于文化、地区和业务而无法解决。应该注意,这里的逻辑中有一个内置的假设,即“假期”不会出现在周六或周日。

func BusinessDays(start time.Time, end time.Time) int {

    from := toDate(start)

    thru := toDate(end)

    weeks, days := delta(from, thru)


    adjustedDays := adjustDays(days, thru.Weekday())


    businessDays := ( ( weeks * 5) + adjustedDays ) - holidaysInRange(from, thru)


    return businessDays


}


func toDate(t time.Time) time.Time {

    y, m, d := t.Date()

    adjusted := time.Date(y, m, d, 0, 0, 0, 0, t.Location())

    return adjusted

}


func holidaysInRange(from, thru time.Time) (cnt int) {

    // TODO: Actual implementation left as an exercise for the reader

    return cnt

}


func delta(from, thru time.Time) (weeks, days int) {

    const seconds_per_day = 86400

    totalDays := (thru.Unix() - from.Unix()) / seconds_per_day


    weeks = int(totalDays / 7)

    days = int(totalDays % 7)

    return weeks, days

}


func adjustDays(days int, lastDay time.Weekday) int {

    adjusted := days

    switch days {

    case 1:

        switch lastDay {

        case time.Saturday:

        case time.Sunday:

            adjusted -= 1

        }

    case 2:

        switch lastDay {

        case time.Sunday:

            adjusted -= 2

        case time.Saturday:

        case time.Monday:

            adjusted -= 1

        }

    case 3:

        switch lastDay {

        case time.Sunday:

        case time.Monday:

            adjusted -= 2

        case time.Tuesday:

        case time.Saturday:

            adjusted -= 1

        }

    case 4:

        switch lastDay {

        case time.Sunday:

        case time.Monday:

        case time.Tuesday:

            adjusted -= 2

        case time.Wednesday:

        case time.Saturday:

            adjusted -= 1

        }

    case 5:

        switch lastDay {

        case time.Sunday:

        case time.Monday:

        case time.Tuesday:

        case time.Wednesday:

            adjusted -= 2

        case time.Thursday:

        case time.Saturday:

            adjusted -= 1

        }

    case 6:

        switch lastDay {

        case time.Sunday:

        case time.Monday:

        case time.Tuesday:

        case time.Wednesday:

        case time.Thursday:

            adjusted -= 2

        case time.Friday:

        case time.Saturday:

            adjusted -= 1

        }

    }


    return adjusted

}


查看完整回答
反对 回复 2022-12-05
  • 2 回答
  • 0 关注
  • 133 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号