如何在Golang中计算工作日的数量

huangapple go评论81阅读模式
英文:

How to calculate number of business days in Golang

问题

我目前已经使用http://golang.org/pkg/time/来计算天数,但我不知道如何排除周末,只计算工作日。

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	f := time.Date(2015, time.August, 21, 24, 0, 0, 0, time.UTC)
	diff := f.Sub(t)

	// 将 diff 转换为天数
	days := int(diff.Hours() / 24)

	fmt.Printf("days  %d\n", days)
}

请注意,这段代码只计算了两个日期之间的天数差异,并没有排除周末。如果你想要排除周末,你需要编写额外的代码来处理这个逻辑。

英文:

I have this so far to count the number of days using http://golang.org/pkg/time/ but I don't know how to exclude weekends and only count business days.

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.Now()
    f := time.Date(2015, time.August, 21, 24, 0, 0, 0, time.UTC)
    diff := f.Sub(t)

    // convert diff to days
    days := int(diff.Hours() / 24)

    fmt.Printf("days  %d\n", days)
}

答案1

得分: 3

这是一个简单的解决方案。

days := 0
for {
     if (t.Equal(f)) {
         return days
     }
     if (t.Weekday() != time.Saturday && t.Weekday() != time.Sunday) {
          days++
     }
     t = t.Add(time.Hour*24)
}

你可能不想使用原始的 t 变量,但为了保持示例简单,我保留了它。我循环直到 t 等于 f,如果它们相等,我返回我的天数计数。如果它们不相等,我检查确保它是工作日,并增加我的天数计数。我无条件地将起始时间增加一天。

英文:

Here's a simple little solution.

days := 0
for {
     if (t.Equal(f)) {
         return days
     }
     if (t.Weekday() != time.Saturday && t.Weekday() != time.Sunday) {
          days++
     }
     t = t.Add(time.Hour*24)
}

You probably don't want to use your original t variable but keeping the example simple. I loop until t equals f, if they're equal I return my days count. If they're not I check to make sure it's a weekday and increment my days count if it is. I unconditionally add one day to my starting time.

答案2

得分: 3

我想要一个不需要循环来计算天数的解决方案。(md2perpevincegScott Owens发布的答案对我来说都不起作用,计算结果错误。)

以下是一个不需要循环的解决方案:

(该解决方案分别计算完整的周数和剩余的天数,然后将它们相加。)

func calculateWorkingDays(startTime time.Time, endTime time.Time) int {
    // 将日期调整为前一个星期一
    startOffset := weekday(startTime)
    startTime = startTime.AddDate(0, 0, -startOffset)
    endOffset := weekday(endTime)
    endTime = endTime.AddDate(0, 0, -endOffset)

    // 计算周数和天数
    dif := endTime.Sub(startTime)
    weeks := int(math.Round((dif.Hours() / 24) / 7))
    days := -min(startOffset, 5) + min(endOffset, 5)

    // 计算总天数
    return weeks*5 + days
}

func weekday(d time.Time) int {
    wd := d.Weekday()
    if wd == time.Sunday {
        return 6
    }
    return int(wd) - 1
}

func min(a int, b int) int {
    if a < b {
        return a
    }
    return b
}

首先,计算开始日期和结束日期的星期偏移量,并将两个日期调整为前一个星期一。然后,计算完整的周数和剩余的天数。(如果偏移量大于5(星期五),则只使用5天。)最后,计算总天数。

英文:

I wanted a solution which doesn't need a loop to calculate the number of days. (The posted answers by md2perpe, vinceg and Scott Owens however didn't work for me and calculated wrong results.)

Here is solution which doesn't require a loop:

(The solution calculates full weeks and loose days separately and adds them later.)

func calculateWorkingDays(startTime time.Time, endTime time.Time) int {
    // Reduce dates to previous Mondays
	startOffset := weekday(startTime)
	startTime = startTime.AddDate(0, 0, -startOffset)
	endOffset := weekday(endTime)
	endTime = endTime.AddDate(0, 0, -endOffset)

    // Calculate weeks and days
	dif := endTime.Sub(startTime)
	weeks := int(math.Round((dif.Hours() / 24) / 7))
	days := -min(startOffset, 5) + min(endOffset, 5)

    // Calculate total days
	return weeks*5 + days
}

func weekday(d time.Time) int {
	wd := d.Weekday()
	if wd == time.Sunday {
		return 6
	}
	return int(wd) - 1
}

func min(a int, b int) int {
	if a &lt; b {
		return a
	}
	return b
}

First, the weekday offset of the start and end date are calculated and both dates are reduced to the previous Monday. Then, the number of full weeks and loose days are calculated. (If a offset it larger than 5(Friday), only 5 days are used.) Finally, the total days are calculated.

答案3

得分: 1

我知道这个已经标记为完成了一段时间,但由于各种原因,我最近不得不计算两个日期之间的工作日数量,这两个日期可能相隔几十年。

所以如果有人需要,这是一个O(1)的解决方案。

func GetWeekdaysBetween(start, end time.Time) int {
    offset := -int(start.Weekday())
    start = start.AddDate(0, 0, -int(start.Weekday()))

    offset += int(end.Weekday())
    if end.Weekday() == time.Sunday {
        offset++
    }
    end = end.AddDate(0, 0, -int(end.Weekday()))

    dif := end.Sub(start).Truncate(time.Hour * 24)
    weeks := float64((dif.Hours() / 24) / 7)
    return int(math.Round(weeks)*5) + offset
}

如果你想要的是工作日,这个函数并没有考虑公共假日,这需要额外的计划。

英文:

I know this has been marked done for a while, but for various reasons I recently had to get the number of weekdays between two dates that could be decades apart.

So if anyone needs it, an O(1) solution.

func GetWeekdaysBetween(start, end time.Time) int {
	offset := -int(start.Weekday())
	start = start.AddDate(0, 0, -int(start.Weekday()))

	offset += int(end.Weekday())
	if end.Weekday() == time.Sunday {
		offset++
	}
	end = end.AddDate(0, 0, -int(end.Weekday()))

	dif := end.Sub(start).Truncate(time.Hour * 24)
	weeks := float64((dif.Hours() / 24) / 7)
	return int(math.Round(weeks)*5) + offset
}

If your wanting business days though, this doesn't account for public holidays, which would require some additional planning.

答案4

得分: 0

这是一个解决方案。感谢evanmcdonnalluc的回答。

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Date(2015, time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond()*0, time.UTC)
	fmt.Printf("%s\n",t)
	f := time.Date(2015, time.August, 22, time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond()*0, time.UTC)
	fmt.Printf("%s\n",f)

	days := 0
	for {
		if (t.Equal(f)) {
			break
		}
		if (t.Weekday() != 6 && t.Weekday() != 0) {
			days++
		}
    	t = t.Add(time.Hour*24)
	}

	fmt.Printf("days %d\n", days)
}

输出结果:32

英文:

Here is a solution. Thank you evanmcdonnal and luc for answering.

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func main() {
	t := time.Date(2015, time.Now().Month(), time.Now().Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond()*0, time.UTC)
	fmt.Printf(&quot;%s\n&quot;,t)
	f := time.Date(2015, time.August, 22, time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond()*0, time.UTC)
	fmt.Printf(&quot;%s\n&quot;,f)

	days := 0
	for {
		if (t.Equal(f)) {
			break
		}
		if (t.Weekday() != 6 &amp;&amp; t.Weekday() != 0) {
			days++
		}
    	t = t.Add(time.Hour*24)
	}

	fmt.Printf(&quot;days %d\n&quot;, days)
}

Output: 32

答案5

得分: 0

更好的做法是使用AddDate来进行大于24小时的时间计算:

package main

import (
	"fmt"
	"time"
)

func AddWorkDays(t time.Time, workdays int) time.Time {
	curDate := t
	for curWorkdays := 0; curWorkdays <= workdays; {
		curDate = curDate.AddDate(0, 0, 1)
		if curDate.Weekday() != 6 && t.Weekday() != 7 {
			curWorkdays++
		}
	}
	return curDate
}

func main() {
	now := time.Now()
	workdays := 3
	t := AddWorkDays(now, workdays)
	fmt.Printf("  now %-9v %v\n", now.Weekday(), now)
	fmt.Printf("later %-9v %v\n", t.Weekday(), t)
}

我回答时的输出结果是:

  now Friday    2017-03-10 21:33:28.395198671 +0000 UTC
later Wednesday 2017-03-15 21:33:28.395198671 +0000 UTC
英文:

Better to use AddDate for time calculations greater than 24 hours:

package main

import (
	&quot;fmt&quot;
	&quot;time&quot;
)

func AddWorkDays(t time.Time, workdays int) time.Time {
	curDate := t
	for curWorkdays := 0; curWorkdays &lt;= workdays; {
		curDate = curDate.AddDate(0, 0, 1)
		if curDate.Weekday() != 6 &amp;&amp; t.Weekday() != 7 {
			curWorkdays++
		}
	}
	return curDate
}

func main() {
	now := time.Now()
	workdays := 3
	t := AddWorkDays(now, workdays)
	fmt.Printf(&quot;  now %-9v %v\n&quot;, now.Weekday(), now)
	fmt.Printf(&quot;later %-9v %v\n&quot;, t.Weekday(), t)
}

Output on day I'm answering:

  now Friday    2017-03-10 21:33:28.395198671 +0000 UTC
later Wednesday 2017-03-15 21:33:28.395198671 +0000 UTC

答案6

得分: 0

意识到我们正在计算一个积分,并记住积分通常使用原始函数来计算,可以将其视为从某个固定时间开始的积分,我们可以编写以下代码。

函数primitive()给出了自2000年1月3日以来的工作日数,选择为星期一,以简化公式。两个日期之间的工作日数由这两天的primitive()之差给出。

package main

import (
    "fmt"
    "time"
)

type Date struct {
    year  int
    month time.Month
    day   int
}

func date2unix(d Date, loc *time.Location) int64 {
    return time.Date(d.year, d.month, d.day, 0, 0, 0, 0, loc).Unix()
}

func primitive(d Date, loc *time.Location) int64 {
    // 2000年1月3日是星期一
    base := Date{2000, time.January, 3}
    seconds := date2unix(d, loc) - date2unix(base, loc)
    weeks := seconds / (7 * 24 * 60 * 60)
    seconds_into_week := seconds % (7 * 24 * 60 * 60)
    workdays := seconds_into_week / (24 * 60 * 60)
    if workdays > 5 {
        workdays = 5
    }

    return 5*weeks + workdays
}

func DayCountExcludingWeekends(from, to Date, loc *time.Location) int {
    return int(primitive(to, loc) - primitive(from, loc))
}

func main() {
    loc, err := time.LoadLocation("Europe/Stockholm")
    if err != nil {
        panic(err)
    }

    f := Date{2017, 6, 28}
    t := Date{2017, 7, 6}

    fmt.Println(DayCountExcludingWeekends(f, t, loc))
}

你可以在以下链接中查看完整代码:

英文:

Realizing that we are calculating an integral and remembering that integrals are usually calculated using primitive functions, which can be taken to be the integral from some fixed time, we can write the following code.

The function primitive() gives the number of workdays since 3 January 2000, chosen to be a Monday to simplify the formula. The number of workdays between two dates is then given by the difference of primitive() for the two days.

package main
import (
&quot;fmt&quot;
&quot;time&quot;
)
type Date struct {
year int
month time.Month
day int
}
func date2unix(d Date, loc *time.Location) int64 {
return time.Date(d.year, d.month, d.day, 0, 0, 0, 0, loc).Unix()
}
func primitive(d Date, loc *time.Location) int64 {
// 3 January 2000 was a Monday
base := Date{2000, time.January, 3}
seconds := date2unix(d, loc) - date2unix(base, loc)
weeks := seconds / (7*24*60*60)
seconds_into_week := seconds % (7*24*60*60)
workdays := seconds_into_week / (24*60*60)
if workdays &gt; 5 {
workdays = 5
}
return 5*weeks + workdays
}
func DayCountExcludingWeekends(from, to Date, loc *time.Location) int {
return int(primitive(to, loc) - primitive(from, loc))
}
func main() {
loc, err := time.LoadLocation(&quot;Europe/Stockholm&quot;)
if err != nil {
panic(err)
}
f := Date{2017, 6, 28}
t := Date{2017, 7,  6}
fmt.Println(DayCountExcludingWeekends(f, t, loc))
}

答案7

得分: 0

CalcBusinessDays函数计算两个日期之间的工作日,不考虑假期。
计算结果包括两个日期在内。

func CalcBusinessDays(from *time.Time, to *time.Time) int {

    totalDays := float32(to.Sub(*from) / (24 * time.Hour))
    weekDays := float32(from.Weekday()) - float32(to.Weekday())
    businessDays := int(1 + (totalDays*5-weekDays*2)/7)
    if to.Weekday() == time.Saturday {
        businessDays--
    }
    if from.Weekday() == time.Sunday {
        businessDays--
    }

    return businessDays
}
英文:

CalcBusinessDays calculates the business days between two dates, not accounting for the holidays.
The calculation is inclusive for both dates.

func CalcBusinessDays(from *time.Time, to *time.Time) int {
totalDays := float32(to.Sub(*from) / (24 * time.Hour))
weekDays := float32(from.Weekday()) - float32(to.Weekday())
businessDays := int(1 + (totalDays*5-weekDays*2)/7)
if to.Weekday() == time.Saturday {
businessDays--
}
if from.Weekday() == time.Sunday {
businessDays--
}
return businessDays
}

答案8

得分: 0

这是一个旧问题,但你可能想考虑一种不使用循环的解决方案:

import (
	"time"
)

func AddBusinessDays(date time.Time, noOfDays int) time.Time {
	actualDateAfterNoOfDays := date.Add(time.Duration(noOfDays) * 24 * time.Hour)
	noOfWorkingDaysInBetween := GetWorkingDaysInBetween(date, actualDateAfterNoOfDays)

	additionalDaysNeeded := noOfDays - noOfWorkingDaysInBetween

	fmt.Println(date, ",", actualDateAfterNoOfDays, ",", noOfWorkingDaysInBetween)
	if additionalDaysNeeded <= 0 {
		return actualDateAfterNoOfDays
	}

	return actualDateAfterNoOfDays.Add(
		AddBusinessDays(actualDateAfterNoOfDays, additionalDaysNeeded).Sub(actualDateAfterNoOfDays),
	)
}

// GetWorkingDaysInBetween ...
func GetWorkingDaysInBetween(s, e time.Time) int {
	sW := int(s.Weekday())
	n := GetDaysInBetween(s, e)
	saturdays := DayOfWeekCount(sW, 6, n)
	sundays := DayOfWeekCount(sW, 0, n)

	// formula excludes end date thus must add one if the date is a weekday
	additional := 1
	if e.Weekday() == time.Saturday || e.Weekday() == time.Sunday {
		additional = 0
	}
	return n - (saturdays + sundays) + additional
}

func GetDaysInBetween(s, e time.Time) int {
	return int(e.Sub(s).Hours() / 24.0)
}

func DayOfWeekCount(startDayOfWeek, targetDayOfWeek, noOfDays int) int {
	// Gives number of a weekday given; a starting weekday and number of days
	// startDayOfWeek, targetDayOfWeek are indexed 0 ([0..6] => [Sunday ... Saturday])
	x := CircularSequenceDistance(startDayOfWeek, targetDayOfWeek, 7)
	y := int(float64(noOfDays) / 7)
	z := 0
	z1 := CircularSequenceDistance(startDayOfWeek, ((startDayOfWeek+noOfDays) % 7)-1, 7)
	if z1 >= x {
		z = 1
	}

	return y + z
}

// CircularSequenceDistance
func CircularSequenceDistance(from, to, length int) int {
	return ((to - from) + length) % length
}

请注意,这只是代码的翻译部分,不包括任何其他内容。

英文:

This is old question but might want to consider a solution that does not use a loop:

https://gist.github.com/khirvy019/5c5eea7564c953cabf49da0fb0c35dc8

import (
&quot;time&quot;
)
func AddBusinessDays(date time.Time, noOfDays int) time.Time {
actualDateAfterNoOfDays := date.Add(time.Duration(noOfDays) * 24 * time.Hour)
noOfWorkingDaysInBetween := GetWorkingDaysInBetween(date, actualDateAfterNoOfDays)
additionalDaysNeeded := noOfDays - noOfWorkingDaysInBetween
fmt.Println(date, &quot;,&quot;, actualDateAfterNoOfDays, &quot;,&quot;, noOfWorkingDaysInBetween)
if additionalDaysNeeded &lt;= 0 {
return actualDateAfterNoOfDays
}
return actualDateAfterNoOfDays.Add(
AddBusinessDays(actualDateAfterNoOfDays, additionalDaysNeeded).Sub(actualDateAfterNoOfDays),
)
}
// GetWorkingDaysInBetween ...
func GetWorkingDaysInBetween(s, e time.Time) int {
sW := int(s.Weekday())
n := GetDaysInBetween(s, e)
saturdays := DayOfWeekCount(sW, 6, n)
sundays := DayOfWeekCount(sW, 0, n)
// formula excludes end date thus must add one if the date is a weekday
additional := 1
if e.Weekday() == time.Saturday || e.Weekday() == time.Sunday {
additional = 0
}
return n - (saturdays + sundays) + additional
}
func GetDaysInBetween(s, e time.Time) int {
return int(e.Sub(s).Hours() / 24.0)
}
func DayOfWeekCount(startDayOfWeek, targetDayOfWeek, noOfDays int) int {
// Gives number of a weekday given; a starting weekday and number of days
// startDayOfWeek, targetDayOfWeek are indexed 0 ([0..6] =&gt; [Sunday ... Saturday])
x := CircularSequenceDistance(startDayOfWeek, targetDayOfWeek, 7)
y := int(float64(noOfDays) / 7)
z := 0
z1 := CircularSequenceDistance(startDayOfWeek, ((startDayOfWeek+noOfDays) % 7)-1, 7)
if z1 &gt;= x {
z = 1
}
return y + z
}
// CircularSequenceDistance
func CircularSequenceDistance(from, to, length int) int {
return ((to - from) + length) % length
}

huangapple
  • 本文由 发表于 2015年7月10日 04:01:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/31327124.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定