คำนวณจำนวนวันทำการระหว่างสองวัน?


99

ใน C #, วิธีการที่ฉันสามารถคำนวณจำนวนของธุรกิจ (หรือวันธรรมดา) วันระหว่างวันที่สองวัน?


1
นี่คือตัวอย่างใน C # ที่ขยายไปสู่การรับชั่วโมงเช่นกัน codeproject.com/KB/datetime/CalculatingBusinessHours.aspx
WesleyJohnson


4
คุณจะยกเว้นวันหยุดหรือไม่
pxl

คำตอบ:


123

ฉันเคยทำงานแบบนี้มาก่อนและฉันมีวิธีแก้ปัญหา ฉันจะหลีกเลี่ยงการแจกแจงทุกวันในช่วงเวลาที่สามารถหลีกเลี่ยงได้ซึ่งเป็นกรณีนี้ ฉันไม่ได้พูดถึงการสร้างอินสแตนซ์ 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 สิงหาคม 2554

ตอบโจทย์มาก! มีข้อผิดพลาดเล็กน้อยแม้ว่า ฉันมีอิสระในการแก้ไขคำตอบนี้เนื่องจากไม่มีผู้ตอบตั้งแต่ปี 2009

โค้ดด้านบนถือว่าDayOfWeek.Sundayมีค่า7ซึ่งไม่เป็นเช่นนั้น 0ค่าที่เป็นจริง ซึ่งนำไปสู่การคำนวณที่ไม่ถูกต้องหากเป็นเช่นนั้นfirstDayและlastDayเป็นวันอาทิตย์เดียวกัน วิธีการส่งกลับ1ในกรณีนี้ 0แต่มันควรจะเป็น

แก้ไขข้อบกพร่องนี้ได้ง่ายที่สุด: แทนที่โค้ดเหนือบรรทัดที่firstDayOfWeekและlastDayOfWeekประกาศตามรายการต่อไปนี้:

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 # ทำให้งานบางอย่างง่ายขึ้นมาก) ธนาคารวันหยุดก็เป็นสัมผัสที่ดีเช่นกัน!
RedGlyph

2
ตรวจสอบให้แน่ใจว่าวันหยุดธนาคารดังต่อไปนี้: if (firstDay <= bh && bh <= lastDay && bh.IsWorkingDay ())
Tawani

5
ขอบคุณสำหรับวิธีการ แม้ว่าฉันต้องเพิ่มสิ่งต่อไปนี้ในการแทนที่ / การทำซ้ำ if-statement ในวันหยุดธนาคาร: && !(bh.DayOfWeek == DayOfWeek.Sunday || bh.DayOfWeek == DayOfWeek.Saturday)มิฉะนั้นจะแทนที่ในวันเดียวกันสองครั้งหากวันหยุดตรงกับวันหยุดสุดสัปดาห์
KristianB

ฉันเปลี่ยนลูปสุดท้ายสำหรับใบแจ้งยอด Linq: businessDays - = bankHolidays เลือก (bankHoliday => bankHoliday.Date) .Count (bh => firstDay <= bh && bh <= lastDay);
JoanComasFdz

1
นอกจากนี้ยังเป็นประเทศที่ไม่มีวันหยุดสุดสัปดาห์ในวันเสาร์วันอาทิตย์ ดูข้อมูลเพิ่มเติมได้ที่ลิงค์นี้: en.wikipedia.org/wiki/Workweek_and_weekend
Gatej Alexandru

106

ตกลง. ฉันคิดว่าถึงเวลาโพสต์คำตอบที่ถูกต้อง:

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 ที่โพสต์ไว้ด้านบนทำให้ฉันเป็นคนแปลกหน้าด้วยเหตุผลบางประการ


10
ทำงานได้ดี แต่อาจใช้ DayOfWeek enums ตัวเองแทนที่จะส่งไปยัง ints?
Neo

3
ทางออกที่ดีที่สุดอย่างจริงจัง Cheers Alec
Mizmor

6
โปรดทราบว่าแม้ว่าฟังก์ชันนี้จะคืนค่าเป็นสองเท่า แต่ก็ควรเชื่อถือได้ว่าให้เวลาทั้งวัน จะไม่ส่งคืนคำตอบที่ถูกต้องสำหรับวันเศษส่วนเมื่อเกี่ยวข้องกับเวลา
Pakman

4
เพียงแค่สังเกตว่า '1+' จะถือว่าเริ่มต้นวันแรกจนถึงสิ้นวันสุดท้ายโดยไม่มี '1+' จะถือว่าสิ้นสุดวันแรกจนถึงสิ้นวันสุดท้าย เอาฉันในขณะที่ตัวเลขที่ออกมาตั้งแต่ผมสมมติว่าจุดเริ่มต้นของวันแรกจนถึงจุดเริ่มต้นของวันที่ผ่านมาซึ่งทำให้ความรู้สึกมากขึ้นกับผม
Jeffry van de Vuurst

11
นี่ไม่ใช่คำตอบที่ถูกต้อง วันสามารถออกได้ไม่เกิน 4 เกือบถูกต้องไม่ได้คำนึงถึงเวลาที่เริ่มต้นและวันสิ้นสุดในช่วงสุดสัปดาห์ซึ่งเป็นบิตที่ยากที่สุด จุดเริ่มต้น - สิ้นสุดไม่ควรอยู่ในวงเล็บ ไม่มีอะไรเกี่ยวข้องกับปัญหา 60% ของเวลาการแก้ปัญหานี้คือผิด
นกฮูก

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 การคำนวณวันทำงานสำหรับช่องว่าง 90 วันในการวนซ้ำ 1 ล้านครั้งจะใช้เวลา 10 วินาทีโดยใช้โซลูชันนี้และมีเพียง 0.2 วินาทีเท่านั้นที่ใช้คำตอบที่ยอมรับหรือคำตอบที่ดีกว่าของ Alec Pojidaev
Whelkaholism

ในการรวมรหัสควรเป็น: return Enumerable .Range (0, dayDifference + 1) ...
Edza

ไม่ย้อนวันวานในอดีต ชอบ -18 วันทำการ.
iwtu

@iwtu นี่อนุมานว่าto > from. บางทีนั่นอาจเป็นปัญหา?
อัลฟา

22

กำหนดวิธีการขยายใน DateTime ดังนี้:

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

จากนั้นใช้ภายในประโยค Where เพื่อกรองรายการวันที่ที่กว้างขึ้น:

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
อดัมนี่เป็นตัวอย่างง่ายๆพร้อมจำนวนโค้ดขั้นต่ำที่จำเป็นในการแสดงแนวคิด ในคำตอบเดิมของฉันฉันยังรวมการวนซ้ำซึ่งรวมรายการ allDates ซึ่งฉันได้แยกออกไปในฟังก์ชัน "GetDates" การทดสอบ IsWorkingDay สามารถย้ายออกจากคำสั่ง LINQ และเข้าสู่ลูปนั้นได้อย่างง่ายดาย โดยส่วนตัวแล้วฉันชอบวิธีการที่เป็นอยู่ตอนนี้เพราะว่ามันเป็นสิ่งที่มนุษย์สามารถอ่านได้เกี่ยวกับสิ่งที่เกิดขึ้น
Qwerty

10
สามารถย่อได้โดยการเปลี่ยนตำแหน่งที่จะนับและกำจัดการนับ
ซ้ำ

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");
    }
}

ทำไมคุณเริ่มนับโดยเพิ่ม 1 วันเป็น "from" @ for (var date = from.AddDays (1); date <= to; date = date.AddDays (1))
Oncel Umut TURER

6

นี่มันถูกทำร้ายจนตาย :) อย่างไรก็ตามฉันจะให้คำตอบอื่นเพราะฉันต้องการบางอย่างที่แตกต่างออกไปเล็กน้อย โซลูชันนี้แตกต่างกันตรงที่ส่งคืน Business TimeSpanระหว่างเริ่มต้นและสิ้นสุดและคุณสามารถตั้งค่าเวลาทำการของวันและเพิ่มวันหยุด คุณจึงใช้คำนวณได้ว่าจะเกิดขึ้นภายใน 1 วันข้ามวันช่วงสุดสัปดาห์หรือแม้แต่วันหยุด และคุณจะได้รับเพียงแค่วันทำการหรือไม่เพียงแค่ได้รับสิ่งที่คุณต้องการจากวัตถุ TimeSpan ที่ส่งคืน และวิธีที่ใช้รายการวันคุณจะเห็นได้ว่าการเพิ่มรายการวันที่ไม่ทำงานนั้นง่ายมากแค่ไหนหากไม่ใช่วันเสาร์และอาทิตย์ทั่วไป และฉันทดสอบเป็นเวลาหนึ่งปีและดูเหมือนว่าจะเร็วมาก

ฉันหวังว่าการวางโค้ดจะถูกต้อง แต่ฉันรู้ว่ามันได้ผล

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 ความแตกต่างของวันทำงานและรวมถึงชุดทดสอบหน่วยเพื่อถดถอยเทียบกับวิธีการนับวันธรรมดาที่ช้าลง ฉันยังได้รวมวิธีการที่รัดกุมในการเพิ่มวันธรรมดาซึ่งก็ใช้ได้ผลเช่นเดียวกันโดยไม่ต้องทำซ้ำ

การทดสอบหน่วยครอบคลุมชุดวันที่ไม่กี่พันชุดเพื่อทดสอบชุดค่าผสมของวันทำงานเริ่มต้น / สิ้นสุดทั้งหมดอย่างละเอียดถี่ถ้วนโดยมีทั้งช่วงวันที่ขนาดเล็กและขนาดใหญ่

สำคัญ : เราตั้งสมมติฐานว่าเรากำลังนับวันโดยไม่รวมวันที่เริ่มต้นและวันที่สิ้นสุด นี่เป็นสิ่งสำคัญเมื่อนับวันธรรมดาเนื่องจากวันเริ่มต้น / วันสิ้นสุดที่คุณรวม / ไม่รวมมีผลต่อผลลัพธ์ นอกจากนี้ยังช่วยให้มั่นใจได้ว่าความแตกต่างระหว่างสองวันที่เท่ากันจะเป็นศูนย์เสมอและเราจะรวมเฉพาะวันทำงานเต็มตามที่คุณต้องการให้คำตอบถูกต้องตลอดเวลาในวันที่เริ่มต้นปัจจุบัน (มักจะเป็นวันนี้) และรวมวันที่สิ้นสุดแบบเต็ม (เช่น วันที่ครบกำหนด)

หมายเหตุ: รหัสนี้ต้องการการปรับปรุงเพิ่มเติมสำหรับวันหยุด แต่เพื่อให้สอดคล้องกับสมมติฐานข้างต้นรหัสนี้จะต้องไม่รวมวันหยุดในวันที่เริ่มต้น

เพิ่มวันธรรมดา:

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 Combinatorialและส่วนขยายของ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 ที่ฉันพบใน stack overflow ความคิดของพวกเขามั่นคง แต่น่าเศร้าที่มันมีข้อบกพร่องเช่นกัน ใช้งานได้สำหรับค่า + ve แต่การแมปตารางการค้นหาไม่ถูกต้องสำหรับค่า -ve
Tony 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 หายไป แต่เป็นเพียงบูลแบบคงที่สาธารณะ IsSameDay (DateTime date1, DateTime date2) {return date1.Date == date2.Date; }
Choco Smith

คุณสามารถใช้ตารางการค้นหาอาร์เรย์ int แทนการสร้างอินสแตนซ์อ็อบเจ็กต์ Date ใหม่
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
วิธีนี้จะไม่ส่งคืนจำนวนวันทำการระหว่างวันที่สองวัน แต่จะส่งคืนวันที่ของธุรกิจระหว่างวันที่สองวัน รหัสที่คุณเสนอนั้นสะอาดมากและฉันชอบการใช้ผลตอบแทน แต่มันไม่ตอบคำถาม
Martin

3

ฉันค้นหาอัลกอริทึมเพื่อคำนวณวันทำการระหว่างวันที่ 2 วันที่และไม่รวมวันหยุดประจำชาติเป็นจำนวนมากและในที่สุดฉันก็ตัดสินใจใช้แนวทางนี้:

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

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 if min = max)
  • 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
คุณจะได้ K ที่มีค่า 1.4 ได้อย่างไร?
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 ที่มั่นคง ด้านล่างนี้เป็นการแปลงรหัส 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

นี่เป็นวิธีแก้ปัญหาที่ง่ายมากสำหรับปัญหานี้ เรามีวันที่เริ่มต้นวันที่สิ้นสุดและ "สำหรับการวนซ้ำ" สำหรับการใส่วันและคำนวณเพื่อดูว่าเป็นวันทำงานหรือวันหยุดสุดสัปดาห์โดยการแปลงเป็นสตริง 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

ฉันเพิ่งปรับปรุงคำตอบของ @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 ฉันได้คำนวณวันหยุดประจำชาติหรือวันเทศกาลใด ๆ ที่จะยกเว้นโดยดึงข้อมูลจากปฏิทินวันหยุดซึ่งคุณจะต้องกำหนดวันใหม่

        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 คือวันที่ของวันที่เริ่มต้น

weekendday_1 คือจำนวนวันของวันสิ้นสัปดาห์

วันที่ - จันทร์ - 1, อังคาร - 2, ... ส. - 6, อาทิตย์ -7

ความแตกต่างคือความแตกต่างระหว่างวันที่สองวัน ..

ตัวอย่าง: วันที่เริ่มต้น: 4 เมษายน 2556 วันที่สิ้นสุด: 14 เมษายน 2556

ความแตกต่าง: 10, ค่าเริ่มต้น: 4, วันหยุดสุดสัปดาห์day_1: 7 (ถ้าวันอาทิตย์เป็นวันหยุดสุดสัปดาห์สำหรับคุณ)

ซึ่งจะให้จำนวนวันหยุด

ไม่มีวันทำการ = (ความแตกต่าง + 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.