2010-09-30 12 views
20

Potrzebuję określić czas trwania między dwoma DateTimes w minutach.C# - Czas między dwoma DateTimes w minutach

Jednak istnieje niewielkie skręcenie:

  • wyklucza weekendy
  • tylko liczyć minuty które są między 7:00 i 7:00 PM. na przykład: [09/30/2010 6:39:00 PM] - [09/30/2010 7:39:00 PM] = 21 Minutes

Ja tylko problemy ze wymyślanie przyzwoity sposób, aby to zrobić i będę wdzięczny jeśli ktoś może coś zasugerować.

Dzięki.


Edit:

I skończyło się za pomocą roztworu DTB. Jest tylko jeden szczególny przypadek, który należy załatwić: jeśli czas zakończenia przypada po godzinie 19:00, policz minuty od godziny 7:00 do faktycznego czasu zakończenia.

To jak ja zmodyfikowano go:

var minutes = from day in start.DaysInRangeUntil(end) 
       where !day.IsWeekendDay() 
       let st = Helpers.Max(day.AddHours(7), start) 
       let en = (day.DayOfYear == end.DayOfYear ? 
          end : 
          Helpers.Min(day.AddHours(19), end) 
          ) 
       select (en - st).TotalMinutes; 

Ponownie, dzięki za pomoc.

+2

@ 0xA3, a nie dupę tego pytania, ponieważ to pytanie nie odnosi się do wykluczonych dni i czasów tutaj zadawanych. –

+2

@Kirk - to prawda. Nieskończona liczba sposobów korzystania z TimeSpan z niestandardowymi regułami wykluczania pozostawia pytania SO przez kolejne 10 lat. –

+0

Cóż, ludzie powinni pokazywać swoje działania, tak jak to miało być w szkole. :) –

Odpowiedz

23

Można oczywiście używać LINQ:

DateTime a = new DateTime(2010, 10, 30, 21, 58, 29); 
DateTime b = a + new TimeSpan(12, 5, 54, 24, 623); 

var minutes = from day in a.DaysInRangeUntil(b) 
       where !day.IsWeekendDay() 
       let start = Max(day.AddHours(7), a) 
       let end = Min(day.AddHours(19), b) 
       select (end - start).TotalMinutes; 

var result = minutes.Sum(); 

// result == 6292.89 

(Uwaga: Prawdopodobnie trzeba sprawdzać wielu przypadkach narożnych które całkowicie ignorowane.)

metody Helper:

static IEnumerable<DateTime> DaysInRangeUntil(this DateTime start, DateTime end) 
{ 
    return Enumerable.Range(0, 1 + (int)(end.Date - start.Date).TotalDays) 
        .Select(dt => start.Date.AddDays(dt)); 
} 

static bool IsWeekendDay(this DateTime dt) 
{ 
    return dt.DayOfWeek == DayOfWeek.Saturday 
     || dt.DayOfWeek == DayOfWeek.Sunday; 
} 

static DateTime Max(DateTime a, DateTime b) 
{ 
    return new DateTime(Math.Max(a.Ticks, b.Ticks)); 
} 

static DateTime Min(DateTime a, DateTime b) 
{ 
    return new DateTime(Math.Min(a.Ticks, b.Ticks)); 
} 
+2

+1, wciąż trochę się wzdrygam, gdy patrzę na ten kod, ale pochwalam kreatywne użycie LINQ. – JaredPar

+0

Czy mogę zaproponować rozszerzenie 'IsWeekend'? –

+0

Po pierwsze, chciałbym powiedzieć, że jest to rzeczywiście bardzo kompaktowy sposób radzenia sobie z tym problemem. LINQ jest całkiem sprytny. Mój problem polega na tym, że robisz iterację przez każdy dzień od początku do końca. Gdyby to był 5-letni okres, wykonywałbyś wiele niepotrzebnych obliczeń. Nie zrozumcie mnie źle, dzisiejsze komputery są bardzo potężne i łatwo poradzą sobie z czymś takim. Łatwo jest jednak obliczyć całkowite godziny w okresie 7 dni => 5 * 12 = 60. Obliczenie pozostałej części jest trudne, ale nigdy nie będzie wymagało wykonywania iteracji przez więcej niż 7 dni. – dana

5

Weź swój czas rozpoczęcia, zdobądź minutę do końca tego dnia (tj. 19.00).

Następnie od 7 rano następnego dnia należy liczyć liczbę dni do ostatniego dnia (z wyłączeniem czasu do dnia końcowego).

Oblicz, ile (jeśli w ogóle) weekendów minęło. (Za każdy weekend zmniejsz liczbę dni o 2).

Wykonaj prostą matematykę, aby uzyskać całkowitą liczbę minut dla liczby dni.

Dodaj dodatkowy czas w ostatnim dniu i dniach rozpoczęcia, dodatkowy czas.

+0

Zgadzam się z tym - nie jest to proste jednoetapowe obliczenie, ale jest czysty, a kod powinien być czytelny. –

+0

Zrobiłbym kod, ale a) To psuje zabawę dla OP, i b) Mój VS jest na granicy. – Psytronic

4

Wypróbuj następującą funkcję DiffRange.

public static DateTime DayStart(DateTime date) 
{ 
    return date.Date.AddHours(7); 
} 

public static DateTime DayEnd(DateTime date) 
{ 
    return date.Date.AddHours(19); 
} 

public static TimeSpan DiffSingleDay(DateTime start, DateTime end) 
{ 
    if (start.Date != end.Date) { 
     throw new ArgumentException(); 
    } 

    if (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday) 
    { 
     return TimeSpan.Zero; 
    } 

    start = start >= DayStart(start) ? start : DayStart(start); 
    end = end <= DayEnd(end) ? end : DayEnd(end); 
    return end - start; 
} 

public static TimeSpan DiffRange(DateTime start, DateTime end) 
{ 
    if (start.Date == end.Date) 
    { 
     return DiffSingleDay(start, end); 
    } 

    var firstDay = DiffSingleDay(start, DayEnd(start)); 
    var lastDay = DiffSingleDay(DayStart(end), end); 

    var middle = TimeSpan.Zero; 
    var current = start.AddDays(1); 
    while (current.Date != end.Date) 
    { 
     middle = middle + DiffSingleDay(current.Date, DayEnd(current.Date)); 
     current = current.AddDays(1); 
    } 

    return firstDay + lastDay + middle; 
} 
+0

To daje znacznie inny wynik niż moje rozwiązanie. Czy możesz zauważyć, co jest nie tak z moim kodem? – dtb

+0

@dtb, wygląda jak mój problem. Dodałem 14 zamiast 19. Poprawię – JaredPar

+0

Och, fajne. Twoja poprawiona wersja zwraca teraz ten sam wynik dla mojego przykładu. – dtb

1

Jestem pewien, że jest coś, co przeoczyłem.

TimeSpan CalcBusinessTime(DateTime a, DateTime b) 
    { 
    if (a > b) 
    { 
     DateTime tmp = a; 
     a = b; 
     b = tmp; 
    } 

    if (a.TimeOfDay < new TimeSpan(7, 0, 0)) 
     a = new DateTime(a.Year, a.Month, a.Day, 7, 0, 0); 
    if (b.TimeOfDay > new TimeSpan(19, 0, 0)) 
     b = new DateTime(b.Year, b.Month, b.Day, 19, 0, 0); 

    TimeSpan sum = new TimeSpan(); 
    TimeSpan fullDay = new TimeSpan(12, 0, 0); 
    while (a < b) 
    { 
     if (a.DayOfWeek != DayOfWeek.Saturday && a.DayOfWeek != DayOfWeek.Sunday) 
     { 
      sum += (b - a < fullDay) ? b - a : fullDay; 
     } 
     a = a.AddDays(1); 
    } 

    return sum; 
    } 
+0

@dtb, Dzięki, mam nadzieję, że wolna od błędów teraz. Może poprowadzę testy –

1

To było dość trudne pytanie. Dla podstawowego, prostego podejścia, napisałem poniższy kod:

DateTime start = new DateTime(2010, 01, 01, 21, 00, 00); 
DateTime end = new DateTime(2010, 10, 01, 14, 00, 00); 

// Shift start date's hour to 7 and same for end date 
// These will be added after doing calculation: 
double startAdjustmentMinutes = (start - start.Date.AddHours(7)).TotalMinutes; 
double endAdjustmentMinutes = (end - end.Date.AddHours(7)).TotalMinutes; 

// We can do some basic 
// mathematical calculation to find weekdays count: 
// divide by 7 multiply by 5 gives complete weeks weekdays 
// and adding remainder gives the all weekdays: 
int weekdaysCount = (((int)((end.Date - start.Date).Days/7) * 5) 
      + ((end.Date - start.Date).Days % 7)); 
// so we can multiply it by minutes between 7am to 7 pm 
int minutes = weekdaysCount * (12 * 60); 

// after adding adjustment we have the result: 
int result = minutes + startAdjustmentMinutes + endAdjustmentMinutes; 

wiem, że to nie wydaje programowo piękne, ale nie wiem, czy to dobrze wykonać iterację dniach i godzinach między początkiem i końcem .

1
static int WorkPeriodMinuteDifference(DateTime start, DateTime end) 
{ 
    //easier to only have to work in one direction. 
    if(start > end) 
     return WorkPeriodMinuteDifference(end, start); 
    //if weekend, move to start of next Monday. 
    while((int)start.DayOfWeek % 6 == 0) 
     start = start.Add(new TimeSpan(1, 0, 0, 0)).Date; 
    while((int)end.DayOfWeek % 6 == 0) 
     end = end.Add(new TimeSpan(1, 0, 0, 0)).Date; 
    //Move up to 07:00 or down to 19:00 
    if(start.TimeOfDay.Hours < 7) 
     start = new DateTime(start.Year, start.Month, start.Day, 7, 0, 0); 
    else if(start.TimeOfDay.Hours > 19) 
     start = new DateTime(start.Year, start.Month, start.Day, 19, 0, 0); 
    if(end.TimeOfDay.Hours < 7) 
     end = new DateTime(end.Year, end.Month, end.Day, 7, 0, 0); 
    else if(end.TimeOfDay.Hours > 19) 
     end = new DateTime(end.Year, end.Month, end.Day, 19, 0, 0); 

    TimeSpan difference = end - start; 

    int weeks = difference.Days/7; 
    int weekDays = difference.Days % 7; 
    if(end.DayOfWeek < start.DayOfWeek) 
     weekDays -= 2; 

    return (weeks * 5 * 12 * 60) + (weekDays * 12 * 60) + difference.Hours * 60 + difference.Minutes 
} 
1

Moja implementacja :) Chodzi o to, aby szybko obliczyć całkowite tygodnie, i chodzić pozostały tydzień dzień po dniu ...

public TimeSpan Compute(DateTime start, DateTime end) 
{ 
    // constant start/end times per day 
    TimeSpan sevenAM = TimeSpan.FromHours(7); 
    TimeSpan sevenPM = TimeSpan.FromHours(19); 

    if(start >= end) 
    { 
     throw new Exception("invalid date range"); 
    } 

    // total # of weeks 
    int completeWeeks = ((int)(end - start).TotalDays)/7; 

    // starting total 
    TimeSpan total = TimeSpan.FromHours(completeWeeks * 12 * 5); 

    // adjust the start date to be exactly "completeWeeks" past its original start 
    start = start.AddDays(completeWeeks * 7); 

    // walk days from the adjusted start to end (at most 7), accumulating time as we can... 
    for(
     // start at midnight 
     DateTime dt = start.Date; 

     // continue while there is time left 
     dt < end; 

     // increment 1 day at a time 
     dt = dt.AddDays(1) 
    ) 
    { 
     // ignore weekend 
     if((dt.DayOfWeek == DayOfWeek.Saturday) || 
      (dt.DayOfWeek == DayOfWeek.Sunday)) 
     { 
      continue; 
     } 

     // get the start/end time for each day... 
     // typically 7am/7pm unless we are at the start/end date 
     TimeSpan dtStartTime = ((dt == start.Date) && (start.TimeOfDay > sevenAM)) ? 
      start.TimeOfDay : sevenAM; 
     TimeSpan dtEndTime = ((dt == end.Date) && (end.TimeOfDay < sevenPM)) ? 
      end.TimeOfDay : sevenPM; 

     if(dtStartTime < dtEndTime) 
     { 
      total = total.Add(dtEndTime - dtStartTime); 
     } 
    } 

    return total; 
} 
0

nie będę pisać żadnego kodu, ale posiadające DateTime można określić dzień tygodnia, dzięki czemu wiesz, ile weekendów jest w twoim zasięgu, więc możesz powiedzieć, ile minut jest w weekend.

Więc nie będzie to takie trudne ... oczywiście musi istnieć optymalne rozwiązanie dla jednej linii ... ale myślę, że można się z tym obejść.

Zapomniałem wspomnieć, że znasz także minuty pomiędzy 19:00 a 7:00, więc wszystko, co musisz zrobić, to odjąć odpowiednią ilość minut do różnicy czasu, którą otrzymujesz.

1

Użyj TimeSpan.TotalMinutes, odejmij dni wolne od pracy, odejmij niepotrzebne godziny.

Powiązane problemy