我有這個問題,流程從一個初始日期開始,比如說 2022-04-14,我必須在這個日期加上十天,但我必須考慮周末和節假日,所以也許如果在初始日期和最終日期之間日期我們有一個周末和一個假期,最終日期將是 2022-04-27。如果初始日期從周末或節假日開始,也必須考慮周到。
這就是問題。
我的第一種方法是創建一個回圈,在第一天加上十天之間每天檢查,每個星期六、星期日和節假日總和一天,所以我將有十天加三天,這個結果將添加到我的初始日期到最后計算最終日期。
我的問題是,是否有其他更有效的解決方案或實作?因為這可能在將來會被很多人使用。
uj5u.com熱心網友回復:
另外不要忘記在添加累積的額外天數(周末和節假日)時,這些天數可能涵蓋新的周末和節假日,因此您必須“遞回”執行此操作。
最簡單的解決方案
最簡單的解決方案可以從初始日期開始,將其增加一天,然后檢查每個日期是否是可跳過的(周末或節假日)。如果沒有,請減少天數,然后重復,直到您添加了所需的天數。
這就是它的樣子:
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():
- 檢查分數周中周末天數的初始回圈可以用算術計算代替(參見示例)
- 遞回可以用迭代解決方案代替
- 另一種解決方案可以將周末列為假期,因此可以消除計算周末的第一部分(不得包括重復)
uj5u.com熱心網友回復:
我會像這樣計算作業日。不包括假期查找,它沒有回圈并且具有 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
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/464594.html
上一篇:傳遞背景關系的最佳方式
