Get a List of Dates In a Date Range - c#

Say I have a list of LockedDate.
A LockedDate has a DateTime and an IsYearly bool. If IsYearly is true then the year should not be considered because it could be any year. Time should never be considered.
Ex: X-Mas, Dec 25 is yearly.
Now I have a List of LockedDate.
There are no duplicates.
Now I need this function:
This function will do:
If a LockedDate is NOT yearly and the day, month, and year are within the range from source, add to return list.
If a LockedDate IS yearly, and its month / day fall in the range, then add a new date for each year in the range.
Say I have Dec 25 with IsYearly as true. My range is Jan 22 2013 to Feb 23 2015 inclusive. would need to add Dec 25 2013 as a new date and Dec 25 2014 as a new Date to the list.
List<Date> GetDateRange(List<LockedDate> source, DateTime start, DateTime end)
{
}
Thanks
Dec 25 Yearly -> Dec 25 2013, Dec 25 2014
Dec 2, 2011 NOT Yearly -> Nothing
March 25, 2013 => March 25 2013

This might give you at least an idea, it's not tested at all yet:
List<DateTime> GetDateRange(List<LockedDate> source, DateTime start, DateTime end)
{
if (start > end)
throw new ArgumentException("Start must be before end");
var ts = end - start;
var dates = Enumerable.Range(0, ts.Days)
.Select(i => start.AddDays(i))
.Where(d => source.Any(ld => ld.Date == d
|| (ld.IsYearly && ld.Date.Month == d.Month && ld.Date.Day == d.Day)));
return dates.ToList();
}
Update Here's the demo with your sample data, it seems to work: http://ideone.com/3KFi97

This code does what you want without using Linq:
List<DateTime> GetDateRange(List<LockedDate> source, DateTime start, DateTime end)
{
List<DateTime> result = new List<DateTime>();
foreach (LockedDate lockedDate in source)
{
if (!lockedDate.IsYearly && (lockedDate.Date >= start && lockedDate.Date <= end))
{
result.Add(lockedDate.Date);
}
else if (lockedDate.IsYearly && (lockedDate.Date >= start && lockedDate.Date <= end))
{
DateTime date = new DateTime(start.Year, lockedDate.Date.Month, lockedDate.Date.Day);
do
{
result.Add(date);
date = date.AddYears(1);
} while (date <= end);
}
}
return result;
}
Make sure to ask about parts you don't understand, so I can explain in detail, but I think it's pretty straightforward.
This code assumes your LockedDate class has a property Date for the DateTime object.

var notYearly = lockDates.Where(d => !d.IsYearly && (d.Date.Date >= start && d.Date.Date <= end)).Select(d => d.Date);
var years = ((end - start).Days / 365) + 2;
var startYear = start.Year;
var yearly = lockDates.Where(d => d.IsYearly)
.SelectMany(d => Enumerable.Range(startYear, years)
.Select(e => new DateTime(e, d.Date.Month, d.Date.Day))
.Where(i => i.Date >= start && i.Date <= end));
var allDates = notYearly.Union(yearly)
Should be more efficient than just iterating through all days between start and end and checking, if that date ok.

If you have a Class LockedDate like this:
public class LockedDate
{
public DateTime Date { get; set; }
public bool IsYearly { get; set; }
...
}
Than you could use this code to get your needed dates:
List<DateTime> GetDateRange(List<LockedDate> source, DateTime start, DateTime end)
{
List<DateTime> dt = new List<DateTime>();
foreach(LockedDate d in source)
{
if(!d.IsYearly)
{
if(start<=d.Date && d.Date<=end)
dt.Add(d.Date);
}
else
{
for(DateTime i = new DateTime(start.Year,d.Date.Month,d.Date.Day);i<=new DateTime(end.Year,d.Date.Month,d.Date.Day);i=i.AddYears(1))
{
dt.Add(i);
}
}
}
return dt;
}

Related

How to get all dates in the last X months

I want to get all dates in the last three months, so I did the following:
protected void BindPermissions(int empNum)
{
var permPeriod = new Dictionary<int, int>();
permPeriod.Add(DateTime.Now.Year, DateTime.Now.Month);
permPeriod.Add(DateTime.Now.AddMonths(-1).Year, (DateTime.Now.AddMonths(-1).Month));
permPeriod.Add(DateTime.Now.AddMonths(-2).Year, (DateTime.Now.AddMonths(-2).Month));
var dt = payload_object.AttendancePermissionBO.permissionList
.Where(x => x.empNum == empNum
&& ((x.permDate.Year == permPeriod.Keys.ElementAtOrDefault(0) && x.permDate.Month == permPeriod.Values.ElementAtOrDefault(0)) ||
(x.permDate.Year == permPeriod.Keys.ElementAtOrDefault(1) && x.permDate.Month == permPeriod.Values.ElementAtOrDefault(1)) ||
(x.permDate.Year == permPeriod.Keys.ElementAtOrDefault(2) && x.permDate.Month == permPeriod.Values.ElementAtOrDefault(2)))).ToList().OrderBy(x => x.permDate);
GV_PermissionHistory.DataSource = dt;
GV_PermissionHistory.DataBind();
}
Is there a better way to do that or this method suits what i need?
It seems like you do not really want all dates in the last three months, but you want all items in your collection where permDate is a date within some date range.
Given your own approach, that some date range seems to be the two previous months plus the entire current month. I.e. for 5th of July 2022, the date range is all of May, June and July 2022.
I think you can simplify your approach by defining a start date and an end date, and compare the permDate values with those two values. A straight-forward way of doing that could be:
var today = DateTime.Today;
var startMonth = today.AddMonths(-2);
var endMonth = today.AddMonths(1);
var startDate = new DateTime(startMonth.Year, startMonth.Month, 1);
var endDate = new DateTime(endMonth.Year, endMonth.Month, 1);
Then, you can use startDate and endDate in your filtering:
var dt = payload_object.AttendancePermissionBO.permissionList
.Where(x =>
x.empNum == empNum &&
x.permDate >= startDate &&
x.permDate < endDate)
.OrderBy(x => x.permDate)
.ToList();
You can use the below code for all the dates of last X- months. You can do some changes based on your requirements.
public static void Main()
{
var list= new List<DateTime>();
for(int i=0;i<3;i++){
var month= DateTime.Now.AddMonths(-i);
var monthDates= GetDates(month.Year, month.Month);
list.AddRange(monthDates);
}
foreach(var item in list){
Console.WriteLine(item.ToString());
}
}
public static List<DateTime> GetDates(int year, int month)
{
return Enumerable.Range(1, DateTime.DaysInMonth(year, month))
.Select(day => new DateTime(year, month, day))
.ToList();
}
Get the date (boundary) three months ago from today.
DateTime threeMonthsAgoDate = DateTime.Now.AddMonths(-3);
Filter the data for permDate that after (inclusive) the date from 3 months ago.
var dt = payload_object.AttendancePermissionBO.permissionList
.Where(x => x.empNum == empNum
&& x.permDate >= threeMonthsAgoDate.Date)
.OrderBy(x => x.permDate)
.ToList();
Updated:
This answer is for querying records from the last 3 months ago until the current date.
Based on Post Owner's requirements and existing code, what he needs is from
Start Date: 2022-05-01
End Date: 2022-07-31 (inclusive)
Hence #Astrid's answer is most accurate.

How to select weekends with an interval between them?

How can I select all weekends until the end of the year, with some criteria to be followed?
User input desired Weekend day:
18/12/2021
Software must out:
25/12/2022 (must be ignored)
01/01/2022
08/01/2022 (must be ignored)
15/01/2022
22/01/2022 (must be ignored)
29/01/2022 and so on...
What i have now:
public void GetWeekends() {
var lastWorkedWeekend = dateTimePicker1.Value;
var workedInSunday = checkBox1.Checked;
var list = new List < DateTime > ();
var weekends = GetDaysBetween(lastWorkedWeekend, DateTime.Today.AddDays(365)).Where(d => d.DayOfWeek == DayOfWeek.Saturday);
var selected = true;
for (int i = 0; i < weekends.Count(); i++) {
if (selected == false) {
list.Add(weekends.ElementAt(i));
selected = true;
} else {
selected = false;
}
}
}
I think I'd just scroll the input day forward until it was saturday (or calculate it, but i find the loop more self documenting than casting DayOfWeek to an int and factoring for sunday being 0) then add 14 days repeatedly. This skips over the 25th, etc..
var d = new DateTime(2021, 12, 18);
while(d.DayOfWeek != DayOfWeek.Saturday)
d += TimeSpan.FromDays(1);
while(d.Year == 2021){ //was your 2022 a typo? or maybe make this <= 2022.. I'm not sure what you want there..
d += TimeSpan.FromDays(14);
Console.WriteLine(d);
}
You might prefer DateTime.AddDays()..
Looks to me that you want to get the date of every second weekend from an initial date until the end of the year. In your example, you kinda bleed over to the next year.
public static void Main()
{
var dates = GetDateTimeRange(new DateTime(2021, 12, 18), new DateTime(2023, 1, 1), TimeSpan.FromDays(14));
foreach (var dateTime in dates.Skip(1))
{
Console.WriteLine(dateTime);
}
}
public static IEnumerable<DateTime> GetDateTimeRange(DateTime startingDate, DateTime endDate, TimeSpan interval)
{
var lastDate = startingDate;
while (lastDate < endDate)
{
yield return lastDate;
lastDate = lastDate.Add(interval);
}
}
This returns
01/01/2022 00:00:00
01/15/2022 00:00:00
01/29/2022 00:00:00
02/12/2022 00:00:00
02/26/2022 00:00:00
03/12/2022 00:00:00
03/26/2022 00:00:00
04/09/2022 00:00:00
04/23/2022 00:00:00
05/07/2022 00:00:00
05/21/2022 00:00:00
06/04/2022 00:00:00
06/18/2022 00:00:00
07/02/2022 00:00:00
07/16/2022 00:00:00
07/30/2022 00:00:00
08/13/2022 00:00:00
08/27/2022 00:00:00
09/10/2022 00:00:00
09/24/2022 00:00:00
10/08/2022 00:00:00
10/22/2022 00:00:00
11/05/2022 00:00:00
11/19/2022 00:00:00
12/03/2022 00:00:00
12/17/2022 00:00:00
12/31/2022 00:00:00
One approach is to use a method that creates an enumerable of dates. Once that's done you can use LINQ queries. If creating that enumerable is expensive you could just create one large range encompassing years and re-use it.
Or you might find better performance by just creating the range you need for each query.
public static class DateRanges
{
public static IEnumerable<DateOnly> GetRange(DateOnly start, DateOnly end)
{
for (var date = start; date <= end; date = date.AddDays(1))
{
yield return date;
}
}
}
It's not clear from your code what the criteria is, but this does what you described - all weekends from now to the end of the year.
var today = DateOnly.FromDateTime(DateTime.Today);
var lastDayOfYear = DateOnly.FromDateTime(new DateTime(DateTime.Today.Year, 12, 31));
var dates = DateRanges.GetRange(today, lastDayOfYear);
var weekendsOnly = dates.Where(date =>
date.DayOfWeek == DayOfWeek.Saturday);
Or if you prefer to create one date range and query it repeatedly:
// Big range of dates, 10 years into past and future.
// Create this once and re-use it
var dates = DateRanges.GetRange(
DateOnly.FromDateTime(DateTime.Today.AddYears(-10)),
DateOnly.FromDateTime(DateTime.Today.AddYears(10)));
var today = DateOnly.FromDateTime(DateTime.Today);
var lastDayOfYear = DateOnly.FromDateTime(new DateTime(DateTime.Today.Year, 12, 31));
var weekendsOnly = dates.Where(date =>
date >= today
&& date <= lastDayOfYear
&& date.DayOfWeek == DayOfWeek.Saturday);
In either case if you want every other Saturday you can add
.Where((date, i) => i % 2 == 0);
Or to maintain that readability you could put that in another extension like
public static IEnumerable<T> EveryOther<T>(this IEnumerable<T> source)
{
return source.Where((date, i) => i % 2 == 0);
}
so your query looks like
var everyOtherWeekend = dates
.Where(date =>
date >= today
&& date <= lastDayOfYear
&& date.DayOfWeek == DayOfWeek.Saturday)
.EveryOther();
I wouldn't position this as a better answer than those that iterate over a series of dates. The difference is that instead of having to write a method for any query you can start with a range of dates and then use LINQ to filter it. That makes it a little easier to read and to compose different queries.

Calculate Date Ranges in Chunks by Year

Writing a small application to calculate interest but the rate changes yearly. Needed to break the range into smaller date ranges when ever it crosses a year boundary. I wrote a little for loop to do it but it's rather clunky. Wondering if there are any built in functions to do this in C# (possible linq). Would basically be looking to return a list of date ranges with the corresponding base year (shortened code for readability).
static void Main(string[] args)
{
var dateStart = DateTime.Parse("2/10/2018");
var dateEnd = DateTime.Parse("3/10/2021");
var years = Years(dateStart, dateEnd);
var baseYear = dateStart.Year;
Console.WriteLine(baseYear);
var loopDateStart = dateStart;
var loopDateEnd = DateTime.Now;
for (int i = 0; i < years + 1; i++)
{
if (i < years) {
loopDateEnd = DateTime.Parse("1/1/" + (baseYear + 1));
Console.WriteLine(loopDateEnd + " ... " + loopDateStart);
Console.WriteLine((loopDateEnd - loopDateStart).Days);
loopDateStart = loopDateEnd;
baseYear++;
}
else {
loopDateEnd = dateEnd;
Console.WriteLine(loopDateEnd + " ... " + loopDateStart);
Console.WriteLine((loopDateEnd - loopDateStart).Days);
}
}
}
public static int Years(DateTime start, DateTime end)
{
return (end.Year - start.Year - 1) +
(((end.Month > start.Month) ||
((end.Month == start.Month) && (end.Day >= start.Day))) ? 1 : 0);
}
Sure, we can use LINQ:
var x = Enumerable.Range(dateStart.Year, (dateEnd.Year-dateStart.Year)+1)
.Select(y => new{
F = new[]{dateStart, new DateTime(y,1,1)}.Max(),
T = new[]{dateEnd, new DateTime(y,12,31)}.Min()
});
It generates an enumerable list of objects that have an F and a T property (from and to) that are your ranges.
It works by using Enumerable.Range to make a list of years: 2018,2019,2020,2021 by starting at 2108 and proceeding for 4 years (2018 to 2018 is one year entry, 2018 to 2021 is 4 year entries)
Then we just turn them into dates using new DateTime(year,amonth,aday) - when were making start dates, amonth and aday are 1 and 1, when making end dates they're 12 and 31
Then we just ask for every year y, "which date is greater, the startdate, or the 1-Jan-y" and "which date is lesser, the enddate or the 31-Dec-y " - for the initial and final date entry it's the startdate and the enddate that are greater and lesser. For other years it's the jan/dec dates. This gives the ranges you want
foreach(var xx in x){
Console.WriteLine(xx.F +" to "+xx.T);
}
2/10/2018 12:00:00 AM to 12/31/2018 12:00:00 AM
1/1/2019 12:00:00 AM to 12/31/2019 12:00:00 AM
1/1/2020 12:00:00 AM to 12/31/2020 12:00:00 AM
1/1/2021 12:00:00 AM to 3/10/2021 12:00:00 AM
If you want to do other work like the number of days between, you can do xx.T-xx.F in the loop, to make a timespan etc
Try:
var start = DateTime.Parse("4/5/2017");
var end = DateTime.Parse("3/1/2019");
DateTime chunkEnd;
for (var chunkStart = start; chunkStart < end; chunkStart = chunkEnd.AddDays(1))
{
var lastDay = new DateTime(chunkStart.Year, 12, 31);
chunkEnd = end > lastDay ? lastDay : end;
var days = (chunkEnd - chunkStart).Days;
Console.WriteLine($"{chunkStart:d} - {chunkEnd:d}; {days} days");
}
Produces:
4/5/2017 - 12/31/2017; 270 days
1/1/2018 - 12/31/2018; 364 days
1/1/2019 - 3/1/2019; 59 days
I came up with the following:
static IEnumerable<(DateTime,DateTime)> ChunkByYear(DateTime start, DateTime end)
{
// Splits <start,end> into chunks each belonging to a different year
while(start <= end)
{
var tempEnd = new DateTime(start.Year, 12, 31);
if(tempEnd >= end ) {
yield return (start, end);
yield break;
}
yield return (start, tempEnd);
start = tempEnd.AddDays(1);
}
}
Here are some results:
4/05/2017 to 3/01/2019:
4/05/2017->31/12/2017
1/01/2018->31/12/2018
1/01/2019->3/01/2019
4/05/2017 to 4/05/2017:
4/05/2017->4/05/2017
31/12/2017 to 31/12/2019:
31/12/2017->31/12/2017
1/01/2018->31/12/2018
1/01/2019->31/12/2019
31/12/2019 to 31/12/2019:
31/12/2019->31/12/2019
31/12/2018 to 1/01/2019:
31/12/2018->31/12/2018
1/01/2019->1/01/2019
Group by years:
using System.Collections.Generic;
using System.Linq;
...
static void Main(string[] args)
{
DateTime dateStart = DateTime.Parse("2/10/2018");
DateTime dateEnd = DateTime.Parse("3/10/2021");
// Group all possible dates by year
foreach(var group in GetDates(dateStart, dateEnd).GroupBy(date => date.Year))
{
Console.WriteLine(group.Key); // The key of the group is year
Console.WriteLine($"{group.Min()} ... {group.Max()}"); // Range: From minimum to maximum, order doesn't matter.
Console.WriteLine($"{group.First()} ... {group.Last()}"); //or Range version 2: From first to last, order matters.
Console.WriteLine(group.Count()); // Count days
}
}
/// <summary>
/// Get all days blindly, might need to pay attention to days on the boundaries
/// </summary>
private static IEnumerable<DateTime> GetDates(DateTime start, DateTime end)
{
// TODO: Check start <= end;
DateTime current = start;
while(current <= end)
{
yield return current;
current = current.AddDays(1);
}
}

Perform calculations on a set using only LINQ

I'd like to perform the following using only LINQ.
I have a list of time sheet entries with user's in and out times. The class looks like this:
public class TimeSheetLog
{
public Guid EmployeeId { get; set; }
public DateTime ClockInTimeStamp { get; set; }
public DateTime ClockOutTimeStamp { get; set; }
}
I'm passing a List<TimeSheetLog>() which contains all logs from the beginning of the year to date.
I'm trying to calculate the total work time -- regardless of employee -- for the month of January. Please also notice that I have a function named GetTimeDifferenceInMinutes() which calculates the number of minutes between two date/time values.
Here's what I currently have but I feel the whole thing can be done using LINQ only.
public static int GetTotalTimeWorked(List<TimeSheetLog> logs, DateTime startDate, DateTime endDate)
{
// I'm passing 1/1/2018 for startDate and 1/31/2018 for endDate to this function
var totalTimeWorkedInMinutes = 0;
var januaryLogs = logs.Where(x => x.ClockInTimeStamp >= startDate &&
x.ClockOutTimeStamp <= endDate);
foreach(var item in januaryLogs)
{
totalTimeWorkedInMinutes += GetTimeDifferenceInMinutes(item.ClockInTimeStamp, itemClockOutTimeStamp);
}
return totalTimeWorkedInMinutes;
}
var logsFilteredByDate = logs.Where(x => x.ClockInTimeStamp >= startDate &&
x.ClockOutTimeStamp <= endDate);
var totalTimeWorkedInMinutes = logsFilteredByDate.Sum(x =>
GetTimeDifferenceInMinutes(x.ClockInTimeStamp, x.ClockOutTimeStamp));
Or, to combine it all into one query, which is unnecessary and harder to read,
var totalTimeWorkedInMinutes = logs.Where(x => x.ClockInTimeStamp >= startDate &&
x.ClockOutTimeStamp <= endDate)
.Sum(x =>
GetTimeDifferenceInMinutes(x.ClockInTimeStamp, x.ClockOutTimeStamp));
you need sum
var tot = januaryLogs.Sum(item=>GetTimeDifferenceInMinutes(item.ClockInTimeStamp, itemClockOutTimeStamp));
Couldn't you do the Where with a Sum and do DateTime Subtract in the Sum, so
decimal total = logs.Where(x => x.ClockInTimeStamp >= startDate && x.ClockOutTimeStamp <= endDate).Sum(x.ClockOutTimeStamp.Subtract(x.ClockInTimeStamp).TotalMinutes);
The problem seems easy until you realize that a time sheet can span months. So if someone clocked in on January 31st and clocked out on February 1st, you have to count partial timesheets, to do it right.
Here is my solution:
public static class ExtensionMethods
{
static public double TotalMinutes(this IEnumerable<TimeSheetLog> input, DateTime startPeriod, DateTime endPeriod)
{
return TimeSpan.FromTicks
(
input
.Where( a=>
a.ClockOutTimeStamp >= startPeriod &&
a.ClockInTimeStamp <= endPeriod
)
.Select( a=>
Math.Min(a.ClockOutTimeStamp.Ticks, endPeriod.Ticks) -
Math.Max(a.ClockInTimeStamp.Ticks, startPeriod.Ticks)
)
.Sum()
)
.TotalMinutes;
}
}
Logic:
Find all timesheets that overlap at least partially with the period of interest.
Compute the start time as either the clock in time or the period start time, whichever is later.
Compute the end time as either the clock out time or the period end time, whichever is earlier.
Take the difference of the start and end time as ticks. Sum() these.
To do all this math, we convert all the timestamps to Ticks, since you can't take a Max() of two DateTimes. We can add ticks up just fine, then convert the total back into minutes before returning.
Test program (notice the third timesheet spans both January and February):
public class Program
{
static public List<TimeSheetLog> testData = new List<TimeSheetLog>
{
new TimeSheetLog
{
ClockInTimeStamp = DateTime.Parse("1/1/2018 9:00 am"),
ClockOutTimeStamp = DateTime.Parse("1/1/2018 5:00 pm")
},
new TimeSheetLog
{
ClockInTimeStamp = DateTime.Parse("1/2/2018 9:00 am"),
ClockOutTimeStamp = DateTime.Parse("1/2/2018 5:00 pm")
},
new TimeSheetLog
{
ClockInTimeStamp = DateTime.Parse("1/31/2018 6:00 pm"),
ClockOutTimeStamp = DateTime.Parse("2/1/2018 9:00 am")
},
new TimeSheetLog
{
ClockInTimeStamp = DateTime.Parse("2/3/2018 9:00 am"),
ClockOutTimeStamp = DateTime.Parse("2/3/2018 5:00 pm")
}
};
public static void Main()
{
var startPeriod = new DateTime(2018, 1, 1);
var endPeriod = new DateTime(2018, 1, 31, 23, 59, 59, 9999);
Console.WriteLine( testData.TotalMinutes(startPeriod, endPeriod).ToString("0.00") );
}
}
Output:
1320.00
...which is correct.
See my code on DotNetFiddle
Another option is to use .Aggregate function.
public static int GetTotalTimeWorked(List<TimeSheetLog> logs, DateTime startDate, DateTime endDate)
{
var totalTimeWorkedInMinutes = 0;
return logs.Where(x => x.ClockInTimeStamp >= startDate && x.ClockOutTimeStamp <= endDate)
.Aggregate(totalTimeWorkedInMinutes, (total, item) => total + GetTimeDifferenceInMinutes(item.ClockInTimeStamp, item.ClockOutTimeStamp));
}

Is there an optimized way of getting business weeks between two dates?

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

Categories

Resources