Câu hỏi AddBusinessDays và GetBusinessDays


Tôi cần phải tìm 2 triển khai hoàn chỉnh trang nhã

public static DateTime AddBusinessDays(this DateTime date, int days)
{
 // code here
}

and 

public static int GetBusinessDays(this DateTime start, DateTime end)
{
 // code here
}

O (1) thích hợp hơn (không có vòng lặp).

CHỈNH SỬA:  Bởi ngày làm việc tôi có nghĩa là ngày làm việc (thứ hai, thứ ba, thứ tư, thứ năm, thứ sáu). Không có ngày lễ, chỉ cần loại trừ các ngày cuối tuần.

Tôi đã có một số giải pháp xấu xí mà dường như làm việc nhưng tôi tự hỏi nếu có những cách thanh lịch để làm điều này. Cảm ơn


Đây là những gì tôi đã viết cho đến nay. Nó hoạt động trong mọi trường hợp và âm bản nữa. Vẫn cần triển khai GetBusinessDays

public static DateTime AddBusinessDays(this DateTime startDate,
                                         int businessDays)
{
    int direction = Math.Sign(businessDays);
    if(direction == 1)
    {
        if(startDate.DayOfWeek == DayOfWeek.Saturday)
        {
            startDate = startDate.AddDays(2);
            businessDays = businessDays - 1;
        }
        else if(startDate.DayOfWeek == DayOfWeek.Sunday)
        {
            startDate = startDate.AddDays(1);
            businessDays = businessDays - 1;
        }
    }
    else
    {
        if(startDate.DayOfWeek == DayOfWeek.Saturday)
        {
            startDate = startDate.AddDays(-1);
            businessDays = businessDays + 1;
        }
        else if(startDate.DayOfWeek == DayOfWeek.Sunday)
        {
            startDate = startDate.AddDays(-2);
            businessDays = businessDays + 1;
        }
    }

    int initialDayOfWeek = (int)startDate.DayOfWeek;

    int weeksBase = Math.Abs(businessDays / 5);
    int addDays = Math.Abs(businessDays % 5);

    if((direction == 1 && addDays + initialDayOfWeek > 5) ||
         (direction == -1 && addDays >= initialDayOfWeek))
    {
        addDays += 2;
    }

    int totalDays = (weeksBase * 7) + addDays;
    return startDate.AddDays(totalDays * direction);
}

76
2018-06-25 15:48


gốc


Có những giải pháp thanh lịch khi nói đến một cái gì đó vô lý như ngày? - Wyatt Barnett
Donno. Đó là lý do tại sao tôi hỏi. - Adrian Zanescu
Bạn có quan tâm đến ngày lễ? - James Conigliaro
Bạn có quan tâm đến ngày lễ? - James Conigliaro. Không - Adrian Zanescu
Bỏ phiếu xuống những người đang cố gắng giúp đỡ không phải là một chiến lược chiến thắng. - Jamie Ide


Các câu trả lời:


Lần thử mới nhất cho chức năng đầu tiên của bạn:

public static DateTime AddBusinessDays(DateTime date, int days)
{
    if (days < 0)
    {
        throw new ArgumentException("days cannot be negative", "days");
    }

    if (days == 0) return date;

    if (date.DayOfWeek == DayOfWeek.Saturday)
    {
        date = date.AddDays(2);
        days -= 1;
    }
    else if (date.DayOfWeek == DayOfWeek.Sunday)
    {
        date = date.AddDays(1);
        days -= 1;
    }

    date = date.AddDays(days / 5 * 7);
    int extraDays = days % 5;

    if ((int)date.DayOfWeek + extraDays > 5)
    {
        extraDays += 2;
    }

    return date.AddDays(extraDays);

}

Hàm thứ hai, GetBusinessDays, có thể được thực hiện như sau:

public static int GetBusinessDays(DateTime start, DateTime end)
{
    if (start.DayOfWeek == DayOfWeek.Saturday)
    {
        start = start.AddDays(2);
    }
    else if (start.DayOfWeek == DayOfWeek.Sunday)
    {
        start = start.AddDays(1);
    }

    if (end.DayOfWeek == DayOfWeek.Saturday)
    {
        end = end.AddDays(-1);
    }
    else if (end.DayOfWeek == DayOfWeek.Sunday)
    {
        end = end.AddDays(-2);
    }

    int diff = (int)end.Subtract(start).TotalDays;

    int result = diff / 7 * 5 + diff % 7;

    if (end.DayOfWeek < start.DayOfWeek)
    {
        return result - 2;
    }
    else{
        return result;
    }
}

118
2018-06-25 16:14



Đối với giải pháp thứ hai, một giải pháp là lấy sự khác biệt giữa ngày và ngày + ngày. Điều này là tốt đẹp ở chỗ nó đảm bảo rằng hai chức năng sẽ đồng bộ đúng cách, và nó loại bỏ sự dư thừa. - Brian
@ Patrick: Nên được khắc phục ngay bây giờ - hy vọng bạn không nhớ chỉnh sửa. (Chỉ cần quay trở lại nếu tôi đã làm điều gì đó ngớ ngẩn.) - Noldorin
Yeah, cuối cùng chúng tôi đã đến đó. (Tôi nói 'chúng tôi' vì những đóng góp nhỏ bé của mình!) Lên tiếng bình chọn cho nỗ lực này. - Noldorin
Đã bỏ phiếu. Chỉ cần yêu một số nhận xét mã để giúp tôi thực hiện nó. - PeterX
DateTime.AddDays làm việc với số âm. Điều này không đúng theo mẫu giống như sử dụng các số âm với AddBusinessDays cho phép chọn ngày không kinh doanh. - Ristogod


sử dụng Thông thạo ngày giờ:

var now = DateTime.Now;
var dateTime1 = now.AddBusinessDays(3);
var dateTime2 = now.SubtractBusinessDays(5);

mã nội bộ như sau

    /// <summary>
    /// Adds the given number of business days to the <see cref="DateTime"/>.
    /// </summary>
    /// <param name="current">The date to be changed.</param>
    /// <param name="days">Number of business days to be added.</param>
    /// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
    public static DateTime AddBusinessDays(this DateTime current, int days)
    {
        var sign = Math.Sign(days);
        var unsignedDays = Math.Abs(days);
        for (var i = 0; i < unsignedDays; i++)
        {
            do
            {
                current = current.AddDays(sign);
            }
            while (current.DayOfWeek == DayOfWeek.Saturday ||
                current.DayOfWeek == DayOfWeek.Sunday);
        }
        return current;
    }

    /// <summary>
    /// Subtracts the given number of business days to the <see cref="DateTime"/>.
    /// </summary>
    /// <param name="current">The date to be changed.</param>
    /// <param name="days">Number of business days to be subtracted.</param>
    /// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
    public static DateTime SubtractBusinessDays(this DateTime current, int days)
    {
        return AddBusinessDays(current, -days);
    }

56
2017-09-04 13:12



Đây là giải pháp duy nhất mà thực sự làm việc cho tôi khi chuyển đổi sang VB.Net - Nicholas Head
OP yêu cầu không có vòng lặp, trong khi điều này rõ ràng có vòng. Không có gì tao nhã về việc làm một cái gì đó theo cách hiệu quả nhất. - Neolisk


Tôi đã tạo tiện ích mở rộng cho phép bạn thêm hoặc trừ ngày làm việc. Sử dụng số âm của businessDays để trừ. Tôi nghĩ rằng đó là một giải pháp khá thanh lịch. Dường như nó hoạt động trong mọi trường hợp.

namespace Extensions.DateTime
{
    public static class BusinessDays
    {
        public static System.DateTime AddBusinessDays(this System.DateTime source, int businessDays)
        {
            var dayOfWeek = businessDays < 0
                                ? ((int)source.DayOfWeek - 12) % 7
                                : ((int)source.DayOfWeek + 6) % 7;

            switch (dayOfWeek)
            {
                case 6:
                    businessDays--;
                    break;
                case -6:
                    businessDays++;
                    break;
            }

            return source.AddDays(businessDays + ((businessDays + dayOfWeek) / 5) * 2);
        }
    }
}

Thí dụ:

using System;
using System.Windows.Forms;
using Extensions.DateTime;

namespace AddBusinessDaysTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            label1.Text = DateTime.Now.AddBusinessDays(5).ToString();
            label2.Text = DateTime.Now.AddBusinessDays(-36).ToString();
        }
    }
}

11
2017-08-06 12:55



Kết quả là có vấn đề nếu ngày nguồn là thứ bảy hoặc chủ nhật. Ví dụ: Thứ Bảy + 1 ngày làm việc kết quả vào thứ ba, nơi tôi mong muốn thứ hai. - Slauma
@ Slauma: Đây là cách mà hầu hết các doanh nghiệp ở Canada hoạt động. +1 ngày làm việc = "ngày làm việc tiếp theo", trong trường hợp Thứ Bảy là Thứ Ba. Thứ hai sẽ là "cùng một ngày làm việc". - Neolisk
@Slauma Program hoạt động như dự định. Hãy suy nghĩ về nó một cách hợp lý. Nếu một điều gì đó liên quan đến việc kinh doanh bắt đầu vào thứ bảy, và bạn phải cho phép 1 ngày làm việc để mọi người phản ứng trong khoảng thời gian của ngày làm việc đó, liệu có nên nói với họ rằng nó phải được thực hiện vào thứ Hai? - Heliac


Đối với tôi, tôi phải có một giải pháp mà có thể bỏ qua cuối tuần và đi trong một trong hai tiêu cực hoặc tích cực. Tiêu chí của tôi là nếu nó đi về phía trước và hạ cánh vào cuối tuần, nó sẽ cần phải tiến tới thứ hai. Nếu nó quay trở lại và hạ cánh vào cuối tuần, nó sẽ phải nhảy đến thứ Sáu.

Ví dụ:

  • Thứ Tư - 3 ngày làm việc = Thứ Sáu cuối cùng
  • Thứ Tư + 3 ngày làm việc = Thứ Hai
  • Thứ Sáu - 7 ngày làm việc = Thứ Tư tuần trước
  • Thứ Ba - 5 ngày làm việc = Thứ Ba tuần trước

Chà, bạn hiểu ý rồi đó ;)

Tôi đã kết thúc viết lớp mở rộng này

public static partial class MyExtensions
{
    public static DateTime AddBusinessDays(this DateTime date, int addDays)
    {
        while (addDays != 0)
        {
            date = date.AddDays(Math.Sign(addDays));
            if (MyClass.IsBusinessDay(date))
            {
                addDays = addDays - Math.Sign(addDays);
            }
        }
        return date;
    }
}

Nó sử dụng phương pháp này tôi nghĩ sẽ hữu ích khi sử dụng ở nơi khác ...

public class MyClass
{
    public static bool IsBusinessDay(DateTime date)
    {
        switch (date.DayOfWeek)
        {
            case DayOfWeek.Monday:
            case DayOfWeek.Tuesday:
            case DayOfWeek.Wednesday:
            case DayOfWeek.Thursday:
            case DayOfWeek.Friday:
                return true;
            default:
                return false;
        }
    }
}

Nếu bạn không muốn bận tâm với điều đó bạn chỉ có thể thay thế if (MyClass.IsBusinessDay(date)) với nếu if ((date.DayOfWeek != DayOfWeek.Saturday) && (date.DayOfWeek != DayOfWeek.Sunday))

Vì vậy, bây giờ bạn có thể làm

var myDate = DateTime.Now.AddBusinessDays(-3);

hoặc là

var myDate = DateTime.Now.AddBusinessDays(5);

Dưới đây là kết quả từ một số thử nghiệm:

Kiểm tra kết quả mong đợi
Thứ Tư -4 ngày làm việc Thứ Năm Thứ Năm
Thứ Tư -3 ngày làm việc Thứ Sáu Thứ Sáu
Thứ Tư + 3 ngày làm việc Thứ Hai Thứ Hai
Thứ Sáu -7 ngày làm việc Thứ Tư Thứ Tư
Thứ Ba -5 ngày làm việc Thứ Ba Thứ Ba
Thứ Sáu +1 ngày làm việc Thứ Hai Thứ Hai
Thứ Bảy +1 ngày làm việc Thứ Hai Thứ Hai
Chủ Nhật -1 ngày làm việc Thứ Sáu Thứ Sáu
Thứ Hai -1 ngày làm việc Thứ Sáu Thứ Sáu
Thứ Hai +1 ngày làm việc Thứ Ba Thứ Ba
Thứ Hai +0 ngày làm việc Thứ Hai Thứ Hai

6
2017-09-17 15:27



Công việc tốt đẹp! Cảm ơn bạn đã viết bài này. - Jean-Paul


Với quốc tế hóa, điều này là khó khăn. Như đã đề cập trong các chủ đề khác ở đây trên SOF, ngày lễ khác nhau giữa các quốc gia chắc chắn và thậm chí từ tỉnh này sang tỉnh khác. Hầu hết các chính phủ không lên kế hoạch cho kỳ nghỉ của họ hơn năm năm hoặc lâu hơn.


2
2018-06-25 16:04



Tôi không thể hiểu tại sao một người nào đó đã downvoted Ash: đúng là nó không phải là câu trả lời cho những gì được hỏi, nhưng Ash IS HOÀN TOÀN QUYỀN, tôi có cùng một vấn đề: phần mềm của tôi phải chạy ở châu Âu (Mon-Sun) VÀ Africa / Châu Á (Thứ Bảy-Thứ Sáu). Để downvote này, người ta phải là một ass (con vật, chỉ trong trường hợp) người không thể nhìn thấy ngoài mũi của mình: có vẻ như có những người trên SO người sợ kiến ​​thức, chỉ không muốn biết và thích để duy trì sự thiếu hiểu biết của họ: nhưng họ không làm một dịch vụ cho AZ ở nơi đầu tiên (và cho chính họ nữa, nhưng đó là kinh doanh riêng của họ) - M.Turrini
i (OP) downvoted bởi vì tôi nghĩ rằng tôi đã làm cho nó rõ ràng trong câu hỏi những gì là những khó khăn và có vẻ như Ash dind không chú ý. Tôi xin lỗi nếu điều đó làm tổn thương ai đó nhưng chỉ là một lời nói xấu - Adrian Zanescu
@AZ: Trong câu hỏi ban đầu bạn đã đề cập đến những hạn chế quốc tế hóa? - Ash Machine
@AshMachine, chính xác. Anh ta không. - jwg
Các ngày lễ khác nhau giữa các quốc gia, nhưng câu hỏi này không liên quan gì đến các ngày lễ. Nó phải làm với những ngày cuối tuần, được nêu rõ trong mô tả vấn đề. Điều này đáng lẽ phải là một bình luận. - Neolisk


public static DateTime AddBusinessDays(this DateTime date, int days)
{
    date = date.AddDays((days / 5) * 7);

    int remainder = days % 5;

    switch (date.DayOfWeek)
    {
        case DayOfWeek.Tuesday:
            if (remainder > 3) date = date.AddDays(2);
            break;
        case DayOfWeek.Wednesday:
            if (remainder > 2) date = date.AddDays(2);
            break;
        case DayOfWeek.Thursday:
            if (remainder > 1) date = date.AddDays(2);
            break;
        case DayOfWeek.Friday:
            if (remainder > 0) date = date.AddDays(2);
            break;
        case DayOfWeek.Saturday:
            if (days > 0) date = date.AddDays((remainder == 0) ? 2 : 1);
            break;
        case DayOfWeek.Sunday:
            if (days > 0) date = date.AddDays((remainder == 0) ? 1 : 0);
            break;
        default:  // monday
            break;
    }

    return date.AddDays(remainder);
}

1
2018-06-25 16:42





Tôi đến trễ để trả lời, nhưng tôi đã làm một thư viện nhỏ với tất cả các tùy chỉnh cần thiết để thực hiện các thao tác đơn giản vào những ngày làm việc ... Tôi để nó ở đây: Quản lý ngày làm việc


1
2018-04-08 13:56



Thật không may này là GNU được cấp phép như vậy là "chất độc hợp pháp" cho bất kỳ ứng dụng thương mại. Bất kỳ cơ hội nào bạn muốn thư giãn với "MIT" hay "Apache"? - Tony O'Hagan
Một số danh sách tĩnh có lẽ nên là mảng (thay vì danh sách được liên kết). - Tony O'Hagan
Tôi vừa đổi giấy phép thành MIT (tôi không muốn chặn bất cứ thứ gì đơn giản). Tôi sẽ xem xét đề xuất khác của bạn. - Boneless
Thật tuyệt, thật thú vị khi nhìn thấy việc quản lý ngày làm việc theo quốc gia vì một số quốc gia có thể có những ngày làm việc khác từ thứ Hai đến thứ Sáu. - serializer


    public static DateTime AddBusinessDays(DateTime date, int days)
    {
        if (days == 0) return date;
        int i = 0;
        while (i < days)
        {
            if (!(date.DayOfWeek == DayOfWeek.Saturday ||  date.DayOfWeek == DayOfWeek.Sunday)) i++;  
            date = date.AddDays(1);
        }
        return date;
    }

0
2017-09-16 18:51



trong tương lai thêm một chút ngữ cảnh cho câu trả lời và có lẽ lý do tại sao bạn đã đặt những gì bạn có :) - dax


Tôi muốn có một "AddBusinessDays" hỗ trợ số ngày tiêu cực để thêm và tôi đã kết thúc với điều này:

// 0 == Monday, 6 == Sunday
private static int epochDayToDayOfWeek0Based(long epochDay) {
    return (int)Math.floorMod(epochDay + 3, 7);
}

public static int daysBetween(long fromEpochDay, long toEpochDay) {
    // http://stackoverflow.com/questions/1617049/calculate-the-number-of-business-days-between-two-dates
    final int fromDOW = epochDayToDayOfWeek0Based(fromEpochDay);
    final int toDOW = epochDayToDayOfWeek0Based(toEpochDay);
    long calcBusinessDays = ((toEpochDay - fromEpochDay) * 5 + (toDOW - fromDOW) * 2) / 7;

    if (toDOW   == 6) calcBusinessDays -= 1;
    if (fromDOW == 6) calcBusinessDays += 1;
    return (int)calcBusinessDays;
}

public static long addDays(long epochDay, int n) {
    // https://alecpojidaev.wordpress.com/2009/10/29/work-days-calculation-with-c/
    // NB: in .NET, Sunday == 0, but in our code Monday == 0
    final int dow = (epochDayToDayOfWeek0Based(epochDay) + 1) % 7;
    final int wds = n + (dow == 0 ? 1 : dow); // Adjusted number of working days to add, given that we now start from the immediately preceding Sunday
    final int wends = n < 0 ? ((wds - 5) / 5) * 2
                            : (wds / 5) * 2 - (wds % 5 == 0 ? 2 : 0);
    return epochDay - dow + // Find the immediately preceding Sunday
           wds +            // Add computed working days
           wends;           // Add weekends that occur within each complete working week
}

Không yêu cầu vòng lặp, vì vậy nó phải nhanh chóng hợp lý ngay cả đối với các bổ sung "lớn".

Nó hoạt động với các ngày được biểu thị dưới dạng một số ngày theo lịch kể từ thời đại, vì nó được lớp JDD8 LocalDate trưng ra và tôi đã làm việc trong Java. Nên đơn giản để thích ứng với các cài đặt khác mặc dù.

Các tính chất cơ bản là addDays luôn trả về một ngày trong tuần và điều đó cho tất cả d và n, daysBetween(d, addDays(d, n)) == n

Lưu ý rằng về mặt lý thuyết, việc thêm 0 ngày và trừ 0 ngày phải là các hoạt động khác nhau (nếu ngày của bạn là Chủ nhật, thêm 0 ngày sẽ đưa bạn đến Thứ Hai và trừ 0 ngày sẽ đưa bạn đến Thứ Sáu). Vì không có điều gì là âm 0 (bên ngoài điểm nổi!), Tôi đã chọn để diễn giải một đối số n = 0 như ý nghĩa thêm vào 0 ngày.


0
2017-12-02 23:01