AddBusinessDays e GetBusinessDays


93

Ho bisogno di trovare 2 eleganti implementazioni complete di

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) preferibile (nessun loop).

EDIT: Per giorni lavorativi intendo i giorni lavorativi (lunedì, martedì, mercoledì, giovedì, venerdì). Niente vacanze, esclusi solo i fine settimana.

Ho già alcune brutte soluzioni che sembrano funzionare, ma mi chiedo se ci siano modi eleganti per farlo. Grazie


Questo è quello che ho scritto finora. Funziona in tutti i casi e fa anche negativi. Serve ancora un'implementazione di 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
Esistono soluzioni eleganti quando si tratta di qualcosa di illogico come le date?
Wyatt Barnett,

Ti interessano le vacanze? - James Conigliaro. No
Adrian Zanescu

9
Votare le persone che stanno cercando di aiutare non è una strategia vincente.
Jamie Ide

1
Breve nota AddBusinessDayssull'implementazione nella domanda sopra (che in realtà era una risposta eliminata che ho proposto di annullare l'eliminazione; una mod invece ha copiato quella risposta alla domanda): Secondo me questa soluzione è migliore di tutte le risposte finora perché è l'unica uno che gestisce correttamente i valori negativi, sabato e domenica come origine e non necessita di una libreria di terze parti. (Ho creato un programmino per testare le diverse soluzioni qui.) Vorrei aggiungere solo if (businessDays == 0) return startDate;all'inizio del metodo per ottenere il risultato corretto anche per questo caso limite.
Slauma

1
@AZ .: La prima cancellazione era piuttosto vecchia. Dopo la mia richiesta di ripristinare la tua risposta, un mod aveva ripristinato la risposta (per 30 secondi) per copiare il contenuto sotto la tua domanda e poi l'ha cancellata di nuovo. Ecco perché la tua risposta ha questo timestamp di cancellazione recente. Ho scritto il commento sopra perché per il mio scopo la tua AddBusinessDaysera la soluzione più generale qui che ha funzionato in tutti i casi di cui avevo bisogno. L'ho copiato in uno dei miei progetti attuali (dopo una leggera modifica e traduzione in C ++), grazie per il codice :) Ha aiutato molto poiché è sorprendentemente difficile ottenere tutti i casi limite.
Slauma

Risposte:


134

Ultimo tentativo per la tua prima funzione:

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

}

La seconda funzione, GetBusinessDays, può essere implementata come segue:

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

Per il secondo, una soluzione è prendere la differenza tra data e data + giorni. Questo è utile in quanto garantisce che le due funzioni si sincronizzino correttamente e rimuove la ridondanza.
Brian

Data corrente del feed, eseguito da 0 a 10 giorni lavorativi, fallisce sempre il mercoledì.
Adrian Godong

1
Sì, alla fine ci siamo arrivati. (Dico "noi" per il mio piccolo contributo!) Ho votato per lo sforzo.
Noldorin

Grazie per il tuo contributo Noldorin, posso solo votare a favore dei tuoi commenti purtroppo!
Patrick McDonald

3
DateTime.AddDays funziona con numeri negativi. Ciò non segue correttamente lo stesso schema dell'utilizzo di numeri negativi con AddBusinessDays che consente di selezionare i giorni non lavorativi.
Ristogod

63

utilizzando Fluent DateTime :

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

il codice interno è il seguente

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

Questa è l'unica soluzione che ha effettivamente funzionato per me quando convertito in VB.Net
Nicholas

1
OP non ha richiesto loop, mentre questo ha chiaramente loop. Non c'è niente di elegante nel fare qualcosa nel modo meno efficiente.
Neolisk

13

Ho creato un'estensione che ti consente di aggiungere o sottrarre giorni lavorativi. Usa un numero negativo di BusinessDays per sottrarre. Penso che sia una soluzione piuttosto elegante. Sembra funzionare in tutti i casi.

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

Esempio:

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

Il risultato è discutibile se la data di origine è un sabato o una domenica. Ad esempio: sabato + 1 giorno lavorativo risulta martedì, dove preferisco aspettarmi lunedì.
Slauma

3
@Slauma: è così che operano la maggior parte delle aziende in Canada. +1 giorno lavorativo = "giorno lavorativo successivo", che in caso di sabato è martedì. Il lunedì sarebbe "lo stesso giorno lavorativo".
Neolisk

3
Il programma @Slauma funziona come previsto. Pensaci logicamente. Se qualcosa di correlato al lavoro inizia sabato e devi consentire alle persone 1 giorno lavorativo di reagire durante l'arco di detto giorno lavorativo, avrebbe senso dire loro che deve essere fatto entro lunedì ?!
Riegardt Steyn

8

Per me dovevo avere una soluzione che salti i fine settimana e andasse in negativo o in positivo. Il mio criterio era che se fosse andato avanti e fosse atterrato in un fine settimana avrebbe dovuto avanzare fino a lunedì. Se fosse tornato indietro e fosse atterrato in un fine settimana, sarebbe dovuto saltare a venerdì.

Per esempio:

  • Mercoledì - 3 giorni lavorativi = ultimo venerdì
  • Mercoledì + 3 giorni lavorativi = lunedì
  • Venerdì - 7 giorni lavorativi = ultimo mercoledì
  • Martedì - 5 giorni lavorativi = ultimo martedì

Bene, hai l'idea;)

Ho finito per scrivere questa classe di estensione

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

Usa questo metodo che ho pensato sarebbe stato utile usare altrove ...

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

Se non vuoi preoccuparti di questo, puoi semplicemente sostituirlo if (MyClass.IsBusinessDay(date))con ifif ((date.DayOfWeek != DayOfWeek.Saturday) && (date.DayOfWeek != DayOfWeek.Sunday))

Quindi ora puoi farlo

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

o

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

Ecco i risultati di alcuni test:

Risultato previsto del test
Mercoledì -4 giorni lavorativi giovedì giovedì
Mercoledì -3 giorni lavorativi venerdì venerdì
Mercoledì +3 giorni lavorativi lunedì lunedì
Venerdì -7 giorni lavorativi mercoledì mercoledì
Martedì -5 giorni lavorativi martedì martedì
Venerdì +1 giorni lavorativi lunedì lunedì
Sabato +1 giorni lavorativi lunedì lunedì
Domenica -1 giorni lavorativi venerdì venerdì
Lunedì -1 giorni lavorativi venerdì venerdì
Lunedì +1 giorni lavorativi martedì martedì
Lunedì + 0 giorni lavorativi lunedì lunedì

Ho reso il secondo metodo anche un metodo di estensione: public static bool IsBusinessDay (questa data DateTime)
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

Arrivo in ritardo per la risposta, ma ho realizzato una piccola libreria con tutte le personalizzazioni necessarie per fare semplici operazioni nei giorni lavorativi ... la lascio qui: Gestione Giorni Lavorativi


2
Sfortunatamente questo è concesso in licenza GNU, quindi è "veleno legale" per qualsiasi app commerciale. C'è qualche possibilità che ti rilassi con "MIT" o "Apache"?
Tony O'Hagan

Alcuni elenchi statici dovrebbero probabilmente essere array (piuttosto che elenchi collegati).
Tony O'Hagan

1
Ho appena cambiato la licenza in MIT (non voglio bloccare nulla su qualcosa di così semplice). Analizzerò la tua altra proposta.
Disossato

Bello, sarebbe interessante vedere la gestione dei giorni lavorativi per paese poiché alcuni paesi potrebbero avere giorni lavorativi diversi dal lunedì al venerdì.
serializzatore

1

L'unica vera soluzione è fare in modo che quelle chiamate accedano a una tabella di database che definisce il calendario per la tua azienda. Potresti codificarlo per una settimana lavorativa dal lunedì al venerdì senza troppe difficoltà, ma gestire le vacanze sarebbe una sfida.

Modificato per aggiungere una soluzione parziale non elegante e non testata:

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

Inoltre ho violato il requisito di assenza di loop.


Non credo che il caso del sabato sarebbe mai stato colpito.
CoderDennis

@ Dennis - lo sarebbe se la data passata fosse un sabato.
Jamie Ide,

Mi sono preso la libertà di modificare il tuo codice per farlo funzionare. Si prega di testare il codice prima di pubblicarlo la prossima volta, grazie.
bytecode77

E ho pensato che gli zero voti positivi parlassero da soli. Grazie!
Jamie Ide

1

Sto resuscitando questo post perché oggi dovevo trovare un modo per escludere non solo il sabato e la domenica nei giorni feriali ma anche i giorni festivi. Più specificamente, avevo bisogno di gestire varie serie di possibili vacanze, tra cui:

  • festività invarianti per paese (almeno per i paesi occidentali - come gennaio, 01).
  • festività calcolate (come Pasqua e Pasquetta).
  • festività specifiche del paese (come il giorno della liberazione italiana o gli Stati Uniti ID4).
  • festività specifiche della città (come il giorno di San Patrono di Roma).
  • eventuali altre festività personalizzate (tipo "domani il nostro ufficio sarà chiuso").

Alla fine, sono uscito con il seguente set di classi di supporto / estensioni: sebbene non siano palesemente eleganti, poiché fanno un uso massiccio di loop inefficienti, sono abbastanza decenti da risolvere i miei problemi per sempre. Sto rilasciando l'intero codice sorgente qui in questo post, sperando che possa essere utile anche a qualcun altro.

Codice sorgente

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

Informazioni sull'utilizzo

Il codice è abbastanza autoesplicativo, tuttavia ecco un paio di esempi per spiegare come puoi usarlo.

Aggiungi 10 giorni lavorativi (saltando solo i giorni feriali di sabato e domenica)

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

Aggiungi 10 giorni lavorativi (saltando il sabato, la domenica e tutte le festività nazionali per il 2019)

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

Aggiungi 10 giorni lavorativi (salta sabato, domenica e tutte le festività italiane per il 2019)

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

Aggiungi 10 giorni lavorativi (saltando sabato, domenica, tutte le festività italiane e le festività specifiche di Roma per il 2019)

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

Le funzioni di cui sopra e gli esempi di codice sono ulteriormente spiegati in questo post del mio blog.


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

in futuro aggiungi un po 'più di contesto per la risposta e forse perché hai messo quello che hai :)
dax

0

Volevo un "AddBusinessDays" che supportasse un numero negativo di giorni da aggiungere e ho concluso con questo:

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

Nessun loop richiesto, quindi dovrebbe essere ragionevolmente veloce anche per aggiunte "grandi".

Funziona con i giorni espressi come numero di giorni di calendario dall'epoca, poiché è esposto dalla nuova classe LocalDate JDK8 e stavo lavorando in Java. Tuttavia, dovrebbe essere semplice adattarsi ad altre impostazioni.

Le proprietà fondamentali sono che addDaysrestituisce sempre un giorno della settimana, e che per tutti de n,daysBetween(d, addDays(d, n)) == n

Nota che in teoria l'aggiunta di 0 giorni e la sottrazione di 0 giorni dovrebbero essere operazioni diverse (se la tua data è una domenica, l'aggiunta di 0 giorni dovrebbe portarti a lunedì e la sottrazione di 0 giorni dovrebbe portarti a venerdì). Poiché non esiste uno 0 negativo (al di fuori del virgola mobile!), Ho scelto di interpretare un argomento n = 0 nel senso di aggiungere zero giorni.


0

Credo che questo potrebbe essere un modo più semplice per 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

Ecco il mio codice con la data di partenza e la data di consegna al cliente.

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

Buona codifica.


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

Spero che questo aiuti qualcuno.

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
Questo non è corretto. Nella maggior parte dei casi non funzionerà. È improbabile che aiuti qualcuno.
Neolisk
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.