2009-11-30 45 views
23

Come posso ottenere il numero di giorni della settimana tra due date date senza solo scorrere tra le date e il conteggio dei giorni della settimana?Calcola il numero di giorni della settimana tra due date in C#

Sembra abbastanza semplice, ma io non riesco a trovare una risposta corretta conclusiva che si attiene i seguenti:

  1. Il totale dovrebbe essere compreso, quindi GetNumberOfWeekdays (nuovo DateTime (2009,11,30), nuovo DateTime (2009,12,4)) dovrebbe essere uguale a 5, da lunedì a venerdì.
  2. Prevedere giorni bisestili
  3. NON esegue semplicemente tutte le date tra il conteggio dei giorni della settimana.

Ho trovato un similar question con una answer che si avvicina, ma non è corretto

+0

Vuoi escludere solo sabato e domenica o prendere in considerazione anche le festività pubbliche? –

+2

nodatime, forse? –

+0

Solo sabato e domenica. Creerò un metodo separato, GetNumberOfBusinessDays (DateTime from, DateTime to, IEnumerable ) che consente di escludere un elenco di esclusione per gestire le festività escluse. –

risposta

11

Da questo link:

public static int Weekdays(DateTime dtmStart, DateTime dtmEnd) 
    { 
     // This function includes the start and end date in the count if they fall on a weekday 
     int dowStart = ((int)dtmStart.DayOfWeek == 0 ? 7 : (int)dtmStart.DayOfWeek); 
     int dowEnd = ((int)dtmEnd.DayOfWeek == 0 ? 7 : (int)dtmEnd.DayOfWeek); 
     TimeSpan tSpan = dtmEnd - dtmStart; 
     if (dowStart <= dowEnd) 
     { 
      return (((tSpan.Days/7) * 5) + Math.Max((Math.Min((dowEnd + 1), 6) - dowStart), 0)); 
     } 
     return (((tSpan.Days/7) * 5) + Math.Min((dowEnd + 6) - Math.Min(dowStart, 6), 5)); 
    } 


    [1]: http://www.eggheadcafe.com/community/aspnet/2/44982/how-to-calculate-num-of-w.aspx 

test (ogni scheda di prova 5):

int ndays = Weekdays(new DateTime(2009, 11, 30), new DateTime(2009, 12, 4)); 
    System.Console.WriteLine(ndays); 

    // leap year test 
    ndays = Weekdays(new DateTime(2000, 2,27), new DateTime(2000, 3, 5)); 
    System.Console.WriteLine(ndays); 

    // non leap year test 
    ndays = Weekdays(new DateTime(2007, 2, 25), new DateTime(2007, 3, 4)); 
    System.Console.WriteLine(ndays); 
+0

Puoi spiegare come funziona questo algoritmo? –

+0

ATTENZIONE! Questo algoritmo sembra non funzionare quando entrambe le date sono nella stessa settimana. Vedi la mia risposta per un po 'meno intelligente ma a mio avviso pezzo di codice funzionante. – eFloh

+0

Restituisce 3 per i giorni feriali (nuovo datetime (2012,3,1), nuovo datetime (2012,3,2)). La risposta dovrebbe essere 2. – smirkingman

2

Questo dovrebbe fare meglio della soluzione con dcp:

/// <summary> 
    /// Count Weekdays between two dates 
    /// </summary> 
    /// <param name="dtmStart">first date</param> 
    /// <param name="dtmEnd">second date</param> 
    /// <returns>weekdays between the two dates, including the start and end day</returns> 
    internal static int getWeekdaysBetween(DateTime dtmStart, DateTime dtmEnd) 
    { 
     if (dtmStart > dtmEnd) 
     { 
      DateTime temp = dtmStart; 
      dtmStart = dtmEnd; 
      dtmEnd = temp; 
     } 

     /* Move border dates to the monday of the first full week and sunday of the last week */ 
     DateTime startMonday = dtmStart; 
     int startDays = 1; 
     while (startMonday.DayOfWeek != DayOfWeek.Monday) 
     { 
      if (startMonday.DayOfWeek != DayOfWeek.Saturday && startMonday.DayOfWeek != DayOfWeek.Sunday) 
      { 
       startDays++; 
      } 
      startMonday = startMonday.AddDays(1); 
     } 

     DateTime endSunday = dtmEnd; 
     int endDays = 0; 
     while (endSunday.DayOfWeek != DayOfWeek.Sunday) 
     { 
      if (endSunday.DayOfWeek != DayOfWeek.Saturday && endSunday.DayOfWeek != DayOfWeek.Sunday) 
      { 
       endDays++; 
      } 
      endSunday = endSunday.AddDays(1); 
     } 

     int weekDays; 

     /* calculate weeks between full week border dates and fix the offset created by moving the border dates */ 
     weekDays = (Math.Max(0, (int)Math.Ceiling((endSunday - startMonday).TotalDays + 1))/7 * 5) + startDays - endDays; 

     return weekDays; 
    } 
3

risposta di eFloh ha avuto un giorno in più se l'ultimo giorno è stato un Sabato o Domenica. Questo lo risolverebbe.

 public static int Weekdays(DateTime dtmStart, DateTime dtmEnd) 
    { 
     if (dtmStart > dtmEnd) 
     { 
      DateTime temp = dtmStart; 
      dtmStart = dtmEnd; 
      dtmEnd = temp; 
     } 

     /* Move border dates to the monday of the first full week and sunday of the last week */ 
     DateTime startMonday = dtmStart; 
     int startDays = 1; 
     while (startMonday.DayOfWeek != DayOfWeek.Monday) 
     { 
      if (startMonday.DayOfWeek != DayOfWeek.Saturday && startMonday.DayOfWeek != DayOfWeek.Sunday) 
      { 
       startDays++; 
      } 
      startMonday = startMonday.AddDays(1); 
     } 

     DateTime endSunday = dtmEnd; 
     int endDays = 0; 
     while (endSunday.DayOfWeek != DayOfWeek.Sunday) 
     { 
      if (endSunday.DayOfWeek != DayOfWeek.Saturday && endSunday.DayOfWeek != DayOfWeek.Sunday) 
      { 
       endDays++; 
      } 
      endSunday = endSunday.AddDays(1); 
     } 

     int weekDays; 

     /* calculate weeks between full week border dates and fix the offset created by moving the border dates */ 
     weekDays = (Math.Max(0, (int)Math.Ceiling((endSunday - startMonday).TotalDays + 1))/7 * 5) + startDays - endDays; 

     if (dtmEnd.DayOfWeek == DayOfWeek.Saturday || dtmEnd.DayOfWeek == DayOfWeek.Sunday) 
     { 
      weekDays -= 1; 
     } 

     return weekDays; 

    } 
+0

Questo è corretto, ma più lento della soluzione iterativa ingenua. – smirkingman

2

avevo bisogno positivi/negativi (non valori assoluti) quindi ecco come ho risolto:

public static int WeekdayDifference(DateTime StartDate, DateTime EndDate) 
    { 
     DateTime thisDate = StartDate; 
     int weekDays = 0; 
     while (thisDate != EndDate) 
     { 
      if (thisDate.DayOfWeek != DayOfWeek.Saturday && thisDate.DayOfWeek != DayOfWeek.Sunday) { weekDays++; } 
      if (EndDate > StartDate) { thisDate = thisDate.AddDays(1); } else { thisDate = thisDate.AddDays(-1); } 
     } 

     /* Determine if value is positive or negative */ 
     if (EndDate > StartDate) { 
      return weekDays; 
     } 
     else 
     { 
      return weekDays * -1; 
     } 
    } 
+1

Come su 'Math.Abs' per ottenere il valore assoluto! – Marshal

1
public static List<DateTime> Weekdays(DateTime startDate, DateTime endDate) 
    { 
     if (startDate > endDate) 
     { 
      Swap(ref startDate, ref endDate); 
     } 
     List<DateTime> days = new List<DateTime>(); 

     var ts = endDate - startDate; 
     for (int i = 0; i < ts.TotalDays; i++) 
     { 
      var cur = startDate.AddDays(i); 
      if (cur.DayOfWeek != DayOfWeek.Saturday && cur.DayOfWeek != DayOfWeek.Sunday) 
       days.Add(cur); 
      //if (startingDate.AddDays(i).DayOfWeek != DayOfWeek.Saturday || startingDate.AddDays(i).DayOfWeek != DayOfWeek.Sunday) 
      //yield return startingDate.AddDays(i); 
     } 
     return days; 
    } 

e scambiare date

private static void Swap(ref DateTime startDate, ref DateTime endDate) 
    { 
     object a = startDate; 
     startDate = endDate; 
     endDate = (DateTime)a; 
    } 
+0

Puoi ottenere le date e il conteggio delle date da qui. –

1

Funzioni di utilità per ottenere una gamma di date:

public IEnumerable<DateTime> GetDates(DateTime begin, int count) 
{ 
    var first = new DateTime(begin.Year, begin.Month, begin.Day); 
    var maxYield = Math.Abs(count); 
    for (int i = 0; i < maxYield; i++) 
    { 
     if(count < 0) 
      yield return first - TimeSpan.FromDays(i); 
     else 
      yield return first + TimeSpan.FromDays(i);  
    } 
    yield break; 
} 

public IEnumerable<DateTime> GetDates(DateTime begin, DateTime end) 
{ 
    var days = (int)Math.Ceiling((end - begin).TotalDays); 
    return GetDates(begin, days); 
} 

LINQPad codice demo:

var begin = DateTime.Now; 
var end = begin + TimeSpan.FromDays(14); 

var dates = GetDates(begin, end); 
var weekdays = dates.Count(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday); 
var mondays = dates.Count(x => x.DayOfWeek == DayOfWeek.Monday); 
var firstThursday = dates 
    .OrderBy(d => d) 
    .FirstOrDefault(d => d.DayOfWeek == DayOfWeek.Thursday); 

dates.Dump("Dates in Range"); 
weekdays.Dump("Count of Weekdays"); 
mondays.Dump("Count of Mondays"); 
firstThursday.Dump("First Thursday"); 
13

O (1) soluzione:

// Count days from d0 to d1 inclusive, excluding weekends 
public static int countWeekDays(DateTime d0, DateTime d1) 
{ 
    int ndays = 1 + Convert.ToInt32((d1 - d0).TotalDays); 
    int nsaturdays = (ndays + Convert.ToInt32(d0.DayOfWeek))/7; 
    return ndays - 2 * nsaturdays 
      - (d0.DayOfWeek == DayOfWeek.Sunday ? 1 : 0) 
      + (d1.DayOfWeek == DayOfWeek.Saturday ? 1 : 0); 
} 

Esempi di gennaio 2014:

January 2014 
Su Mo Tu We Th Fr Sa 
      1 2 3 4 
5 6 7 8 9 10 11 
12 13 14 15 16 17 18 
19 20 21 22 23 24 25 
26 27 28 29 30 31 

countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 1)); // 1 
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 2)); // 2 
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 3)); // 3 
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 4)); // 3 
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 5)); // 3 
countWeekDays(new DateTime(2014, 1, 1), new DateTime(2014, 1, 6)); // 4 

N.B. Gli ingressi DateTime dovrebbero essere all'incirca alla stessa ora del giorno. Se stai creando oggetti DateTime basati esclusivamente su anno, mese e giorno come negli esempi sopra, allora dovresti stare bene. Come esempio contrario, dalle 12:01 am dal 1 ° gennaio alle 23:59, Jan 2 si estende solo per 2 giorni, ma la funzione sopra riportata conterà 3 se si utilizzano quei tempi.

+0

soluzione più semplice di gran lunga. –

+0

Ottima soluzione - confermata con casi di test di qualsiasi giorno della settimana come giorno di inizio e data di fine dallo stesso giorno a 3 settimane di distanza, e funziona perfettamente ogni volta –

+0

Non funziona. prova questo: countWeekDays (new DateTime (2016, 10, 1), new DateTime (2016, 10, 31)); // 22. Dovrebbe essere 21 – Tawani

0

Qui la funzione, che calcola il conteggio di DayOfWeek tra due date.Essere attentamente calcola che globalmente (include Giornata d'inizio anno e termina il giorno di calcolo):

private int GetWeekdayCount(DayOfWeek dayOfWeek, DateTime begin, DateTime end) 
    { 
     if (begin < end) 
     { 
      var timeSpan = end.Subtract(begin); 
      var fullDays = timeSpan.Days; 
      var count = fullDays/7; // количество дней равно как минимум количеству полных недель попавших в диапазон 
      var remain = fullDays % 7; // остаток от деления 

      // и вычислим попал ли еще один день в те куски недели, что остаются от полной 
      if (remain > 0) 
      { 
       var dowBegin = (int)begin.DayOfWeek; 
       var dowEnd = (int)end.DayOfWeek; 
       var dowDay = (int)dayOfWeek; 
       if (dowBegin < dowEnd) 
       { 
        // когда день недели начала меньше дня недели конца, например: 
        // начало  конец 
        // \/   \/ 
        // -- -- -- -- -- 
        // Вс Пн Вт Ср Чт Пт Сб 
        if (dowDay >= dowBegin && dowDay <= dowEnd) 
         count++; 
       } 
       else 
       { 
        // когда день недели начала больше дня недели конца, например: 
        // конец  начало 
        // \/   \/ 
        // -- --   -- -- 
        // Вс Пн Вт Ср Чт Пт Сб 
        if (dowDay <= dowEnd || dowDay >= dowBegin) 
         count++; 
       } 
      } 
      else if (begin.DayOfWeek == dayOfWeek) 
       count++; 

      return count; 
     } 
     return 0; 
    } 

Ecco un altro un semplice analogico di funzione precedente:

private int GetWeekdayCountStupid(DayOfWeek dayOfWeek, DateTime begin, DateTime end) 
    { 
     if (begin < end) 
     { 
      var count = 0; 
      var day = begin; 
      while (day <= end) 
      { 
       if (day.DayOfWeek == dayOfWeek) 
        count++; 
       day = day.AddDays(1); 
      } 
      return count; 
     } 
     return 0; 
    } 

E test per entrambe le funzioni:

[TestMethod()] 
    public void TestWeekdayCount() 
    { 
     var init = new DateTime(2000, 01, 01); 
     for (int day = 0; day < 7; day++) 
     { 
      var dayOfWeek = (DayOfWeek)day; 
      for (int shift = 0; shift < 8; shift++) 
      { 
       for (int i = 0; i < 365; i++) 
       { 
        var begin = init.AddDays(shift); 
        var end = init.AddDays(shift + i); 
        var count1 = GetWeekdayCount(dayOfWeek, begin, end); 
        var count2 = GetWeekdayCountStupid(dayOfWeek, begin, end); 
        Assert.AreEqual(count1, count2); 
       } 
      } 
     } 
    } 
0
var dates = new List<DateTime>(); 

     for (var dt = YourStartDate; dt <= YourEndDate; dt = dt.AddDays(1)) 
     { 

      if (dt.DayOfWeek != DayOfWeek.Sunday && dt.DayOfWeek != DayOfWeek.Saturday) 
      { dates.Add(dt); } 

     } 

in questo codice si potrebbe avere un elenco che tutti i giorni di affari tra due dat es.

se si desidera il conteggio di queste date è possibile ottenere dates.Count come numero intero. o se si desidera ottenere ogni giorno, è possibile unire l'elenco a una stringa.

Problemi correlati