2

Just wondering if there is a more optimized and/or neater way (using LINQ for example) of writing what I have below to get a list of business week date ranges between two dates?

This is what I have currently ..

// Some storage
public class Bucket
{
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

// Other code removed for brevity ...

DateTime start = new DateTime(2015, 7, 1);
DateTime end = new DateTime(2015, 9, 1);

DayOfWeek firstDayOfWeek = DayOfWeek.Monday;
DayOfWeek lastDayOfWeek = DayOfWeek.Friday;

var buckets = new List<Bucket>();
var currentDate = start;
DateTime startOfBucket = currentDate;
DateTime endOfBucket = currentDate;
while (currentDate <= end)
{
    var currentDayOfWeek = currentDate.DayOfWeek;

    // Skip days outside the business week
    if (currentDayOfWeek >= firstDayOfWeek && currentDayOfWeek <= lastDayOfWeek)
    {
        if (currentDayOfWeek == firstDayOfWeek)
        {
            // Start a new bucket
            startOfBucket = currentDate;
        }

        if ((currentDayOfWeek == lastDayOfWeek) || (currentDate == end))
        {
            // End of bucket
            endOfBucket = currentDate;

            // Create bucket
            buckets.Add(new Bucket() 
            { 
                StartDate = startOfBucket, 
                EndDate = endOfBucket 
            });

        }
    }

    currentDate = currentDate.AddDays(1);
}

And this will give me the following date ranges ...

  • Start: 01/Jul/2015 End: 03/Jul/2015
  • Start: 06/Jul/2015 End: 10/Jul/2015
  • Start: 13/Jul/2015 End: 17/Jul/2015
  • Start: 20/Jul/2015 End: 24/Jul/2015
  • Start: 27/Jul/2015 End: 31/Jul/2015
  • Start: 03/Aug/2015 End: 07/Aug/2015
  • Start: 10/Aug/2015 End: 14/Aug/2015
  • Start: 17/Aug/2015 End: 21/Aug/2015
  • Start: 24/Aug/2015 End: 28/Aug/2015
  • Start: 31/Aug/2015 End: 01/Sep/2015

N.B. The first and last weeks are purposefully not full weeks (they abide to the date range given).

Edit The solution provided here gives the number of days between the two dates but I am interested in getting the collection of date ranges.

Also, I don't need to account for any holidays.

Thanks,

Community
  • 1
  • 1
Gavin Sutherland
  • 1,666
  • 3
  • 23
  • 36

2 Answers2

1

It's quite handy using linq

var startDate = new DateTime(2015, 7, 1);
var endDate = new DateTime(2015, 9, 1);
var workDates = Enumerable.Range(0, (int)(endDate - startDate).TotalDays + 1)
    .Select(i => startDate.AddDays(i))
    .Where(date => (date.DayOfWeek != DayOfWeek.Saturday && date.DayOfWeek != DayOfWeek.Sunday))
    .Select(i => i);


var display = workDates
    .GroupAdjacentBy((x, y) => x.AddDays(1) == y)
    .Select(g => string.Format("Start: {0:dd/MMM/yyyy} End: {1:dd/MMM/yyyy}", g.First(), g.Last()));

With the extension method GroupAdjacentBy<T>

public static class IEnumerableExtension
{
    public static IEnumerable<IEnumerable<T>> GroupAdjacentBy<T>(
        this IEnumerable<T> source, Func<T, T, bool> predicate)
    {
        using (var e = source.GetEnumerator())
        {
            if (e.MoveNext())
            {
                var list = new List<T> { e.Current };
                var pred = e.Current;
                while (e.MoveNext())
                {
                    if (predicate(pred, e.Current))
                    {
                        list.Add(e.Current);
                    }
                    else
                    {
                        yield return list;
                        list = new List<T> { e.Current };
                    }
                    pred = e.Current;
                }
                yield return list;
            }
        }
    }
}

Fiddle

Eric
  • 5,675
  • 16
  • 24
0

This is based on Eric's accepted answer so please give him any upvote. I've just modified his solution to handle business weeks that could be 7 days long and also for one that could wrap a weekend.

var startDate = new DateTime(2015, 7, 1);
var endDate = new DateTime(2015, 9, 1);

DayOfWeek firstDayOfWeek = DayOfWeek.Monday;
DayOfWeek lastDayOfWeek = DayOfWeek.Friday;

var workDates = Enumerable.Range(0, (int)(endDate - startDate).TotalDays + 1)
            .Select(i => startDate.AddDays(i))
            .Where(date => 
                // Normal work weeks where first day of week is before last (numerically) e.g. Monday -> Friday or Sunday -> Saturday
                (firstDayOfWeek < lastDayOfWeek && date.DayOfWeek >= firstDayOfWeek && date.DayOfWeek <= lastDayOfWeek) ||
                // Cater for business weeks whose start and end dates wrap over the weekend e.g. Thursday -> Tuesday
                (lastDayOfWeek < firstDayOfWeek && (date.DayOfWeek >= firstDayOfWeek || date.DayOfWeek <= lastDayOfWeek)))
            .Select(i => i);

var display = workDates
            .GroupAdjacentBy((x, y) => x.AddDays(1) == y && !(x.DayOfWeek == lastDayOfWeek && y.DayOfWeek == firstDayOfWeek))
            .Select(g => string.Format("Start: {0:dd/MMM/yyyy} End: {1:dd/MMM/yyyy}", g.First(), g.Last())); 
Community
  • 1
  • 1
Gavin Sutherland
  • 1,666
  • 3
  • 23
  • 36