Подтвердить что ты не робот

С# - Продолжительность между двумя DateTimes в минутах

Мне нужно определить продолжительность между двумя DateTimes в минутах.

Однако есть небольшой поворот:

  • исключить выходные дни
  • количество минут которые находятся между 7:00 и 7:00. например: [09/30/2010 6:39:00 PM] - [09/30/2010 7:39:00 PM] = 21 Minutes

Мне просто сложно придумать достойный способ сделать это, и я был бы признателен, если кто-нибудь может что-то предложить.

Спасибо.


Edit:

Я закончил работу с решением dtb. Существует только один особый случай, о котором нужно позаботиться: если время окончания после 7:00 вечера, посчитайте минуты с 7:00 до фактического времени окончания.

Вот как я его модифицировал:

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;

Снова спасибо за помощь.

4b9b3361

Ответ 1

Вы можете, конечно, использовать 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

(Примечание. Вероятно, вам нужно проверить множество угловых случаев, которые я полностью проигнорировал.)

Вспомогательные методы:

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

Сделайте свое время начала, получите количество минут до конца этого дня (т.е. 7 вечера).

Затем с 7 часов следующего дня подсчитайте количество дней до последнего дня (исключая любое время в конец дня).

Рассчитайте количество (если есть) выходных дней. (Для каждого выходного дня количество дней уменьшается на 2).

Проделайте некоторую простую математику оттуда, чтобы получить общее количество минут для подсчета дней.

Добавьте дополнительное время в последний день и в начало дня дополнительное время.

Ответ 3

Попробуйте выполнить следующую функцию 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;
}

Ответ 4

Я уверен, что что-то я пропустил.

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

Ответ 5

Это был довольно сложный вопрос. Для базового, прямолинейного подхода, я написал код ниже:

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

// Shift start date 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;

Я знаю, что это не выглядит программно красивым, но я не знаю, хорошо ли повторять через дни и часы между началом и концом.

Ответ 6

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
}

Ответ 7

Моя реализация:) Идея состоит в том, чтобы быстро вычислить полные недели и проделать оставшуюся неделю день за днем ​​...

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

Ответ 8

Используйте TimeSpan.TotalMinutes, вычитайте нерабочие дни, вычтите лишние часы.

Ответ 9

Я не буду писать какой-либо код, но имея DateTime, вы можете рассказать День недели, таким образом, вы знаете, сколько уикэндов в вашем диапазоне, поэтому вы можете узнать, сколько минут в выходные.

Так что это было бы не так сложно... конечно, должно быть оптимальное однострочное решение... но я думаю, вы можете обойти это.

Я забыл упомянуть, что вы также знаете минуты между 7:00 и 7:00, поэтому все, что вам нужно сделать, это вычесть правильное количество минут на разницу во времени, которую вы получаете.