두 날짜 사이의 영업일 수를 계산 하시겠습니까?


답변:


122

나는 전에 그런 일이 있었고 해결책이 있습니다. 피할 수있는 사이의 모든 날을 열거하는 것을 피할 것입니다. 위의 답변 중 하나에서 본 것처럼 많은 DateTime 인스턴스를 만드는 것에 대해서도 언급하지 않습니다. 이것은 처리 능력의 낭비입니다. 특히 실제 상황에서 몇 개월의 시간 간격을 조사해야 할 때. 아래의 주석과 함께 내 코드를 참조하십시오.

    /// <summary>
    /// Calculates number of business days, taking into account:
    ///  - weekends (Saturdays and Sundays)
    ///  - bank holidays in the middle of the week
    /// </summary>
    /// <param name="firstDay">First day in the time interval</param>
    /// <param name="lastDay">Last day in the time interval</param>
    /// <param name="bankHolidays">List of bank holidays excluding weekends</param>
    /// <returns>Number of business days during the 'span'</returns>
    public static int BusinessDaysUntil(this DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays)
    {
        firstDay = firstDay.Date;
        lastDay = lastDay.Date;
        if (firstDay > lastDay)
            throw new ArgumentException("Incorrect last day " + lastDay);

        TimeSpan span = lastDay - firstDay;
        int businessDays = span.Days + 1;
        int fullWeekCount = businessDays / 7;
        // find out if there are weekends during the time exceedng the full weeks
        if (businessDays > fullWeekCount*7)
        {
            // we are here to find out if there is a 1-day or 2-days weekend
            // in the time interval remaining after subtracting the complete weeks
            int firstDayOfWeek = (int) firstDay.DayOfWeek;
            int lastDayOfWeek = (int) lastDay.DayOfWeek;
            if (lastDayOfWeek < firstDayOfWeek)
                lastDayOfWeek += 7;
            if (firstDayOfWeek <= 6)
            {
                if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval
                    businessDays -= 2;
                else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval
                    businessDays -= 1;
            }
            else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval
                businessDays -= 1;
        }

        // subtract the weekends during the full weeks in the interval
        businessDays -= fullWeekCount + fullWeekCount;

        // subtract the number of bank holidays during the time interval
        foreach (DateTime bankHoliday in bankHolidays)
        {
            DateTime bh = bankHoliday.Date;
            if (firstDay <= bh && bh <= lastDay)
                --businessDays;
        }

        return businessDays;
    }

Slauma 편집, 2011 년 8 월

좋은 대답! 그래도 버그가 거의 없습니다. 2009 년부터 답변자가 부재 중이므로이 답변을 자유롭게 편집 할 수 있습니다.

위의 코드 는 그렇지 않은 DayOfWeek.Sunday값 을 가지고 있다고 가정합니다 7. 값은 실제로 0입니다. 그것은 예를 들어, 만약 잘못된 계산에 이르게 firstDay하고 lastDay모두 같은 일요일이다. 1이 경우 메서드가 반환 되지만 0.

이 버그에 대한 가장 쉬운 수정 : 다음 firstDayOfWeeklastDayOfWeek같이 선언 된 줄 위의 코드에서 바꿉니다 .

int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday 
    ? 7 : (int)firstDay.DayOfWeek;
int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday
    ? 7 : (int)lastDay.DayOfWeek;

이제 결과는 다음과 같습니다.

  • 금요일-금요일-> 1
  • 토요일-토요일-> 0
  • 일요일-일요일-> 0
  • 금요일-토요일-> 1
  • 금요일 ~ 일요일-> 1
  • 금요일-월요일-> 2
  • 토요일 ~ 월요일-> 1
  • 일요일-월요일-> 1
  • 월요일-월요일-> 1

1
+1 아마도 가장 쉽고 효율적인 방법 일 것입니다 (C ++에서 제공하는 솔루션은 TimeSpan 지원을 사용하지 않고 C #은 일부 작업을 훨씬 쉽게 만듭니다). bankHolidays도 좋은 터치입니다!
RedGlyph

2
또한 다음과 같이 공휴일을 확인하십시오. if (firstDay <= bh && bh <= lastDay && bh.IsWorkingDay ())
Tawani

5
방법 주셔서 감사합니다. 비록, 나는 공휴일 빼기 / 반복 if 문에 다음을 추가해야했다 : && !(bh.DayOfWeek == DayOfWeek.Sunday || bh.DayOfWeek == DayOfWeek.Saturday), 그렇지 않으면 휴일이 주말에 속한다면 같은 날을 두 번 뺄 것이다.
KristianB

Linq 문에 대한 마지막 루프를 변경했습니다. businessDays-= bankHolidays.Select (bankHoliday => bankHoliday.Date) .Count (bh => firstDay <= bh && bh <= lastDay);
JoanComasFdz

1
또한 토요일, 일요일에 주말이없는 국가입니다. 자세한 정보는이 링크를 참조하십시오 : en.wikipedia.org/wiki/Workweek_and_weekend
Gatej Alexandru

104

확인. 정답을 게시 할 때라고 생각합니다.

public static double GetBusinessDays(DateTime startD, DateTime endD)
{
    double calcBusinessDays =
        1 + ((endD - startD).TotalDays * 5 -
        (startD.DayOfWeek - endD.DayOfWeek) * 2) / 7;

    if (endD.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--;
    if (startD.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--;

    return calcBusinessDays;
}

원본 출처 :

http://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/

위에 게시 된 PS Solutions는 어떤 이유로 든 나를 sic하게 만듭니다.


10
잘 했어.하지만 DayOfWeek 열거 형을 int로 캐스트하는 대신 자체적으로 사용 하는가?
Neo

3
진지하게, 최고의 솔루션입니다. Cheers Alec
Mizmor 2011

6
이 함수는 double을 반환하지만 전체 영업일 을 제공하는 것으로 만 신뢰되어야합니다 . 시간이 관련된 부분 일에 대해서는 정답을 반환하지 않습니다.
Pakman 2014 년

4
그냥 말 때문에,이 '1+'는 가정으로 시작 때까지 첫 번째 날의 를 빼고, 마지막 날의 '1+'는 가정 까지 첫날을 마지막 날. 내가 가정 된 이후, 알아낼 걸 렸어요 시작 때까지 첫날을 시작 나에게 더 의미가 만든 마지막 날의.
Jeffry van de Vuurst

11
이것은 정답 이 아닙니다 . 하루는 4까지 끝날 수 있습니다. 거의 맞습니다. 시작일과 종료일이 주말을 둘러싸는시기를 고려하지 않는 것이 가장 까다로운 부분입니다. 시작-끝도 괄호 안에 있으면 안됩니다. 문제와 관련이 없습니다. 이 솔루션의 60 %는 잘못된 것 입니다.
Owl

47

이 질문은 이미 해결 된 것으로 알고 있지만 앞으로 다른 방문자에게 도움이 될 수있는보다 직관적 인 답변을 제공 할 수 있다고 생각했습니다.

내 생각은 다음과 같습니다.

public int GetWorkingDays(DateTime from, DateTime to)
{
    var dayDifference = (int)to.Subtract(from).TotalDays;
    return Enumerable
        .Range(1, dayDifference)
        .Select(x => from.AddDays(x))
        .Count(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday);
}

이것은 내 원래 제출이었습니다.

public int GetWorkingDays(DateTime from, DateTime to)
{
    var totalDays = 0;
    for (var date = from; date < to; date = date.AddDays(1))
    {
        if (date.DayOfWeek != DayOfWeek.Saturday
            && date.DayOfWeek != DayOfWeek.Sunday)
            totalDays++;
    }

    return totalDays;
}

"여기서"수 "계산"수를 단축하기
bygrace

1
훨씬 더 명확하고 열거 된 솔루션은 은행 휴일을 없애는 데 도움이됩니다. 하지만 대량으로 훨씬 느립니다. LINQPad에서 100 만 반복 루프의 90 일 간격에 대한 작업 일을 계산하는 데이 솔루션을 사용하면 10 초가 걸리며 허용 된 답변 또는 Alec Pojidaev의 훨씬 더 좋은 답변을 사용하면 약 0.2 초가 걸립니다.
Whelkaholism 2014 년

포함하려면 코드는 다음과 같아야합니다. return Enumerable .Range (0, dayDifference + 1) ...
Edza

과거의 날짜를 반환하지 않습니다. 영업일 기준 -18 일.
iwtu

@iwtu 이것은 to > from. 그게 문제일까요?
Alpha

22

다음과 같이 DateTime에 확장 메서드를 정의합니다.

public static class DateTimeExtensions
{
    public static bool IsWorkingDay(this DateTime date)
    {
        return date.DayOfWeek != DayOfWeek.Saturday
            && date.DayOfWeek != DayOfWeek.Sunday;
    }
}

그런 다음 Where 절 내에서 is를 사용하여보다 광범위한 날짜 목록을 필터링합니다.

var allDates = GetDates(); // method which returns a list of dates

// filter dates by working day's  
var countOfWorkDays = allDates
     .Where(day => day.IsWorkingDay())
     .Count() ;

계속해서 시간 범위를 연장하여 사용할 수 있도록하지 않겠습니까? 그가 날짜 목록이 아니라 두 날짜 사이의 거리를 사용하고 싶다고 말했기 때문입니까?
WesleyJohnson

두 날짜 사이의 거리는 그 사이의 일 수이므로 Count ()로 충분합니다.
Carles Company

3
왜 이것이 적절한 대답인지 모르겠습니다 ... 그는 개별 날짜 목록이없고 두 개의 날짜가 있으며 그 사이의 영업일 수를 찾고 싶어합니다. 이 솔루션을 사용 하려면 twyp 사이의 모든 날짜 목록을 생성하는 또 다른 기능 을 제공해야합니다 .
Adam Robinson

1
adam, 이것은 개념을 설명하는 데 필요한 최소한의 코드를 사용한 간단한 예입니다. 내 원래 대답에는 "GetDates"함수로 추상화 한 allDates 목록을 채우는 루프도 포함되었습니다. IsWorkingDay 테스트는 LINQ 문에서 해당 루프로 쉽게 이동할 수 있습니다. 나는 개인적으로 그것이 일어나는 일에 대해 매우 인간이 읽을 수 있기 때문에 지금의 모습을 좋아합니다.
Qwerty

10
Count to Count를 변경하고 Count를 제거하면 단락 될 수 있습니다.
반복적

12

다음 코드를 사용하여 은행 공휴일을 계산했습니다.

public class WorkingDays
{
    public List<DateTime> GetHolidays()
    {
        var client = new WebClient();
        var json = client.DownloadString("https://www.gov.uk/bank-holidays.json");
        var js = new JavaScriptSerializer();
        var holidays = js.Deserialize <Dictionary<string, Holidays>>(json);
        return holidays["england-and-wales"].events.Select(d => d.date).ToList();
    }

    public int GetWorkingDays(DateTime from, DateTime to)
    {
        var totalDays = 0;
        var holidays = GetHolidays();
        for (var date = from.AddDays(1); date <= to; date = date.AddDays(1))
        {
            if (date.DayOfWeek != DayOfWeek.Saturday
                && date.DayOfWeek != DayOfWeek.Sunday
                && !holidays.Contains(date))
                totalDays++;
        }

        return totalDays;
    }
}

public class Holidays
{
    public string division { get; set; }
    public List<Event> events { get; set; }
}

public class Event
{
    public DateTime date { get; set; }
    public string notes { get; set; }
    public string title { get; set; }
}

그리고 단위 테스트 :

[TestClass]
public class WorkingDays
{
    [TestMethod]
    public void SameDayIsZero()
    {
        var service = new WorkingDays();

        var from = new DateTime(2013, 8, 12);

        Assert.AreEqual(0, service.GetWorkingDays(from, from));

    }

    [TestMethod]
    public void CalculateDaysInWorkingWeek()
    {
        var service = new WorkingDays();

        var from = new DateTime(2013, 8, 12);
        var to = new DateTime(2013, 8, 16);

        Assert.AreEqual(4, service.GetWorkingDays(from, to), "Mon - Fri = 4");

        Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Mon - Tues = 1");
    }

    [TestMethod]
    public void NotIncludeWeekends()
    {
        var service = new WorkingDays();

        var from = new DateTime(2013, 8, 9);
        var to = new DateTime(2013, 8, 16);

        Assert.AreEqual(5, service.GetWorkingDays(from, to), "Fri - Fri = 5");

        Assert.AreEqual(2, service.GetWorkingDays(from, new DateTime(2013, 8, 13)), "Fri - Tues = 2");
        Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 12)), "Fri - Mon = 1");
    }

    [TestMethod]
    public void AccountForHolidays()
    {
        var service = new WorkingDays();

        var from = new DateTime(2013, 8, 23);

        Assert.AreEqual(0, service.GetWorkingDays(from, new DateTime(2013, 8, 26)), "Fri - Mon = 0");

        Assert.AreEqual(1, service.GetWorkingDays(from, new DateTime(2013, 8, 27)), "Fri - Tues = 1");
    }
}

(var date = from.AddDays (1); date <= to; date = date.AddDays (1))에 대해 "from"@에 1 일을 추가하여 계산을 시작하는 이유는 무엇입니까?
Oncel Umut TURER

6

글쎄, 이것은 맞아 죽었다. :) 그러나 나는 조금 다른 것이 필요했기 때문에 여전히 다른 대답을 제공 할 것입니다. 이 솔루션은 업무 시간 범위를 반환한다는 점에서 다릅니다. 시작과 끝 사이에, 당신은 일의 영업 시간을 설정하고 휴일을 추가 할 수 있습니다. 따라서이를 사용하여 하루 이내, 며칠 동안, 주말 및 휴일에도 발생하는지 계산할 수 있습니다. 반환 된 TimeSpan 개체에서 필요한 정보를 가져 오는 것만으로 영업일 만 가져 오거나 그렇지 않을 수도 있습니다. 그리고 요일 목록을 사용하는 방식을 보면 일반적인 Sat 및 Sun이 아닌 경우 휴무일 목록을 추가하는 것이 얼마나 쉬운 지 알 수 있습니다. 그리고 저는 1 년 동안 테스트를했는데 매우 빠른 것 같습니다.

코드 붙여 넣기가 정확하기를 바랍니다. 그러나 나는 그것이 효과가 있다는 것을 압니다.

public static TimeSpan GetBusinessTimespanBetween(
    DateTime start, DateTime end,
    TimeSpan workdayStartTime, TimeSpan workdayEndTime,
    List<DateTime> holidays = null)
{
    if (end < start)
        throw new ArgumentException("start datetime must be before end datetime.");

    // Just create an empty list for easier coding.
    if (holidays == null) holidays = new List<DateTime>();

    if (holidays.Where(x => x.TimeOfDay.Ticks > 0).Any())
        throw new ArgumentException("holidays can not have a TimeOfDay, only the Date.");

    var nonWorkDays = new List<DayOfWeek>() { DayOfWeek.Saturday, DayOfWeek.Sunday };

    var startTime = start.TimeOfDay;

    // If the start time is before the starting hours, set it to the starting hour.
    if (startTime < workdayStartTime) startTime = workdayStartTime;

    var timeBeforeEndOfWorkDay = workdayEndTime - startTime;

    // If it's after the end of the day, then this time lapse doesn't count.
    if (timeBeforeEndOfWorkDay.TotalSeconds < 0) timeBeforeEndOfWorkDay = new TimeSpan();
    // If start is during a non work day, it doesn't count.
    if (nonWorkDays.Contains(start.DayOfWeek)) timeBeforeEndOfWorkDay = new TimeSpan();
    else if (holidays.Contains(start.Date)) timeBeforeEndOfWorkDay = new TimeSpan();

    var endTime = end.TimeOfDay;

    // If the end time is after the ending hours, set it to the ending hour.
    if (endTime > workdayEndTime) endTime = workdayEndTime;

    var timeAfterStartOfWorkDay = endTime - workdayStartTime;

    // If it's before the start of the day, then this time lapse doesn't count.
    if (timeAfterStartOfWorkDay.TotalSeconds < 0) timeAfterStartOfWorkDay = new TimeSpan();
    // If end is during a non work day, it doesn't count.
    if (nonWorkDays.Contains(end.DayOfWeek)) timeAfterStartOfWorkDay = new TimeSpan();
    else if (holidays.Contains(end.Date)) timeAfterStartOfWorkDay = new TimeSpan();

    // Easy scenario if the times are during the day day.
    if (start.Date.CompareTo(end.Date) == 0)
    {
        if (nonWorkDays.Contains(start.DayOfWeek)) return new TimeSpan();
        else if (holidays.Contains(start.Date)) return new TimeSpan();
        return endTime - startTime;
    }
    else
    {
        var timeBetween = end - start;
        var daysBetween = (int)Math.Floor(timeBetween.TotalDays);
        var dailyWorkSeconds = (int)Math.Floor((workdayEndTime - workdayStartTime).TotalSeconds);

        var businessDaysBetween = 0;

        // Now the fun begins with calculating the actual Business days.
        if (daysBetween > 0)
        {
            var nextStartDay = start.AddDays(1).Date;
            var dayBeforeEnd = end.AddDays(-1).Date;
            for (DateTime d = nextStartDay; d <= dayBeforeEnd; d = d.AddDays(1))
            {
                if (nonWorkDays.Contains(d.DayOfWeek)) continue;
                else if (holidays.Contains(d.Date)) continue;
                businessDaysBetween++;
            }
        }

        var dailyWorkSecondsToAdd = dailyWorkSeconds * businessDaysBetween;

        var output = timeBeforeEndOfWorkDay + timeAfterStartOfWorkDay;
        output = output + new TimeSpan(0, 0, dailyWorkSecondsToAdd);

        return output;
    }
}

다음은 테스트 코드입니다. 테스트 코드가 작동하려면 DateHelper 라는 클래스에이 함수를 넣어야합니다 .

[TestMethod]
public void TestGetBusinessTimespanBetween()
{
    var workdayStart = new TimeSpan(8, 0, 0);
    var workdayEnd = new TimeSpan(17, 0, 0);

    var holidays = new List<DateTime>()
    {
        new DateTime(2018, 1, 15), // a Monday
        new DateTime(2018, 2, 15) // a Thursday
    };

    var testdata = new[]
    {
        new
        {
            expectedMinutes = 0,
            start = new DateTime(2016, 10, 19, 9, 50, 0),
            end = new DateTime(2016, 10, 19, 9, 50, 0)
        },
        new
        {
            expectedMinutes = 10,
            start = new DateTime(2016, 10, 19, 9, 50, 0),
            end = new DateTime(2016, 10, 19, 10, 0, 0)
        },
        new
        {
            expectedMinutes = 5,
            start = new DateTime(2016, 10, 19, 7, 50, 0),
            end = new DateTime(2016, 10, 19, 8, 5, 0)
        },
        new
        {
            expectedMinutes = 5,
            start = new DateTime(2016, 10, 19, 16, 55, 0),
            end = new DateTime(2016, 10, 19, 17, 5, 0)
        },
        new
        {
            expectedMinutes = 15,
            start = new DateTime(2016, 10, 19, 16, 50, 0),
            end = new DateTime(2016, 10, 20, 8, 5, 0)
        },
        new
        {
            expectedMinutes = 10,
            start = new DateTime(2016, 10, 19, 16, 50, 0),
            end = new DateTime(2016, 10, 20, 7, 55, 0)
        },
        new
        {
            expectedMinutes = 5,
            start = new DateTime(2016, 10, 19, 17, 10, 0),
            end = new DateTime(2016, 10, 20, 8, 5, 0)
        },
        new
        {
            expectedMinutes = 0,
            start = new DateTime(2016, 10, 19, 17, 10, 0),
            end = new DateTime(2016, 10, 20, 7, 5, 0)
        },
        new
        {
            expectedMinutes = 545,
            start = new DateTime(2016, 10, 19, 12, 10, 0),
            end = new DateTime(2016, 10, 20, 12, 15, 0)
        },
        // Spanning multiple weekdays
        new
        {
            expectedMinutes = 835,
            start = new DateTime(2016, 10, 19, 12, 10, 0),
            end = new DateTime(2016, 10, 21, 8, 5, 0)
        },
        // Spanning multiple weekdays
        new
        {
            expectedMinutes = 1375,
            start = new DateTime(2016, 10, 18, 12, 10, 0),
            end = new DateTime(2016, 10, 21, 8, 5, 0)
        },
        // Spanning from a Thursday to a Tuesday, 5 mins short of complete day.
        new
        {
            expectedMinutes = 1615,
            start = new DateTime(2016, 10, 20, 12, 10, 0),
            end = new DateTime(2016, 10, 25, 12, 5, 0)
        },
        // Spanning from a Thursday to a Tuesday, 5 mins beyond complete day.
        new
        {
            expectedMinutes = 1625,
            start = new DateTime(2016, 10, 20, 12, 10, 0),
            end = new DateTime(2016, 10, 25, 12, 15, 0)
        },
        // Spanning from a Friday to a Monday, 5 mins beyond complete day.
        new
        {
            expectedMinutes = 545,
            start = new DateTime(2016, 10, 21, 12, 10, 0),
            end = new DateTime(2016, 10, 24, 12, 15, 0)
        },
        // Spanning from a Friday to a Monday, 5 mins short complete day.
        new
        {
            expectedMinutes = 535,
            start = new DateTime(2016, 10, 21, 12, 10, 0),
            end = new DateTime(2016, 10, 24, 12, 5, 0)
        },
        // Spanning from a Saturday to a Monday, 5 mins short complete day.
        new
        {
            expectedMinutes = 245,
            start = new DateTime(2016, 10, 22, 12, 10, 0),
            end = new DateTime(2016, 10, 24, 12, 5, 0)
        },
        // Spanning from a Saturday to a Sunday, 5 mins beyond complete day.
        new
        {
            expectedMinutes = 0,
            start = new DateTime(2016, 10, 22, 12, 10, 0),
            end = new DateTime(2016, 10, 23, 12, 15, 0)
        },
        // Times within the same Saturday.
        new
        {
            expectedMinutes = 0,
            start = new DateTime(2016, 10, 22, 12, 10, 0),
            end = new DateTime(2016, 10, 23, 12, 15, 0)
        },
        // Spanning from a Saturday to the Sunday next week.
        new
        {
            expectedMinutes = 2700,
            start = new DateTime(2016, 10, 22, 12, 10, 0),
            end = new DateTime(2016, 10, 30, 12, 15, 0)
        },
        // Spanning a year.
        new
        {
            expectedMinutes = 143355,
            start = new DateTime(2016, 10, 22, 12, 10, 0),
            end = new DateTime(2017, 10, 30, 12, 15, 0)
        },
        // Spanning a year with 2 holidays.
        new
        {
            expectedMinutes = 142815,
            start = new DateTime(2017, 10, 22, 12, 10, 0),
            end = new DateTime(2018, 10, 30, 12, 15, 0)
        },
    };

    foreach (var item in testdata)
    {
        Assert.AreEqual(item.expectedMinutes,
            DateHelper.GetBusinessTimespanBetween(
                item.start, item.end,
                workdayStart, workdayEnd,
                holidays)
                .TotalMinutes);
    }
}

5

이 솔루션은 반복을 방지하고 + ve 및 -ve 주일 차이에 대해 작동하며 더 느린 주중 계산 방법에 대한 회귀를위한 단위 테스트 스위트를 포함합니다. 또한 평일을 추가하는 간결한 방법도 포함되어 있습니다.

단위 테스트는 작은 기간과 큰 기간을 모두 포함하는 모든 시작 / 종료 요일 조합을 철저히 테스트하기 위해 수천 개의 날짜 조합을 포함합니다.

중대한 : 시작일을 제외하고 종료일을 포함하여 일수를 계산한다고 가정합니다. 포함 / 제외하는 특정 시작 / 종료일이 결과에 영향을 미치므로 평일을 계산할 때 중요합니다. 이렇게하면 동일한 2 일 간의 차이가 항상 0이고 일반적으로 현재 시작일 (종종 오늘)에 대한 답변이 정확하고 전체 종료일 (예 : 기한).

참고 :이 코드는 휴일에 대한 추가 조정이 필요하지만 위의 가정에 따라이 코드는 시작일의 휴일을 제외해야합니다.

평일 추가 :

private static readonly int[,] _addOffset = 
{
  // 0  1  2  3  4
    {0, 1, 2, 3, 4}, // Su  0
    {0, 1, 2, 3, 4}, // M   1
    {0, 1, 2, 3, 6}, // Tu  2
    {0, 1, 4, 5, 6}, // W   3
    {0, 1, 4, 5, 6}, // Th  4
    {0, 3, 4, 5, 6}, // F   5
    {0, 2, 3, 4, 5}, // Sa  6
};

public static DateTime AddWeekdays(this DateTime date, int weekdays)
{
    int extraDays = weekdays % 5;
    int addDays = weekdays >= 0
        ? (weekdays / 5) * 7 + _addOffset[(int)date.DayOfWeek, extraDays]
        : (weekdays / 5) * 7 - _addOffset[6 - (int)date.DayOfWeek, -extraDays];
    return date.AddDays(addDays);
}

요일 차이 계산 :

static readonly int[,] _diffOffset = 
{
  // Su M  Tu W  Th F  Sa
    {0, 1, 2, 3, 4, 5, 5}, // Su
    {4, 0, 1, 2, 3, 4, 4}, // M 
    {3, 4, 0, 1, 2, 3, 3}, // Tu
    {2, 3, 4, 0, 1, 2, 2}, // W 
    {1, 2, 3, 4, 0, 1, 1}, // Th
    {0, 1, 2, 3, 4, 0, 0}, // F 
    {0, 1, 2, 3, 4, 5, 0}, // Sa
};

public static int GetWeekdaysDiff(this DateTime dtStart, DateTime dtEnd)
{
    int daysDiff = (int)(dtEnd - dtStart).TotalDays;
    return daysDiff >= 0
        ? 5 * (daysDiff / 7) + _diffOffset[(int) dtStart.DayOfWeek, (int) dtEnd.DayOfWeek]
        : 5 * (daysDiff / 7) - _diffOffset[6 - (int) dtStart.DayOfWeek, 6 - (int) dtEnd.DayOfWeek];
}

스택 오버플로에 대한 대부분의 다른 솔루션이 느리거나 (반복적) 지나치게 복잡하고 많은 솔루션이 잘못되었음을 발견했습니다. 이야기의 도덕은 ... 철저히 테스트 하지 않았다면 믿지 마세요 !!

NUnit 조합 테스트ShouldBe NUnit 확장을 기반으로 한 단위 테스트 .

[TestFixture]
public class DateTimeExtensionsTests
{
    /// <summary>
    /// Exclude start date, Include end date
    /// </summary>
    /// <param name="dtStart"></param>
    /// <param name="dtEnd"></param>
    /// <returns></returns>
    private IEnumerable<DateTime> GetDateRange(DateTime dtStart, DateTime dtEnd)
    {
        Console.WriteLine(@"dtStart={0:yy-MMM-dd ddd}, dtEnd={1:yy-MMM-dd ddd}", dtStart, dtEnd);

        TimeSpan diff = dtEnd - dtStart;
        Console.WriteLine(diff);

        if (dtStart <= dtEnd)
        {
            for (DateTime dt = dtStart.AddDays(1); dt <= dtEnd; dt = dt.AddDays(1))
            {
                Console.WriteLine(@"dt={0:yy-MMM-dd ddd}", dt);
                yield return dt;
            }
        }
        else
        {
            for (DateTime dt = dtStart.AddDays(-1); dt >= dtEnd; dt = dt.AddDays(-1))
            {
                Console.WriteLine(@"dt={0:yy-MMM-dd ddd}", dt);
                yield return dt;
            }
        }
    }

    [Test, Combinatorial]
    public void TestGetWeekdaysDiff(
        [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)]
        int startDay,
        [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)]
        int endDay,
        [Values(7)]
        int startMonth,
        [Values(7)]
        int endMonth)
    {
        // Arrange
        DateTime dtStart = new DateTime(2016, startMonth, startDay);
        DateTime dtEnd = new DateTime(2016, endMonth, endDay);

        int nDays = GetDateRange(dtStart, dtEnd)
            .Count(dt => dt.DayOfWeek != DayOfWeek.Saturday && dt.DayOfWeek != DayOfWeek.Sunday);

        if (dtEnd < dtStart) nDays = -nDays;

        Console.WriteLine(@"countBusDays={0}", nDays);

        // Act / Assert
        dtStart.GetWeekdaysDiff(dtEnd).ShouldBe(nDays);
    }

    [Test, Combinatorial]
    public void TestAddWeekdays(
        [Values(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)]
        int startDay,
        [Values(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 20, 30)]
        int weekdays)
    {
        DateTime dtStart = new DateTime(2016, 7, startDay);
        DateTime dtEnd1 = dtStart.AddWeekdays(weekdays);     // ADD
        dtStart.GetWeekdaysDiff(dtEnd1).ShouldBe(weekdays);  

        DateTime dtEnd2 = dtStart.AddWeekdays(-weekdays);    // SUBTRACT
        dtStart.GetWeekdaysDiff(dtEnd2).ShouldBe(-weekdays);
    }
}

이에 대한 아이디어는 스택 오버플로에서 찾은 SQL 솔루션에서 나왔습니다. 그들의 아이디어는 견고했지만 슬프게도 버그가있었습니다. + ve 값에 대해서는 작동했지만 -ve 값에 대해서는 조회 테이블 매핑이 잘못되었습니다.
토니 O'Hagan

4

여기에 스웨덴 공휴일이 포함 된 몇 가지 코드가 있지만 계산할 공휴일을 조정할 수 있습니다. 제거 할 수있는 제한을 추가했지만 웹 기반 시스템을위한 것이기 때문에 누구도 프로세스를 독차지하기 위해 엄청난 날짜를 입력하는 것을 원하지 않았습니다.

  public static int GetWorkdays(DateTime from ,DateTime to)
    {
        int limit = 9999;
        int counter = 0;
        DateTime current = from;
        int result = 0;

        if (from > to)
        {
            DateTime temp = from;
            from = to;
            to = temp;
        }

        if (from >= to)
        {
            return 0;
        }


        while (current <= to && counter < limit)
        {
            if (IsSwedishWorkday(current))
            {
                result++;
            }
            current = current.AddDays(1);
            counter++;

        }
        return result;
    }


    public static bool IsSwedishWorkday(DateTime date)
    {
        return (!IsSwedishHoliday(date) && date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday);
    }

    public static bool IsSwedishHoliday(DateTime date)
    {
        return (
        IsSameDay(GetEpiphanyDay(date.Year), date) ||
        IsSameDay(GetMayDay(date.Year), date) ||
        IsSameDay(GetSwedishNationalDay(date.Year), date) ||
        IsSameDay(GetChristmasDay(date.Year), date) ||
        IsSameDay(GetBoxingDay(date.Year), date) ||
        IsSameDay(GetGoodFriday(date.Year), date) ||
        IsSameDay(GetAscensionDay(date.Year), date) ||
        IsSameDay(GetAllSaintsDay(date.Year), date) ||
        IsSameDay(GetMidsummersDay(date.Year), date) ||
        IsSameDay(GetPentecostDay(date.Year), date) ||
        IsSameDay(GetEasterMonday(date.Year), date) ||
        IsSameDay(GetNewYearsDay(date.Year), date) ||
        IsSameDay(GetEasterDay(date.Year), date)
        );
    }

    // Trettondagen
    public static DateTime GetEpiphanyDay(int year)
    {
        return new DateTime(year, 1, 6);
    }

    // Första maj
    public static DateTime GetMayDay(int year)
    {
        return new DateTime(year,5,1);
    }

    // Juldagen
    public static DateTime GetSwedishNationalDay(int year)
    {
        return new DateTime(year, 6, 6);
    }


    // Juldagen
    public static DateTime GetNewYearsDay(int year)
    {
        return new DateTime(year,1,1);
    }

    // Juldagen
    public static DateTime GetChristmasDay(int year)
    {
        return new DateTime(year,12,25);
    }

    // Annandag jul
    public static DateTime GetBoxingDay(int year)
    {
        return new DateTime(year, 12, 26);
    }


    // Långfredagen
    public static DateTime GetGoodFriday(int year)
    {
        return GetEasterDay(year).AddDays(-3);
    }

    // Kristi himmelsfärdsdag
    public static DateTime GetAscensionDay(int year)
    {
        return GetEasterDay(year).AddDays(5*7+4);
    }

    // Midsommar
    public static DateTime GetAllSaintsDay(int year)
    {
        DateTime result = new DateTime(year,10,31);
        while (result.DayOfWeek != DayOfWeek.Saturday)
        {
            result = result.AddDays(1);
        }
        return result;
    }

    // Midsommar
    public static DateTime GetMidsummersDay(int year)
    {
        DateTime result = new DateTime(year, 6, 20);
        while (result.DayOfWeek != DayOfWeek.Saturday)
        {
            result = result.AddDays(1);
        }
        return result;
    }

    // Pingstdagen
    public static DateTime GetPentecostDay(int year)
    {
        return GetEasterDay(year).AddDays(7 * 7);
    }

    // Annandag påsk
    public static DateTime GetEasterMonday(int year)
    {
        return GetEasterDay(year).AddDays(1);
    }
    public static DateTime GetEasterDay(int y)
    {
        double c;
        double n;
        double k;
        double i;
        double j;
        double l;
        double m;
        double d;
        c = System.Math.Floor(y / 100.0);
        n = y - 19 * System.Math.Floor(y / 19.0);
        k = System.Math.Floor((c - 17) / 25.0);
        i = c - System.Math.Floor(c / 4) - System.Math.Floor((c - k) / 3) + 19 * n + 15;
        i = i - 30 * System.Math.Floor(i / 30);
        i = i - System.Math.Floor(i / 28) * (1 - System.Math.Floor(i / 28) * System.Math.Floor(29 / (i + 1)) * System.Math.Floor((21 - n) / 11));
        j = y + System.Math.Floor(y / 4.0) + i + 2 - c + System.Math.Floor(c / 4);
        j = j - 7 * System.Math.Floor(j / 7);
        l = i - j;
        m = 3 + System.Math.Floor((l + 40) / 44);// month
        d = l + 28 - 31 * System.Math.Floor(m / 4);// day

        double days = ((m == 3) ? d : d + 31);

        DateTime result = new DateTime(y, 3, 1).AddDays(days-1);

        return result;
    }

issamedate 함수가 누락되었지만 단순히 public static bool IsSameDay (DateTime date1, DateTime date2) {return date1.Date == date2.Date; }
Choco Smith

새 Date 객체를 인스턴스화하는 대신 int 배열 조회 테이블을 사용할 수 있습니다.
TheRealChx101

3

다음은 빠른 샘플 코드입니다. 이것은 클래스 메소드이므로 클래스 내에서만 작동합니다. 원하는 경우 static서명을 private static(또는 public static) 로 변경하십시오 .

    private IEnumerable<DateTime> GetWorkingDays(DateTime sd, DateTime ed)
    {
        for (var d = sd; d <= ed; d = d.AddDays(1))
            if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday)
                yield return d;
    }

이 메서드는 루프 변수를 만들고 d시작일로 초기화 sd한 다음 반복 할 때마다 하루 씩 증가합니다 ( d = d.AddDays(1)).

그것은 사용하여 원하는 값으로 반환 yield를 생성하는들을 iterator. 반복자의 멋진 점은 모든 값을 IEnumerable메모리에 저장 하지 않고 각 값을 순차적으로 호출하는 것입니다. 즉, 메모리 부족에 대해 걱정할 필요없이 새벽부터 지금까지이 메서드를 호출 할 수 있습니다.


1
이 메서드는 두 날짜 사이의 영업일 수를 반환하지 않고 두 날짜 사이의 영업일을 반환합니다. 당신이 제안하는 코드는 매우 깨끗하고 저는 yield 사용을 좋아하지만 질문에 답하지 않습니다.
Martin

3

두 날짜 사이의 근무일을 계산하고 공휴일을 제외하기 위해 소화하기 쉬운 알고리즘을 많이 검색 한 후 마지막으로이 방법을 사용하기로 결정했습니다.

public static int NumberOfWorkingDaysBetween2Dates(DateTime start,DateTime due,IEnumerable<DateTime> holidays)
        {
            var dic = new Dictionary<DateTime, DayOfWeek>();
            var totalDays = (due - start).Days;
            for (int i = 0; i < totalDays + 1; i++)
            {
                if (!holidays.Any(x => x == start.AddDays(i)))
                    dic.Add(start.AddDays(i), start.AddDays(i).DayOfWeek);
            }

            return dic.Where(x => x.Value != DayOfWeek.Saturday && x.Value != DayOfWeek.Sunday).Count();
        } 

기본적으로 나는 각 날짜와 함께 내 상태를 평가하고 싶었습니다.

  1. 토요일이 아님
  2. 일요일이 아닙니다
  3. 공휴일이 아님

또한 반복되는 날짜를 피하고 싶었습니다.

1 년을 평가하는 데 필요한 시간을 실행하고 측정하여 다음과 같은 결과를 얻었습니다.

static void Main(string[] args)
        {
            var start = new DateTime(2017, 1, 1);
            var due = new DateTime(2017, 12, 31);

            var sw = Stopwatch.StartNew();
            var days = NumberOfWorkingDaysBetween2Dates(start, due,NationalHolidays());
            sw.Stop();

            Console.WriteLine($"Total working days = {days} --- time: {sw.Elapsed}");
            Console.ReadLine();

            // result is:
           // Total working days = 249-- - time: 00:00:00.0269087
        }

편집 : 더 간단한 새로운 방법 :

public static int ToBusinessWorkingDays(this DateTime start, DateTime due, DateTime[] holidays)
        {
            return Enumerable.Range(0, (due - start).Days)
                            .Select(a => start.AddDays(a))
                            .Where(a => a.DayOfWeek != DayOfWeek.Sunday)
                            .Where(a => a.DayOfWeek != DayOfWeek.Saturday)
                            .Count(a => !holidays.Any(x => x == a));

        }

1

위의 답변 중 실제로 올바른 것은 없다고 생각합니다. 그들 중 어느 것도 날짜가 주말 중반에 시작하고 끝나는 경우, 날짜가 금요일에 시작하여 다음 월요일에 끝나는 경우 등과 같은 모든 특수한 경우를 해결하지 못합니다. 또한 계산을 전체로 반올림합니다. 예를 들어 시작 날짜가 토요일 중간이면 근무일에서 하루 종일을 빼서 잘못된 결과를 제공합니다.

어쨌든, 여기에 매우 효율적이고 간단하며 모든 경우에 작동하는 솔루션이 있습니다. 트릭은 시작일과 종료일에 대해 이전 월요일을 찾은 다음 주말에 시작과 종료가 발생할 때 약간의 보상을하는 것입니다.

public double WorkDays(DateTime startDate, DateTime endDate){
        double weekendDays;

        double days = endDate.Subtract(startDate).TotalDays;

        if(days<0) return 0;

        DateTime startMonday = startDate.AddDays(DayOfWeek.Monday - startDate.DayOfWeek).Date;
        DateTime endMonday = endDate.AddDays(DayOfWeek.Monday - endDate.DayOfWeek).Date;

        weekendDays = ((endMonday.Subtract(startMonday).TotalDays) / 7) * 2;

        // compute fractionary part of weekend days
        double diffStart = startDate.Subtract(startMonday).TotalDays - 5;
        double diffEnd = endDate.Subtract(endMonday).TotalDays - 5;

        // compensate weekenddays
        if(diffStart>0) weekendDays -= diffStart;
        if(diffEnd>0) weekendDays += diffEnd;

        return days - weekendDays;
    }

2
토요일과 일요일로 호출하면 -1을 반환합니다.
Whelkaholism 2014 년

1
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime start = new DateTime(2014, 1, 1);
            DateTime stop = new DateTime(2014, 12, 31);

            int totalWorkingDays = GetNumberOfWorkingDays(start, stop);

            Console.WriteLine("There are {0} working days.", totalWorkingDays);
        }

        private static int GetNumberOfWorkingDays(DateTime start, DateTime stop)
        {
            TimeSpan interval = stop - start;

            int totalWeek = interval.Days / 7;
            int totalWorkingDays = 5 * totalWeek;

            int remainingDays = interval.Days % 7;


            for (int i = 0; i <= remainingDays; i++)
            {
                DayOfWeek test = (DayOfWeek)(((int)start.DayOfWeek + i) % 7);
                if (test >= DayOfWeek.Monday && test <= DayOfWeek.Friday)
                    totalWorkingDays++;
            }

            return totalWorkingDays;
        }
    }
}

1

작동 및 루프 없음

이 방법은 루프를 사용하지 않으며 실제로 매우 간단합니다. 매주 5 영업일이 있다는 것을 알고 있으므로 기간을 전체 주로 확장합니다. 그런 다음 룩업 테이블을 사용하여 올바른 결과를 얻기 위해 시작과 끝에서 빼야하는 영업일 수를 찾습니다. 나는 무슨 일이 일어나고 있는지 보여주기 위해 계산을 확장했지만 필요한 경우 모든 것을 한 줄로 요약 할 수 있습니다.

어쨌든 이것은 나를 위해 작동하므로 다른 사람들에게 도움이 될 수 있도록 여기에 게시 할 것이라고 생각했습니다. 즐거운 코딩입니다.

계산

  • t : 날짜 사이의 총 일수 (최소 = 최대 인 경우 1)
  • a + b : 합계를 전체 주로 확장하는 데 필요한 추가 일수
  • k : 1.4는 주당 평일 수, 즉 (t / 7) * 5
  • c : 합계에서 뺄 평일 수
  • m : 각 요일의 "c"값을 찾는 데 사용되는 조회 테이블

문화

코드는 월요일부터 금요일까지 근무하는 것으로 가정합니다. 일요일부터 목요일까지 다른 문화권의 경우 계산 전에 날짜를 오프셋해야합니다.

방법

public int Weekdays(DateTime min, DateTime max) 
{       
        if (min.Date > max.Date) throw new Exception("Invalid date span");
        var t = (max.AddDays(1).Date - min.Date).TotalDays;
        var a = (int) min.DayOfWeek;
        var b = 6 - (int) max.DayOfWeek;
        var k = 1.4;
        var m = new int[]{0, 0, 1, 2, 3, 4, 5}; 
        var c = m[a] + m[b];
        return (int)((t + a + b) / k) - c;
}

1
1.4 값으로 K를 어떻게 얻을 수 있습니까?
toha

0

내 솔루션을 공유하겠습니다. 그것은 나를 위해 일했습니다. 아마도 나는 버그가 있다는 것을 알지 못합니다. 처음으로 불완전한 주가 있으면 시작했습니다. 완전한 주가 토요일부터 일요일부터이므로 (int) _now.DayOfWeek가 0 (일요일)이 아니면 첫 주가 불완전한 것입니다.

첫 번째 주 토요일에 1에서 첫 번째 주 수를 뺀 다음 새 수에 더합니다.

그런 다음 마지막 불완전한 주를 얻은 다음 일요일에 1을 뺀 다음 새 수에 더합니다.

마지막으로 완전한 주 수에 5 (평일)를 곱한 값이 새 수에 추가되었습니다.

public int RemoveNonWorkingDays(int numberOfDays){

            int workingDays = 0;

            int firstWeek = 7 - (int)_now.DayOfWeek;

            if(firstWeek < 7){

                if(firstWeek > numberOfDays)
                    return numberOfDays;

                workingDays += firstWeek-1;
                numberOfDays -= firstWeek;
            }


            int lastWeek = numberOfDays % 7;

            if(lastWeek > 0){

                numberOfDays -= lastWeek;
                workingDays += lastWeek - 1;

            }

            workingDays += (numberOfDays/7)*5;

            return workingDays;
        }

0

이 코드의 견고한 TSQL 버전을 찾는 데 어려움이있었습니다. 아래는 기본적으로 공휴일을 미리 계산하는 데 사용해야하는 Holiday 테이블을 추가하여 여기에 C # 코드를 변환 한 것입니다.

CREATE TABLE dbo.Holiday
(
    HolidayDt       DATE NOT NULL,
    Name            NVARCHAR(50) NOT NULL,
    IsWeekday       BIT NOT NULL,
    CONSTRAINT PK_Holiday PRIMARY KEY (HolidayDt)
)
GO
CREATE INDEX IDX_Holiday ON Holiday (HolidayDt, IsWeekday)

GO

CREATE function dbo.GetBusinessDays
(
     @FirstDay  datetime,
     @LastDay   datetime
) 
RETURNS INT
 AS
BEGIN
    DECLARE @BusinessDays INT, @FullWeekCount INT 
    SELECT  @FirstDay = CONVERT(DATETIME,CONVERT(DATE,@FirstDay))
        ,   @LastDay = CONVERT(DATETIME,CONVERT(DATE,@LastDay))

    IF @FirstDay > @LastDay
        RETURN NULL;

    SELECT @BusinessDays = DATEDIFF(DAY, @FirstDay, @LastDay) + 1 
    SELECT @FullWeekCount = @BusinessDays / 7;

    -- find out if there are weekends during the time exceedng the full weeks
    IF @BusinessDays > (@FullWeekCount * 7)
    BEGIN
    -- we are here to find out if there is a 1-day or 2-days weekend
    -- in the time interval remaining after subtracting the complete weeks
        DECLARE @firstDayOfWeek INT, @lastDayOfWeek INT;
        SELECT @firstDayOfWeek = DATEPART(DW, @FirstDay), @lastDayOfWeek = DATEPART(DW, @LastDay);

        IF @lastDayOfWeek < @firstDayOfWeek
                SELECT @lastDayOfWeek = @lastDayOfWeek + 7;

        IF @firstDayOfWeek <= 6 
            BEGIN
                IF (@lastDayOfWeek >= 7) --Both Saturday and Sunday are in the remaining time interval
                    BEGIN 
                        SELECT @BusinessDays = @BusinessDays - 2
                    END
                ELSE IF @lastDayOfWeek>=6 --Only Saturday is in the remaining time interval
                    BEGIN
                        SELECT @BusinessDays = @BusinessDays - 1
                    END

            END
        ELSE IF @firstDayOfWeek <= 7 AND @lastDayOfWeek >=7 -- Only Sunday is in the remaining time interval
        BEGIN 
            SELECT @BusinessDays = @BusinessDays - 1
        END
    END

    -- subtract the weekends during the full weeks in the interval
    DECLARE @Holidays INT;
    SELECT  @Holidays = COUNT(*) 
    FROM    Holiday 
    WHERE   HolidayDt BETWEEN @FirstDay AND @LastDay 
    AND     IsWeekday = CAST(1 AS BIT)

    SELECT @BusinessDays = @BusinessDays - (@FullWeekCount + @FullWeekCount) -- - @Holidays

    RETURN @BusinessDays
END

0
    int BusinessDayDifference(DateTime Date1, DateTime Date2)
    {
        int Sign = 1;
        if (Date2 > Date1)
        {
            Sign = -1;
            DateTime TempDate = Date1;
            Date1 = Date2;
            Date2 = TempDate;
        }
        int BusDayDiff = (int)(Date1.Date - Date2.Date).TotalDays;
        if (Date1.DayOfWeek == DayOfWeek.Saturday)
            BusDayDiff -= 1;
        if (Date2.DayOfWeek == DayOfWeek.Sunday)
            BusDayDiff -= 1;
        int Week1 = GetWeekNum(Date1);
        int Week2 = GetWeekNum(Date2);
        int WeekDiff = Week1 - Week2;
        BusDayDiff -= WeekDiff * 2;
        foreach (DateTime Holiday in Holidays)
            if (Date1 >= Holiday && Date2 <= Holiday)
                BusDayDiff--;
        BusDayDiff *= Sign;
        return BusDayDiff;
    }

    private int GetWeekNum(DateTime Date)
    {
        return (int)(Date.AddDays(-(int)Date.DayOfWeek).Ticks / TimeSpan.TicksPerDay / 7);
    }

0

이 문제에 대한 매우 간단한 해결책이 있습니다. 시작 날짜, 종료 날짜 및 "for loop"를 사용하여 하루를 늘리고 DayOfWeek 문자열로 변환하여 근무일인지 주말인지 계산합니다.

class Program
{
    static void Main(string[] args)
    {
        DateTime day = new DateTime();
        Console.Write("Inser your end date (example: 01/30/2015): ");
        DateTime endDate = DateTime.Parse(Console.ReadLine());
        int numberOfDays = 0;
        for (day = DateTime.Now.Date; day.Date < endDate.Date; day = day.Date.AddDays(1))
        {
            string dayToString = Convert.ToString(day.DayOfWeek);
            if (dayToString != "Saturday" && dayToString != "Sunday") numberOfDays++;
        }
        Console.WriteLine("Number of working days (not including local holidays) between two dates is "+numberOfDays);
    }
}

0

답변 및 패치 권장으로 표시된 댓글에 따라->이 버전은 요일을 업무 시간으로 변환하려고합니다 ... 당일 시간도 고려합니다.

 /// <summary>
    /// Calculates number of business days, taking into account:
    ///  - weekends (Saturdays and Sundays)
    ///  - bank holidays in the middle of the week
    /// </summary>
    /// <param name="firstDay">First day in the time interval</param>
    /// <param name="lastDay">Last day in the time interval</param>
    /// <param name="bankHolidays">List of bank holidays excluding weekends</param>
    /// <returns>Number of business hours during the 'span'</returns>
    public static int BusinessHoursUntil(DateTime firstDay, DateTime lastDay, params DateTime[] bankHolidays)
    {
        var original_firstDay = firstDay;
        var original_lastDay = lastDay;
        firstDay = firstDay.Date;
        lastDay = lastDay.Date;
        if (firstDay > lastDay)
            return -1; //// throw new ArgumentException("Incorrect last day " + lastDay);

        TimeSpan span = lastDay - firstDay;
        int businessDays = span.Days + 1;
        int fullWeekCount = businessDays / 7;
        // find out if there are weekends during the time exceedng the full weeks
        if (businessDays > fullWeekCount * 7)
        {
            // we are here to find out if there is a 1-day or 2-days weekend
            // in the time interval remaining after subtracting the complete weeks
            int firstDayOfWeek = firstDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)firstDay.DayOfWeek;
            int lastDayOfWeek = lastDay.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)lastDay.DayOfWeek;

            if (lastDayOfWeek < firstDayOfWeek)
                lastDayOfWeek += 7;
            if (firstDayOfWeek <= 6)
            {
                if (lastDayOfWeek >= 7)// Both Saturday and Sunday are in the remaining time interval
                    businessDays -= 2;
                else if (lastDayOfWeek >= 6)// Only Saturday is in the remaining time interval
                    businessDays -= 1;
            }
            else if (firstDayOfWeek <= 7 && lastDayOfWeek >= 7)// Only Sunday is in the remaining time interval
                businessDays -= 1;
        }

        // subtract the weekends during the full weeks in the interval
        businessDays -= fullWeekCount + fullWeekCount;

        if (bankHolidays != null && bankHolidays.Any())
        {
            // subtract the number of bank holidays during the time interval
            foreach (DateTime bankHoliday in bankHolidays)
            {
                DateTime bh = bankHoliday.Date;
                if (firstDay <= bh && bh <= lastDay)
                    --businessDays;
            }
        }

        int total_business_hours = 0;
        if (firstDay.Date == lastDay.Date)
        {//If on the same day, go granular with Hours from the Orginial_*Day values
            total_business_hours = (int)(original_lastDay - original_firstDay).TotalHours;
        }
        else
        {//Convert Business-Days to TotalHours
            total_business_hours = (int)(firstDay.AddDays(businessDays).AddHours(firstDay.Hour) - firstDay).TotalHours;
        }
        return total_business_hours;
    }

0

토요일이 영업일 인 경우 또는 영업일로 간주되는 요일이 단 2 일인 경우에도 영업주를 매개 변수로 지원하기 위해 @Alexander 및 @Slauma 답변을 개선했습니다.

/// <summary>
/// Calculate the number of business days between two dates, considering:
///  - Days of the week that are not considered business days.
///  - Holidays between these two dates.
/// </summary>
/// <param name="fDay">First day of the desired 'span'.</param>
/// <param name="lDay">Last day of the desired 'span'.</param>
/// <param name="BusinessDaysOfWeek">Days of the week that are considered to be business days, if NULL considers monday, tuesday, wednesday, thursday and friday as business days of the week.</param>
/// <param name="Holidays">Holidays, if NULL, considers no holiday.</param>
/// <returns>Number of business days during the 'span'</returns>
public static int BusinessDaysUntil(this DateTime fDay, DateTime lDay, DayOfWeek[] BusinessDaysOfWeek = null, DateTime[] Holidays = null)
{
    if (BusinessDaysOfWeek == null)
        BusinessDaysOfWeek = new DayOfWeek[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday };
    if (Holidays == null)
        Holidays = new DateTime[] { };

    fDay = fDay.Date;
    lDay = lDay.Date;

    if (fDay > lDay)
        throw new ArgumentException("Incorrect last day " + lDay);

    int bDays = (lDay - fDay).Days + 1;
    int fullWeekCount = bDays / 7;
    int fullWeekCountMult = 7 - WeekDays.Length;
    //  Find out if there are weekends during the time exceedng the full weeks
    if (bDays > (fullWeekCount * 7))
    {
        int fDayOfWeek = (int)fDay.DayOfWeek;
        int lDayOfWeek = (int)lDay.DayOfWeek;

        if (fDayOfWeek > lDayOfWeek)
            lDayOfWeek += 7;

        // If they are the same, we already covered it right before the Holiday subtraction
        if (lDayOfWeek != fDayOfWeek)
        {
            //  Here we need to see if any of the days between are considered business days
            for (int i = fDayOfWeek; i <= lDayOfWeek; i++)
                if (!WeekDays.Contains((DayOfWeek)(i > 6 ? i - 7 : i)))
                    bDays -= 1;
        }
    }

    //  Subtract the days that are not in WeekDays[] during the full weeks in the interval
    bDays -= (fullWeekCount * fullWeekCountMult);
    //  Subtract the number of bank holidays during the time interval
    bDays = bDays - Holidays.Select(x => x.Date).Count(x => fDay <= x && x <= lDay);

    return bDays;
}

0

다음은 두 날짜 사이의 영업일을 계산하는 데 사용할 수있는 함수입니다. 국가 / 지역에 따라 다를 수 있으므로 휴일 목록을 사용하지 않습니다.

어쨌든 그것을 사용하고 싶다면 휴일 목록으로 세 번째 인수를 취할 수 있으며 카운트를 늘리기 전에 목록에 d가 포함되어 있지 않은지 확인해야합니다.

public static int GetBussinessDaysBetweenTwoDates(DateTime StartDate,   DateTime EndDate)
    {
        if (StartDate > EndDate)
            return -1;

        int bd = 0;

        for (DateTime d = StartDate; d < EndDate; d = d.AddDays(1))
        {
            if (d.DayOfWeek != DayOfWeek.Saturday && d.DayOfWeek != DayOfWeek.Sunday)
                bd++;
        }

        return bd;
    }

0

나는 이것이 더 간단한 방법이 될 수 있다고 믿습니다.

    public int BusinessDaysUntil(DateTime start, DateTime end, params DateTime[] bankHolidays)
    {
        int tld = (int)((end - start).TotalDays) + 1; //including end day
        int not_buss_day = 2 * (tld / 7); //Saturday and Sunday
        int rest = tld % 7; //rest.

        if (rest > 0)
        {
            int tmp = (int)start.DayOfWeek - 1 + rest;
            if (tmp == 6 || start.DayOfWeek == DayOfWeek.Sunday) not_buss_day++; else if (tmp > 6) not_buss_day += 2;
        }

        foreach (DateTime bankHoliday in bankHolidays)
        {
            DateTime bh = bankHoliday.Date;
            if (!(bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday) && (start <= bh && bh <= end))
            {
                not_buss_day++;
            }
        }
        return tld - not_buss_day;
    }

0

여기에 또 다른 아이디어가 있습니다.이 방법을 사용하면 모든 근무 주 및 공휴일을 지정할 수 있습니다.

여기서 아이디어는 첫 번째 작업 일부터 마지막 ​​주말까지 날짜 범위의 핵심을 찾는 것입니다. 이를 통해 전체 주를 쉽게 계산할 수 있습니다 ( 모든 날짜를 반복 하지 않고도 ). 우리가해야 할 일은이 핵심 범위의 시작과 끝 이전에 해당하는 근무일을 추가하는 것입니다.

public static int CalculateWorkingDays(
    DateTime startDate, 
    DateTime endDate, 
    IList<DateTime> holidays, 
    DayOfWeek firstDayOfWeek,
    DayOfWeek lastDayOfWeek)
{
    // Make sure the defined working days run contiguously
    if (lastDayOfWeek < firstDayOfWeek)
    {
        throw new Exception("Last day of week cannot fall before first day of week!");
    }

    // Create a list of the days of the week that make-up the weekend by working back
    // from the firstDayOfWeek and forward from lastDayOfWeek to get the start and end
    // the weekend
    var weekendStart = lastDayOfWeek == DayOfWeek.Saturday ? DayOfWeek.Sunday : lastDayOfWeek + 1;
    var weekendEnd = firstDayOfWeek == DayOfWeek.Sunday ? DayOfWeek.Saturday : firstDayOfWeek - 1;
    var weekendDays = new List<DayOfWeek>();

    var w = weekendStart;
    do {
        weekendDays.Add(w);
        if (w == weekendEnd) break;
        w = (w == DayOfWeek.Saturday) ? DayOfWeek.Sunday : w + 1;
    } while (true);


    // Force simple dates - no time
    startDate = startDate.Date;
    endDate = endDate.Date;

    // Ensure a progessive date range
    if (endDate < startDate)
    {
        var t = startDate;
        startDate = endDate;
        endDate = t;
    }

    // setup some working variables and constants
    const int daysInWeek = 7;           // yeah - really!
    var actualStartDate = startDate;    // this will end up on startOfWeek boundary
    var actualEndDate = endDate;        // this will end up on weekendEnd boundary
    int workingDaysInWeek = daysInWeek - weekendDays.Count;

    int workingDays = 0;        // the result we are trying to find
    int leadingDays = 0;        // the number of working days leading up to the firstDayOfWeek boundary
    int trailingDays = 0;       // the number of working days counting back to the weekendEnd boundary

    // Calculate leading working days
    // if we aren't on the firstDayOfWeek we need to step forward to the nearest
    if (startDate.DayOfWeek != firstDayOfWeek)
    {
        var d = startDate;
        do {
            if (d.DayOfWeek == firstDayOfWeek || d >= endDate)
            {
                actualStartDate = d;
                break;  
            }
            if (!weekendDays.Contains(d.DayOfWeek))
            {
                leadingDays++;
            }
            d = d.AddDays(1);
        } while(true);
    }

    // Calculate trailing working days
    // if we aren't on the weekendEnd we step back to the nearest
    if (endDate >= actualStartDate && endDate.DayOfWeek != weekendEnd)
    {
        var d = endDate;
        do {
            if (d.DayOfWeek == weekendEnd || d < actualStartDate)
            {
                actualEndDate = d;
                break;  
            }
            if (!weekendDays.Contains(d.DayOfWeek))
            {
                trailingDays++;
            }
            d = d.AddDays(-1);
        } while(true);
    }

    // Calculate the inclusive number of days between the actualStartDate and the actualEndDate
    var coreDays = (actualEndDate - actualStartDate).Days + 1;
    var noWeeks =  coreDays / daysInWeek;

    // add together leading, core and trailing days
    workingDays +=  noWeeks * workingDaysInWeek;
    workingDays += leadingDays;
    workingDays += trailingDays;

    // Finally remove any holidays that fall within the range.
    if (holidays != null)
    {
        workingDays -= holidays.Count(h => h >= startDate && (h <= endDate));
    }

    return workingDays;
}

0

나는 코멘트를 할 수 없기 때문에. 공휴일이 주말에 있더라도 공휴일을 빼는 허용 된 솔루션에는 또 하나의 문제가 있습니다. 다른 입력을 확인하는 방법을 살펴보면 이것도 마찬가지입니다.

따라서 foreach는 다음과 같아야합니다.

    // subtract the number of bank holidays during the time interval
    foreach (DateTime bankHoliday in bankHolidays)
    {
        DateTime bh = bankHoliday.Date;

        // Do not subtract bank holidays when they fall in the weekend to avoid double subtraction
        if (bh.DayOfWeek == DayOfWeek.Saturday || bh.DayOfWeek == DayOfWeek.Sunday)
                continue;

        if (firstDay <= bh && bh <= lastDay)
            --businessDays;
    }

0

다음은 MVC를 사용하는 경우 접근 방식입니다. 나는 또한 당신이 만들어야 할 holidayscalendar에서 그것을 가져 와서 제외 할 국경일이나 축제 일을 계산했습니다.

        foreach (DateTime day in EachDay(model))
        {
            bool key = false;
            foreach (LeaveModel ln in holidaycalendar)
            {
                if (day.Date == ln.Date && day.DayOfWeek != DayOfWeek.Saturday && day.DayOfWeek != DayOfWeek.Sunday)
                {
                    key = true; break;
                }
            }
            if (day.DayOfWeek == DayOfWeek.Saturday || day.DayOfWeek == DayOfWeek.Sunday)
            {
                key = true;
            }
            if (key != true)
            {
                leavecount++;
            }
        }

Leavemodel은 여기에 목록입니다


0

다음은 해당 작업을 위해 작성한 도우미 함수입니다.
또한 out매개 변수 를 통해 주말 수를 반환합니다 .
원하는 경우 다른 주말을 사용하는 국가의 런타임에서 "주말"요일을 사용자 정의하거나 weekendDays[]선택적 매개 변수를 통해 휴일을 포함 할 수 있습니다 .

public static int GetNetworkDays(DateTime startDate, DateTime endDate,out int totalWeekenDays, DayOfWeek[] weekendDays = null)
{
    if (startDate >= endDate)
    {
        throw new Exception("start date can not be greater then or equel to end date");
    }

    DayOfWeek[] weekends = new DayOfWeek[] { DayOfWeek.Sunday, DayOfWeek.Saturday };
    if (weekendDays != null)
    {
        weekends = weekendDays;
    }

    var totaldays = (endDate - startDate).TotalDays + 1; // add one to include the first day to
    var counter = 0;
    var workdaysCounter = 0;
    var weekendsCounter = 0;

    for (int i = 0; i < totaldays; i++)
    {

        if (weekends.Contains(startDate.AddDays(counter).DayOfWeek))
        {
            weekendsCounter++;
        }
        else
        {
            workdaysCounter++;
        }

        counter++;
    }

    totalWeekenDays = weekendsCounter;
    return workdaysCounter;
}

0

다음 솔루션을 생각해 냈습니다.

var dateStart = new DateTime(2019,01,10);
var dateEnd = new DateTime(2019,01,31);

var timeBetween = (dateEnd - dateStart).TotalDays + 1;
int numberOf7DayWeeks = (int)(timeBetween / 7);
int numberOfWeekendDays = numberOf7DayWeeks * 2;
int workingDays =(int)( timeBetween - numberOfWeekendDays);

if(dateStart.DayOfWeek == DayOfWeek.Saturday || dateEnd.DayOfWeek == DayOfWeek.Sunday){
    workingDays -=2;
}       
if(dateStart.DayOfWeek == DayOfWeek.Sunday || dateEnd.DayOfWeek == DayOfWeek.Saturday){
    workingDays -=1;
}

0

시간 범위에서 매일 반복하고 토요일 또는 일요일 인 경우 카운터에서 하루를 빼면됩니다.

    private float SubtractWeekend(DateTime start, DateTime end) {
        float totaldays = (end.Date - start.Date).Days;
        var iterationVal = totalDays;
        for (int i = 0; i <= iterationVal; i++) {
            int dayVal = (int)start.Date.AddDays(i).DayOfWeek;
            if(dayVal == 6 || dayVal == 0) {
                // saturday or sunday
                totalDays--;
            }
        }
        return totalDays;
    }

0
public static int CalculateBusinessDaysInRange(this DateTime startDate, DateTime endDate, params DateTime[] holidayDates)
{
    endDate = endDate.Date;
    if(startDate > endDate)
        throw new ArgumentException("The end date can not be before the start date!", nameof(endDate));
    int accumulator = 0;
    DateTime itterator = startDate.Date;
    do 
    {
        if(itterator.DayOfWeek != DayOfWeek.Saturday && itterator.DayOfWeek != DayOfWeek.Sunday && !holidayDates.Any(hol => hol.Date == itterator))
        { accumulator++; }
    } 
    while((itterator = itterator.AddDays(1)).Date <= endDate);
    return accumulator
}

주어진 모든 훌륭한 답변에도 불구하고 수학이 나에게 의미가 없기 때문에 이것을 게시하는 것입니다. 이것은 확실히 작동하고 상당히 유지 관리 할 수있는 KISS 방법입니다. 2-3 개월 이상의 범위를 계산하는 경우 이것이 가장 효과적인 방법이 아닙니다. 우리는 단순히 그것이 토요일인지 일요일인지 또는 날짜가 주어진 휴일 날짜인지 결정합니다. 그렇지 않은 경우 영업일을 추가합니다. 그렇다면 모든 것이 정상입니다.

LINQ를 사용하면 이것이 훨씬 더 간단해질 수 있다고 확신하지만이 방법은 이해하기 훨씬 쉽습니다.


0

휴일을 고려하지 않고 하루 중 일부를 반환하는 시간을 고려하여 영업일을 계산하는 또 다른 방법 :

public static double GetBusinessDays(DateTime startD, DateTime endD)
{
    while (IsWeekend(startD))
        startD = startD.Date.AddDays(1);

    while (IsWeekend(endD))
        endD = endD.Date.AddDays(-1);

    var bussDays = (endD - startD).TotalDays -
        (2 * ((int)(endD - startD).TotalDays / 7)) -
        (startD.DayOfWeek > endD.DayOfWeek ? 2 : 0);

    return bussDays;
}

public static bool IsWeekend(DateTime d)
{
    return d.DayOfWeek == DayOfWeek.Saturday || d.DayOfWeek == DayOfWeek.Sunday;
}

여기에서 바이올린을 사용할 수 있습니다 : https://rextester.com/ASHRS53997


-1

이것은 일반적인 솔루션입니다.

startdayvalue는 시작일의 일 번호입니다.

weekday_1은 주말의 요일입니다.

일 번호-MON-1, TUE-2, ... SAT-6, SUN -7.

차이는 두 날짜의 차이입니다 ..

예 : 시작일 : 2013 년 4 월 4 일, 종료일 : 2013 년 4 월 14 일

차이 : 10, startdayvalue : 4, weekendday_1 : 7 (SUNDAY가 주말 인 경우)

이것은 당신에게 휴일 수를 줄 것입니다.

영업일 수 = (차이 + 1)-휴일 1

    if (startdayvalue > weekendday_1)
    {

        if (difference > ((7 - startdayvalue) + weekendday_1))
        {
            holiday1 = (difference - ((7 - startdayvalue) + weekendday_1)) / 7;
            holiday1 = holiday1 + 1;
        }
        else
        {
            holiday1 = 0;
        }
    }
    else if (startdayvalue < weekendday_1)
    {

        if (difference > (weekendday_1 - startdayvalue))
        {
            holiday1 = (difference - (weekendday_1 - startdayvalue)) / 7;
            holiday1 = holiday1 + 1;
        }
        else if (difference == (weekendday_1 - startdayvalue))
        {
            holiday1 = 1;
        }
        else
        {
            holiday1 = 0;
        }
    }
    else
    {
        holiday1 = difference / 7;
        holiday1 = holiday1 + 1;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.