Pytanie AddBusinessDays i GetBusinessDays


Muszę znaleźć 2 eleganckie kompletne implementacje

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) lepiej (bez pętli).

EDYTOWAĆ:  Przez dni robocze mam na myśli dni robocze (poniedziałek, wtorek, środę, czwartek, piątek). Bez wakacji, z wyłączeniem weekendów.

Mam już jakieś brzydkie rozwiązania, które wydają się działać, ale zastanawiam się, czy są na to eleganckie sposoby. Dzięki


Oto, co napisałem do tej pory. Działa we wszystkich przypadkach i robi również negatywy. Nadal potrzebna jest implementacja 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


pochodzenie


Czy istnieją eleganckie rozwiązania, jeśli chodzi o coś równie nielogicznego jak daty? - Wyatt Barnett
Donno. Dlatego spytałem. - Adrian Zanescu
Czy masz na myśli wakacje? - James Conigliaro
Czy masz na myśli wakacje? - James Conigliaro. Nie - Adrian Zanescu
Głosowanie na ludzi, którzy próbują pomóc, nie jest strategią wygrywającą. - Jamie Ide


Odpowiedzi:


Ostatnia próba Twojej pierwszej funkcji:

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

}

Druga funkcja, GetBusinessDays, może być zaimplementowana w następujący sposób:

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



Po drugie, jednym z rozwiązań jest różnica między datą i datą + dniami. To miłe, ponieważ gwarantuje, że obie funkcje będą poprawnie synchronizowane i usunie nadmiarowość. - Brian
@Patrick: Powinien zostać naprawiony teraz - mam nadzieję, że nie masz nic przeciwko edycji. (Po prostu wycofaj się, jeśli zrobiłem coś głupiego.) - Noldorin
Tak, dotarliśmy na końcu. (Mówię "my" za mój malutki wkład!) Głosowałem za tym wysiłkiem. - Noldorin
Voted-up. Po prostu pokochałbym komentarze do kodu, które pomogłyby mi przejść przez to. - PeterX
DateTime.AddDays działa z liczbami ujemnymi. Nie jest to zgodne z tym samym wzorcem, w którym użycie liczb ujemnych w AddBusinessDays umożliwia wybór dni wolnych od pracy. - Ristogod


za pomocą Płynna data:

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

kod wewnętrzny jest następujący

    /// <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



To jedyne rozwiązanie, które zadziałało dla mnie po konwersji na VB.Net - Nicholas Head
OP nie zażądał żadnych pętli, podczas gdy ten wyraźnie ma pętle. Nie ma nic eleganckiego w robieniu czegoś w najmniej skuteczny sposób. - Neolisk


Stworzyłem rozszerzenie, które pozwala dodawać lub odejmować dni robocze. Użyj ujemnej liczby dni roboczych do odjęcia. Myślę, że to całkiem eleganckie rozwiązanie. Wydaje się, że działa we wszystkich przypadkach.

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

Przykład:

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



Wynik jest wątpliwy, jeśli datą źródłową jest sobota lub niedziela. Na przykład: sobota + 1 dzień roboczy ma miejsce we wtorek, a raczej w poniedziałek. - Slauma
@Slauma: Tak działa większość firm w Kanadzie. +1 dzień roboczy = "następny dzień roboczy", w przypadku soboty wtorek. Poniedziałek będzie "tym samym dniem roboczym". - Neolisk
@Slauma Program działa zgodnie z przeznaczeniem. Pomyśl o tym logicznie. Jeśli coś związanego z biznesem rozpocznie się w sobotę i musisz pozwolić na 1 dzień roboczy, aby ludzie mogli zareagować w ciągu tego dnia roboczego, czy byłoby sensowne, aby powiedzieć im, że musi to być zrobione do poniedziałku ?! - Heliac


Dla mnie musiałem mieć rozwiązanie, które pomijałoby weekendy i kończyło się negacją lub pozytywem. Moje kryteria były takie, że gdyby wyszedł i wylądował w weekend, musiałby przejść do poniedziałku. Gdyby wrócił i wylądował w weekend, musiałby przeskoczyć do piątku.

Na przykład:

  • Środa - 3 dni robocze = ostatni piątek
  • Środa + 3 dni robocze = poniedziałek
  • Piątek - 7 dni roboczych = ostatnia środa
  • Wtorek - 5 dni roboczych = ostatni wtorek

Cóż, masz pomysł;)

Skończyło się na pisaniu tej klasy rozszerzenia

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

Używa tej metody, która moim zdaniem mogłaby być użyta gdzie indziej ...

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

Jeśli nie chcesz się tym przejmować, możesz po prostu wymienić if (MyClass.IsBusinessDay(date)) z jeśli if ((date.DayOfWeek != DayOfWeek.Saturday) && (date.DayOfWeek != DayOfWeek.Sunday))

Teraz możesz zrobić

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

lub

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

Oto wyniki niektórych testów:

Przetestuj oczekiwany wynik
Środa -4 dni robocze czwartek czwartek
Środa - 3 dni robocze w piątek w piątek
Środa +3 dni robocze poniedziałek poniedziałek
Piątek - 7 dni roboczych środa środa
Wtorek -5 dni roboczych wtorek wtorek
Piątek +1 dni robocze poniedziałek poniedziałek
Sobota +1 dni robocze poniedziałek poniedziałek
Niedziela -1 dni robocze piątek piątek
Poniedziałek -1 dni robocze piątek piątek
Poniedziałek +1 dni robocze wtorek wtorek
Poniedziałek +0 dni roboczych poniedziałek poniedziałek

6
2017-09-17 15:27



Dobra robota! Dziękuję za napisanie tego. - Jean-Paul


W przypadku internacjonalizacji jest to trudne. Jak wspomniano w innych wątkach na SOF, wakacje różnią się z pewnością w zależności od kraju, a nawet od prowincji do prowincji. Większość rządów nie planuje wakacji przez ponad pięć lat.


2
2018-06-25 16:04



Nie mogę zrozumieć, dlaczego ktoś zajął się Ashem: to prawda, że ​​nie jest to odpowiedź na pytanie, ale Ash jest CAŁKOWICIE PRAWA, mam ten sam problem: moje oprogramowanie musi działać w Europie (pn-nd) A Afryka / Azja (sob. - pt.). Aby to zlekceważyć, trzeba być osłem (zwierzę, na wszelki wypadek), który nie widzi poza nosem: wygląda na to, że są ludzie na SO, którzy boją się wiedzy, po prostu nie chcą wiedzieć i wolą aby pozostać na ich uspokajającej ignorancji: ale nie robią usługi dla AZ na pierwszym miejscu (i dla nich też, ale to ich własny biznes) - M.Turrini
Ja (OP) przegłosowałem, ponieważ myślałem, że jasno wyjaśniłem, jakie są ograniczenia i wydawało się, że Ash nie zwraca uwagi. Przepraszam, jeśli to boli, że ktoś czuje, ale jest tylko przegłosowaniem - Adrian Zanescu
@AZ: Gdzie w pierwotnym pytaniu wspomniałeś o ograniczeniach internacjonalizacji? - Ash Machine
@AshMachine, dokładnie. Nie zrobił tego. - jwg
Święta różnią się w zależności od kraju, ale to pytanie nie ma nic wspólnego z wakacjami. Ma to związek z weekendami, co jasno wynika z opisu problemu. To powinien być komentarz. - 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





Spóźniam się na odpowiedź, ale stworzyłem małą bibliotekę z dostosowaniem niezbędnym do wykonywania prostych operacji w dni robocze ... Zostawiam to tutaj: Zarządzanie dniami pracy


1
2018-04-08 13:56



Niestety jest to licencja GNU, więc jest "legalną trucizną" dla każdej komercyjnej aplikacji. Czy jest szansa, że ​​rozluźnisz to na "MIT" lub "Apache"? - Tony O'Hagan
Niektóre statyczne listy powinny prawdopodobnie być tablicami (a nie listami powiązanymi). - Tony O'Hagan
Właśnie zmieniłem licencję na MIT (nie chcę niczego blokować na czymś tak prostym). Zajrzę do twojej drugiej propozycji. - Boneless
Miło, byłoby ciekawie zobaczyć zarządzanie dniem roboczym według kraju, ponieważ niektóre kraje mogą mieć inne dni robocze niż od poniedziałku do piątku. - serializer