AddBusinessDays và GetBusinessDays


93

Tôi cần tìm 2 cách triển khai hoàn chỉnh thanh lịch của

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: Theo 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ỉ trừ cuối tuần.

Tôi đã có một số giải pháp xấu xí có vẻ hiệu quả nhưng tôi tự hỏi liệu có cách nào thanh lịch để làm điều này không. 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à cũng có tác dụng tiêu cực. 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);
}

14
Có giải pháp thanh lịch nào khi nói đến một thứ phi logic như ngày tháng không?
— Wyatt Barnett

Bạn có say mê với những kỳ nghỉ? - James Conigliaro. Không
— Adrian Zanescu

9
Bỏ phiếu từ 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

1
Lưu ý ngắn gọn về việc AddBusinessDaystriển khai trong câu hỏi ở trên (thực sự là một câu trả lời đã bị xóa mà tôi đã đề xuất để phục hồi; thay vào đó, một mod đã sao chép câu trả lời đó cho câu hỏi): Theo tôi, giải pháp này tốt hơn tất cả các câu trả lời cho đến nay vì nó là duy nhất một giao dịch chính xác với các giá trị âm, thứ Bảy và Chủ nhật là nguồn và không cần lib của Bên thứ ba. (Tôi đã thực hiện một chương trình nhỏ để thử nghiệm các giải pháp khác nhau ở đây.) Tôi sẽ chỉ làm tăng thêm if (businessDays == 0) return startDate;vào đầu của phương pháp này để có được kết quả chính xác cho trường hợp cạnh này là tốt.
— Slauma

1
@AZ.: Lần xóa đầu tiên khá cũ. Sau khi tôi yêu cầu khôi phục câu trả lời của bạn, một mod đã xóa câu trả lời (trong 30 giây) để sao chép nội dung bên dưới câu hỏi của bạn và sau đó anh ta lại xóa nó. Đó là lý do tại sao câu trả lời của bạn có dấu thời gian xóa gần đây này. Tôi đã viết nhận xét ở trên bởi vì mục đích của tôi AddBusinessDayslà giải pháp chung nhất ở đây hoạt động trong mọi trường hợp tôi cần. Tôi đã sao chép nó vào một trong những dự án hiện tại của mình (sau khi sửa đổi một chút và dịch sang C ++), cảm ơn vì mã :) Nó đã giúp rất nhiều vì thật khó để làm đúng tất cả các trường hợp.
— Slauma

Câu trả lời:


134

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

}

Chức năng thứ hai, GetBusinessDays, có thể được triển khai 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;
    }
}

Đối với giải pháp thứ hai, một giải pháp là lấy chênh lệch giữa ngày tháng và ngày + ngày. Điều này hay ở chỗ nó đảm bảo rằng hai chức năng sẽ đồng bộ hóa đúng cách và loại bỏ phần dư thừa.
— Brian

Nguồn cấp dữ liệu ngày hiện tại, chạy trong 0 đến 10 ngày làm việc, nó luôn không thành công vào thứ Tư.
— Adrian Godong

1
Vâng, cuối cùng thì chúng tôi cũng đến đó. (Tôi nói 'chúng tôi' vì đóng góp nhỏ bé của tôi!) Được bình chọn cho nỗ lực.
— Noldorin

Cảm ơn ý kiến ​​đóng góp của bạn Noldorin, rất tiếc tôi chỉ có thể ủng hộ ý kiến ​​của bạn!
— Patrick McDonald

3
DateTime.AddDays hoạt động với số âm. Điều này không tuân theo chính xác mô hình giống như việc sử dụng số âm với AddBusinessDays cho phép các ngày không phải làm việc được chọn.
— Ristogod

63

bằng cách sử dụng Fluent DateTime :

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

Đâ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

1
OP yêu cầu không có vòng lặp, trong khi cái này rõ ràng có vòng lặp. Không có gì thanh lịch khi làm một việc gì đó theo cách kém hiệu quả nhất.
— Neolisk

13

Tôi đã tạo một tiện ích mở rộng cho phép bạn thêm hoặc bớt ngày làm việc. Sử dụng một số ngày kinh doanh âm để trừ. Tôi nghĩ rằng đó là một giải pháp khá thanh lịch. Nó dường như 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();
        }
    }
}

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 muốn là Thứ Hai.
— Slauma

3
@Slauma: Đây là cách 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

3
@Slauma Chương trình hoạt động như dự định. Hãy suy nghĩ về nó một cách hợp lý. Nếu điều gì đó liên quan đến 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 đã nói, có hợp lý không khi nói với họ rằng điều đó phải được thực hiện trước thứ Hai ?!
— Riegardt Steyn

8

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

Ví dụ:

  • Thứ 4 - 3 ngày làm việc = Thứ 6 tuần trước
  • Thứ 4 + 3 ngày làm việc = Thứ 2
  • 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 đã viết xong 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 mà tôi nghĩ sẽ hữu ích khi sử dụng ở nhữ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 đến điều đó, bạn có thể thay thế if (MyClass.IsBusinessDay(date))bằng nếuif ((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);

Đâ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ứ 4 -3 ngày làm việc Thứ 6 Thứ 6
Thứ 4 +3 ngày làm việc Thứ 2 Thứ 2
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

Tôi đã thực hiện phương pháp thứ 2 là một phương pháp mở rộng cũng như: public static bool IsBusinessDay (ngày DateTime này)
— Andy B

2
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

Tôi đến muộn để có câu trả lời, nhưng tôi đã tạo 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 ngày làm việc ... Tôi để nó ở đây: Quản lý ngày làm việc


2
Thật không may, đây là GNU được cấp phép nên là "chất độc hợp pháp" cho bất kỳ ứng dụng thương mại nào. Bất kỳ cơ hội nào bạn muốn thư giãn điều này thành "MIT" hoặc "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

1
Tôi vừa thay đổi giấy phép thành MIT (tôi không muốn chặn bất cứ thứ gì trên một cái gì đó đơn giản). Tôi sẽ xem xét đề xuất khác của bạn.
— Boneless,

Thật tuyệt, sẽ rất thú vị nếu thấy quản lý ngày làm việc theo quốc gia vì một số quốc gia có thể có các ngày làm việc khác từ Thứ Hai đến Thứ Sáu.
— serializer

1

Giải pháp thực sự duy nhất là để những cuộc gọi đó truy cập vào bảng cơ sở dữ liệu xác định lịch cho doanh nghiệp của bạn. Bạn có thể viết mã nó cho một tuần làm việc từ thứ Hai đến thứ Sáu mà không có quá nhiều khó khăn nhưng xử lý các ngày nghỉ sẽ là một thách thức.

Đã chỉnh sửa để thêm giải pháp từng phần không trang nhã và chưa được thử nghiệm:

public static DateTime AddBusinessDays(this DateTime date, int days)
{
    for (int index = 0; index < days; index++)
    {
        switch (date.DayOfWeek)
        {
            case DayOfWeek.Friday:
                date = date.AddDays(3);
                break;
            case DayOfWeek.Saturday:
                date = date.AddDays(2);
                break;
            default:
                date = date.AddDays(1);
                break;
         }
    }
    return date;
}

Ngoài ra, tôi đã vi phạm yêu cầu không có vòng lặp.


Tôi không nghĩ rằng vụ án thứ bảy sẽ xảy ra.
— CoderDennis

@Dennis - nó sẽ xảy ra nếu ngày trôi qua là thứ bảy.
— Jamie Ide

Tôi đã tự do chỉnh sửa mã của bạn để làm cho nó hoạt động. Vui lòng kiểm tra mã trước khi đăng nó vào lần sau, cảm ơn bạn.
— bytecode77

Và tôi nghĩ rằng số phiếu ủng hộ không nói lên điều gì cho chính nó. Cảm ơn!
— Jamie Ide

1

Tôi đang phục hồi bài đăng này vì hôm nay tôi phải tìm cách loại trừ không chỉ thứ bảy và chủ nhật các ngày trong tuần mà còn cả ngày lễ. Cụ thể hơn, tôi cần xử lý nhiều nhóm ngày nghỉ khác nhau, bao gồm:

  • các ngày lễ bất biến của đất nước (ít nhất là đối với các nước phương Tây - chẳng hạn như tháng Giêng, 01).
  • các ngày lễ được tính toán (chẳng hạn như Lễ Phục sinh và Thứ hai Lễ Phục sinh).
  • các ngày lễ cụ thể của từng quốc gia (chẳng hạn như ngày giải phóng Ý hoặc ID4 của Hoa Kỳ).
  • các ngày lễ cụ thể của thị trấn (chẳng hạn như Ngày bảo trợ của Thánh Rome).
  • bất kỳ ngày lễ tùy chỉnh nào khác (chẳng hạn như "ngày mai văn phòng của chúng tôi sẽ đóng cửa").

Cuối cùng, tôi đã tìm ra tập hợp các lớp trợ giúp / mở rộng sau: mặc dù chúng không được thanh lịch một cách rõ ràng, vì chúng sử dụng rất nhiều các vòng lặp không hiệu quả, nhưng chúng đủ tốt để giải quyết các vấn đề của tôi. Tôi đang bỏ toàn bộ mã nguồn ở đây trong bài đăng này, hy vọng nó cũng sẽ hữu ích cho người khác.

Mã nguồn

/// <summary>
/// Helper/extension class for manipulating date and time values.
/// </summary>
public static class DateTimeExtensions
{
    /// <summary>
    /// Calculates the absolute year difference between two dates.
    /// </summary>
    /// <param name="dt1"></param>
    /// <param name="dt2"></param>
    /// <returns>A whole number representing the number of full years between the specified dates.</returns>
    public static int Years(DateTime dt1,DateTime dt2)
    {
        return Months(dt1,dt2)/12;
        //if (dt2<dt1)
        //{
        //    DateTime dt0=dt1;
        //    dt1=dt2;
        //    dt2=dt0;
        //}

        //int diff=dt2.Year-dt1.Year;
        //int m1=dt1.Month;
        //int m2=dt2.Month;
        //if (m2>m1) return diff;
        //if (m2==m1 && dt2.Day>=dt1.Day) return diff;
        //return (diff-1);
    }

    /// <summary>
    /// Calculates the absolute year difference between two dates.
    /// Alternative, stand-alone version (without other DateTimeUtil dependency nesting required)
    /// </summary>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <returns></returns>
    public static int Years2(DateTime start, DateTime end)
    {
        return (end.Year - start.Year - 1) +
            (((end.Month > start.Month) ||
            ((end.Month == start.Month) && (end.Day >= start.Day))) ? 1 : 0);
    }

    /// <summary>
    /// Calculates the absolute month difference between two dates.
    /// </summary>
    /// <param name="dt1"></param>
    /// <param name="dt2"></param>
    /// <returns>A whole number representing the number of full months between the specified dates.</returns>
    public static int Months(DateTime dt1,DateTime dt2)
    {
        if (dt2<dt1)
        {
            DateTime dt0=dt1;
            dt1=dt2;
            dt2=dt0;
        }

        dt2=dt2.AddDays(-(dt1.Day-1));
        return (dt2.Year-dt1.Year)*12+(dt2.Month-dt1.Month);
    }

    /// <summary>
    /// Returns the higher of the two date time values.
    /// </summary>
    /// <param name="dt1">The first of the two <c>DateTime</c> values to compare.</param>
    /// <param name="dt2">The second of the two <c>DateTime</c> values to compare.</param>
    /// <returns><c>dt1</c> or <c>dt2</c>, whichever is higher.</returns>
    public static DateTime Max(DateTime dt1,DateTime dt2)
    {
        return (dt2>dt1?dt2:dt1);
    }

    /// <summary>
    /// Returns the lower of the two date time values.
    /// </summary>
    /// <param name="dt1">The first of the two <c>DateTime</c> values to compare.</param>
    /// <param name="dt2">The second of the two <c>DateTime</c> values to compare.</param>
    /// <returns><c>dt1</c> or <c>dt2</c>, whichever is lower.</returns>
    public static DateTime Min(DateTime dt1,DateTime dt2)
    {
        return (dt2<dt1?dt2:dt1);
    }

    /// <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>
    /// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
    /// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
    public static DateTime AddBusinessDays(
        this DateTime current, 
        int days, 
        IEnumerable<DateTime> holidays = null)
    {
        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
                || (holidays != null && holidays.Contains(current.Date))
                );
        }
        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>
    /// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
    /// <returns>A <see cref="DateTime"/> increased by a given number of business days.</returns>
    public static DateTime SubtractBusinessDays(
        this DateTime current, 
        int days,
        IEnumerable<DateTime> holidays)
    {
        return AddBusinessDays(current, -days, holidays);
    }

    /// <summary>
    /// Retrieves the number of business days from two dates
    /// </summary>
    /// <param name="startDate">The inclusive start date</param>
    /// <param name="endDate">The inclusive end date</param>
    /// <param name="holidays">An optional list of holiday (non-business) days to consider.</param>
    /// <returns></returns>
    public static int GetBusinessDays(
        this DateTime startDate, 
        DateTime endDate,
        IEnumerable<DateTime> holidays)
    {
        if (startDate > endDate)
            throw new NotSupportedException("ERROR: [startDate] cannot be greater than [endDate].");

        int cnt = 0;
        for (var current = startDate; current < endDate; current = current.AddDays(1))
        {
            if (current.DayOfWeek == DayOfWeek.Saturday
                || current.DayOfWeek == DayOfWeek.Sunday
                || (holidays != null && holidays.Contains(current.Date))
                )
            {
                // skip holiday
            }
            else cnt++;
        }
        return cnt;
    }

    /// <summary>
    /// Calculate Easter Sunday for any given year.
    /// src.: https://stackoverflow.com/a/2510411/1233379
    /// </summary>
    /// <param name="year">The year to calcolate Easter against.</param>
    /// <returns>a DateTime object containing the Easter month and day for the given year</returns>
    public static DateTime GetEasterSunday(int year)
    {
        int day = 0;
        int month = 0;

        int g = year % 19;
        int c = year / 100;
        int h = (c - (int)(c / 4) - (int)((8 * c + 13) / 25) + 19 * g + 15) % 30;
        int i = h - (int)(h / 28) * (1 - (int)(h / 28) * (int)(29 / (h + 1)) * (int)((21 - g) / 11));

        day = i - ((year + (int)(year / 4) + i + 2 - c + (int)(c / 4)) % 7) + 28;
        month = 3;

        if (day > 31)
        {
            month++;
            day -= 31;
        }

        return new DateTime(year, month, day);
    }

    /// <summary>
    /// Retrieve holidays for given years
    /// </summary>
    /// <param name="years">an array of years to retrieve the holidays</param>
    /// <param name="countryCode">a country two letter ISO (ex.: "IT") to add the holidays specific for that country</param>
    /// <param name="cityName">a city name to add the holidays specific for that city</param>
    /// <returns></returns>
    public static IEnumerable<DateTime> GetHolidays(IEnumerable<int> years, string countryCode = null, string cityName = null)
    {
        var lst = new List<DateTime>();

        foreach (var year in years.Distinct())
        {
            lst.AddRange(new[] {
                new DateTime(year, 1, 1),       // 1 gennaio (capodanno)
                new DateTime(year, 1, 6),       // 6 gennaio (epifania)
                new DateTime(year, 5, 1),       // 1 maggio (lavoro)
                new DateTime(year, 8, 15),      // 15 agosto (ferragosto)
                new DateTime(year, 11, 1),      // 1 novembre (ognissanti)
                new DateTime(year, 12, 8),      // 8 dicembre (immacolata concezione)
                new DateTime(year, 12, 25),     // 25 dicembre (natale)
                new DateTime(year, 12, 26)      // 26 dicembre (s. stefano)
            });

            // add easter sunday (pasqua) and monday (pasquetta)
            var easterDate = GetEasterSunday(year);
            lst.Add(easterDate);
            lst.Add(easterDate.AddDays(1));

            // country-specific holidays
            if (!String.IsNullOrEmpty(countryCode))
            {
                switch (countryCode.ToUpper())
                {
                    case "IT":
                        lst.Add(new DateTime(year, 4, 25));     // 25 aprile (liberazione)
                        break;
                    case "US":
                        lst.Add(new DateTime(year, 7, 4));     // 4 luglio (Independence Day)
                        break;

                    // todo: add other countries

                    case default:
                        // unsupported country: do nothing
                        break;
                }
            }

            // city-specific holidays
            if (!String.IsNullOrEmpty(cityName))
            {
                switch (cityName)
                {
                    case "Rome":
                    case "Roma":
                        lst.Add(new DateTime(year, 6, 29));  // 29 giugno (s. pietro e paolo)
                        break;
                    case "Milano":
                    case "Milan":
                        lst.Add(new DateTime(year, 12, 7));  // 7 dicembre (s. ambrogio)
                        break;

                    // todo: add other cities

                    default:
                        // unsupported city: do nothing
                        break;

                }
            }
        }
        return lst;
    }
}

Thông tin sử dụng

Mã này khá dễ hiểu, tuy nhiên đây là một vài ví dụ để giải thích cách bạn có thể sử dụng nó.

Thêm 10 ngày làm việc (chỉ bỏ qua các ngày trong tuần thứ bảy và chủ nhật)

var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10);

Thêm 10 ngày làm việc (bỏ qua thứ bảy, chủ nhật và tất cả các ngày lễ bất biến của quốc gia cho năm 2019)

var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019));

Thêm 10 ngày làm việc (bỏ qua thứ bảy, chủ nhật và tất cả các ngày lễ của Ý cho năm 2019)

var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT"));

Thêm 10 ngày làm việc (bỏ qua thứ bảy, chủ nhật, tất cả các ngày lễ của Ý và các ngày lễ cụ thể ở Rome cho năm 2019)

var dtResult = DateTimeUtil.AddBusinessDays(srcDate, 10, GetHolidays(2019, "IT", "Rome"));

Các chức năng trên và ví dụ mã được giải thích thêm trong bài đăng này của blog của tôi.


0
    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;
    }

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

0

Tôi muốn có một "AddBusinessDays" hỗ trợ số ngày âm để 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 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ừ kỷ nguyên, vì điều đó được hiển thị bởi lớp LocalDate JDK8 mới và tôi đang làm việc trong Java. Tuy nhiên, nên đơn giản để thích ứng với các cài đặt khác.

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

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


0

Tôi tin rằng đây có thể là một cách đơn giản hơn để GetBusinessDays:

    public int GetBusinessDays(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

Đây là mã của tôi với cả ngày khởi hành và ngày giao hàng cho khách hàng.

            // Calculate departure date
            TimeSpan DeliveryTime = new TimeSpan(14, 30, 0); 
            TimeSpan now = DateTime.Now.TimeOfDay;
            DateTime dt = DateTime.Now;
            if (dt.TimeOfDay > DeliveryTime) dt = dt.AddDays(1);
            if (dt.DayOfWeek == DayOfWeek.Saturday) dt = dt.AddDays(1);
            if (dt.DayOfWeek == DayOfWeek.Sunday) dt = dt.AddDays(1);
            dt = dt.Date + DeliveryTime;
            string DepartureDay = "today at "+dt.ToString("HH:mm");
            if (dt.Day!=DateTime.Now.Day)
            {
                DepartureDay = dt.ToString("dddd at HH:mm", new CultureInfo(WebContextState.CurrentUICulture));
            }
            Return DepartureDay;

            // Caclulate delivery date
            dt = dt.AddDays(1);
            if (dt.DayOfWeek == DayOfWeek.Saturday) dt = dt.AddDays(1);
            if (dt.DayOfWeek == DayOfWeek.Sunday) dt = dt.AddDays(1);
            string DeliveryDay = dt.ToString("dddd", new CultureInfo(WebContextState.CurrentUICulture));
            return DeliveryDay;

Chúc bạn viết mã vui vẻ.


0
public static DateTime AddWorkingDays(this DateTime date, int daysToAdd)
{
    while (daysToAdd > 0)
    {
        date = date.AddDays(1);

        if (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday)
        {
            daysToAdd -= 1;
        }
    }

    return date;
}

0
public static int GetBusinessDays(this DateTime start, DateTime end)
            {
                return Enumerable.Range(0, (end- start).Days)
                                .Select(a => start.AddDays(a))
                                .Where(a => a.DayOfWeek != DayOfWeek.Sunday)
                                .Where(a => a.DayOfWeek != DayOfWeek.Saturday)
                                .Count();
    
            }

-1

Hy vọng điều này sẽ giúp ai đó.

private DateTime AddWorkingDays(DateTime addToDate, int numberofDays)
    {
        addToDate= addToDate.AddDays(numberofDays);
        while (addToDate.DayOfWeek == DayOfWeek.Saturday || addToDate.DayOfWeek == DayOfWeek.Sunday)
        {
            addToDate= addToDate.AddDays(1);
        }
        return addToDate;
    }

2
Điều này là không đúng. Trong hầu hết các trường hợp, nó sẽ không hoạt động. Không có khả năng giúp đỡ bất cứ ai.
— Neolisk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.