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()
:
检查小数周中周末天数的初始循环可以用算术计算代替(参见示例)
递归可以用迭代解决方案代替
另一种解决方案可以将周末列为假期,因此可以消除计算周末的第一部分(不得包括重复项)

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
}
- 2 回答
- 0 关注
- 133 浏览
添加回答
举报