So I'm building a meeting scheduler, in which each potential participant has a set of unavailable dates. For the sake of testing I inserted my own.
The user enters a date range, which is then put into a list of all the dates in-between.
I currently have a list of unavailable dates depending on who the user has invited to the event. This is saved into a list called unavailableDates.
I'm trying to delete all the dates that exist in the datesBetween list that are also in the unavailableDates list, to leave only dates that aren't on a persons unavailable dates.
I've tried a few different things but i cant seem to get it working.
private void compileDates()
{
DateTime startingDate = cal1.SelectionEnd; //Get the starting date from the first calender
DateTime endingDate = cal2.SelectionEnd; //Get the ending date from the first calender
var namesList = lstConfirmedParticipants.Items.Cast<String>().ToList(); //Collects all the participants names and converts it to a list
List<DateTime> PhillChambers = new List<DateTime>(); //Set Pauls Unavailable Dates
PhillChambers.Add(new DateTime(2014, 08, 11));
PhillChambers.Add(new DateTime(2014, 08, 12));
PhillChambers.Add(new DateTime(2014, 08, 17));
List<DateTime> HenryWright = new List<DateTime>(); //Set Pauls Unavailable Dates
HenryWright.Add(new DateTime(2014, 08, 09));
HenryWright.Add(new DateTime(2014, 08, 12));
HenryWright.Add(new DateTime(2014, 08, 14));
List<DateTime> PaulaCooper = new List<DateTime>(); //Set Pauls Unavailable Dates
PaulaCooper.Add(new DateTime(2014, 08, 11));
PaulaCooper.Add(new DateTime(2014, 08, 12));
PaulaCooper.Add(new DateTime(2014, 08, 16));
List<DateTime> unavailableDates = new List<DateTime>(); //Creates a new list to hold all the unavailable dates
if (namesList.Contains("Paula Cooper"))
{ //Add Paulas Unavailable Dates
unavailableDates.AddRange(PaulaCooper);
}
if (namesList.Contains("Henry Wright"))
{ //Add Henrys Unavailable Dates
unavailableDates.AddRange(HenryWright);
}
if (namesList.Contains("Phill Chambers"))
{ //Add Phills Unavailable Dates
unavailableDates.AddRange(PhillChambers);
}
foreach (DateTime date in GetDateRange(startingDate, endingDate))
{
lstDatesBetween.Items.Add(date.ToShortDateString()); //Get all the dates between the date ranges and put them into the listbox.
}
List<DateTime> datesBetween = lstDatesBetween.Items.OfType<DateTime>().ToList(); //Convert the Listbox into a list holding all the dates between the date ranges
datesBetween.RemoveAll(item => unavailableDates.Contains(item)); //remove all the dates in dates between that also appear in unavailable dates
List<DateTime> availableDates = new List<DateTime>(); //Creates a new list to hold all the available dates(FUTURE USE)
availableDates.AddRange (datesBetween);
lstDatesAvailable.DataSource = availableDates; //display the available dates for the meeting
private List<DateTime> GetDateRange(DateTime StartingDate, DateTime EndingDate)
{
if (StartingDate > EndingDate)
{
return null;
}
List<DateTime> datesBetween = new List<DateTime>();
DateTime tempDate = StartingDate;
do
{
datesBetween.Add(tempDate);
tempDate = tempDate.AddDays(1);
} while (tempDate <= EndingDate);
return datesBetween;
}
It seems that lstDatesBetween is populated with strings:
lstDatesBetween.Items.Add(date.ToShortDateString());
and then datesBetween takes elements of type DateTime:
List<DateTime> datesBetween = lstDatesBetween.Items.OfType<DateTime>().ToList();
So I guess datesBetween is empty.
Try if this works:
List<DateTime> datesBetween = lstDatesBetween.Items
.OfType<string>()
.Select(s => Convert.ToDateTime(s))
.ToList();
Related
I hope get week datetime list by datetime range,I tried the following code get number of weeks per month.
var start = new DateTime(2021, 6, 09);
var end = new DateTime(2021, 7, 01);
end = new DateTime(end.Year, end.Month, DateTime.DaysInMonth(end.Year, end.Month));
var diff = Enumerable.Range(0, Int32.MaxValue)
.Select(e => start.AddMonths(e))
.TakeWhile(e => e <= end)
.Select(e => Convert.ToDateTime(e.ToString("yyyy-MM")));
foreach (var item in diff)
{
DateTime dateTime = item;
Calendar calendar = CultureInfo.CurrentCulture.Calendar;
IEnumerable<int> daysInMonth = Enumerable.Range(1, calendar.GetDaysInMonth(dateTime.Year, dateTime.Month));
List<Tuple<DateTime, DateTime>> weeks = daysInMonth.Select(day => new DateTime(dateTime.Year, dateTime.Month, day))
.GroupBy(d => calendar.GetWeekOfYear(d, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday))
.Select(g => new Tuple<DateTime, DateTime>(g.First(), g.Last()))
.ToList();
}
Executing the above code I got the following result,get all the weeks of each month。
2021-06-01 2021-06-06
......
2021-07-26 2021-07-31
I want to count the week from my start date 2021-06-09 to the end date 2021-07-01, like this.
2021-06-09 2021-06-13
2021-06-14 2021-06-20
2021-06-21 2021-06-27
2021-06-28 2021-07-01
how to changed my code
This will produce the desired output:
var start = new DateTime(2021, 6, 09);
var end = new DateTime(2021, 7, 01);
// If you want to get the first week complete, add this line:
//DateTime currDate = start.AddDays(1-(int)start.DayOfWeek);
// Otherwise, use this one
DateTime currDate = start;
while(currDate <= end){
var dayOfWeek = (int)currDate.DayOfWeek;
var endOfWeek = currDate.AddDays(7 - dayOfWeek);
//If you want the complete last week, omit this if
if(endOfWeek > end)
{
endOfWeek = end;
}
Console.WriteLine($"{currDate:yyyy-MM-dd} {endOfWeek:yyyy-MM-dd}");
currDate = endOfWeek.AddDays(1);
}
output:
2021-06-09 2021-06-13
2021-06-14 2021-06-20
2021-06-21 2021-06-27
2021-06-28 2021-07-01
I want to find the date range which falls in input date, following is structure
public class Duration
{
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
var durations = new List<Duration>();
var duration1 = new Duration()
{
StartDate = new DateTime(2017, 08, 1),
EndDate = new DateTime(2017, 08, 10)
};
durations.Add(duration1);
var duration2 = new Duration()
{
StartDate = new DateTime(2017, 08, 5),
EndDate = new DateTime(2017, 08, 10)
};
durations.Add(duration2);
var duration3 = new Duration()
{
StartDate = new DateTime(2017, 08, 5),
EndDate = new DateTime(2017, 08, 6)
};
durations.Add(duration3);
Now I want to find duration which is closest to the entered date for list of <Durations> with LINQ or for-loop
My expected result for currentDate=new DateTime(2017, 08, 7); is duration2
You first need to check if the currentDate is within the start and end dates of each range. For the ones that meet that condition, you calculate the "closeness" adding both distances. When you find one lapse(gap) smaller tan the previous, you save its index... and voilá
int lapse = Integer.MaxValue;
int counter = 0;
int index = 0;
foreach (d in durations) {
if (((d.StartDate <= currentDate) && (d.EndDate >= currentDate))) {
int newlapse = ((currentDate - d.StartDate).TotalDays + (d.EndDate - currentDate).TotalDays);
if ((newlapse < lapse)) {
lapse = newlapse;
index = counter;
}
}
counter +=1;
}
return durations(index);
If you need the middle of interval to be closest:
durations.OrderBy((d) => Math.Abs(d.EndDate.Ticks + d.StartDate.Ticks) / 2 - currentDate.Ticks).FirstOrDefault();
If you need the start of interval to be closest:
durations.OrderBy((d) => Math.Abs(d.EndDate.Ticks - currentDate.Ticks)).FirstOrDefault();
As D le mentioned above
First check if currentDate is within the start and end dates
Second select the duration with the minimal difference between start end end date
I used a nuget package called morelinq which gives nice extensions methods like MinBy:
var result = (from d in durations
where (d.StartDate <= currentDate && d.EndDate >= currentDate)
select d).MinBy(d => d.EndDate - d.StartDate);
I would like to know the best way to set a range of dates. I have logic that checks what day a certain record is approved on and based on that day i set a date the next time that record needs to be re-approved
so if the record is approved in january or february it should be re evaluated in march if its approved in march it should be re evaluated in june.
i declare my ranges like this for now, but this i believe is not the best way to do it
DateTime quarterOneStart = new DateTime(DateTime.Now.Year,07,01);
DateTime quarterOneEnd = new DateTime(DateTime.Now.Year, 09, 15));
DateTime quarterTwoStart = new DateTime(DateTime.Now.Year, 10, 01);
DateTime quarterTwoEnd = new DateTime(DateTime.Now.Year, 12, 15));
DateTime quarterThreeStart = new DateTime(DateTime.Now.Year, 01, 01);
DateTime quarterThreeEnd = new DateTime(DateTime.Now.Year, 03, 15));
DateTime quarterFourStart = new DateTime(DateTime.Now.Year, 04, 01);
DateTime quarterFourEnd = new DateTime(DateTime.Now.Year, 06, 15));
is there a better way to set the date time variables above?
What about class for quarter
public class Quarter {
private readonly DateTime _startDate;
private readonly DateTime _endDate;
public Quarter(DateTime startDate, DateTime endDate) {
_startDate = startDate;
_endDate = endDate;
}
public DateTime StartDate => _startDate;
public DateTime EndDate => _endDate;
}
and use it
Quarter one = new Quarter(new DateTime(2017, 07, 01), new DateTime(2017, 09, 15));
Quarter two = new Quarter(new DateTime(2017, 09, 15), new DateTime(2017, 10, 01));
...
Rather than creating multiple variables, you can create a Dictionary of items key'ed by an enum. For example:
public enum Quaters
{
Q1_Start,
Q1_End,
Q2_Start,
Q2_End,
Q3_Start,
Q3_End,
Q4_Start,
Q4_End
}
Dictionary<Quaters, DateTime> dateRange = new Dictionary<Quaters, DateTime>
{
{Quaters.Q1_Start, new DateTime(DateTime.Now.Year, 07, 01)},
{Quaters.Q1_End, new DateTime(DateTime.Now.Year, 09, 15)},
{Quaters.Q2_Start, new DateTime(DateTime.Now.Year, 10, 01)},
{Quaters.Q2_End, new DateTime(DateTime.Now.Year, 12, 15)},
...
};
When you need to verify the value against any range parameter you can index the dateRange dictionary based on keys. It is just a proposal and there could be better solutions to your actual problem.
In the past I've written a FinancialYear class that internally holds a startYear (int) variable and offers various methods/properties, e.g. (among others)
public DateTime StartDate
{
get { return new DateTime(_startYear, 4, 1); } // April 1st
}
public static FinancialYear ForDate(DateTime dt)
{
DateTime finYearStart = new DateTime(dt.Year, 4, 1);
return (dt >= finYearStart) ? new FinancialYear(dt.Year) : new FinancialYear(dt.Year - 1);
}
I have a c# list containing periods using cDates class.
public class cDates
{
public Date PeriodStart {get;set;}
public Date PeriodEnd {get;set;}
}
2014/01/01 - 2014/04/30
2014/05/01 - 2014/07/31
2014/08/01 - 2014/09/30
Is it possible to get a list of dates containing all dates in existing periods, something like List.
public class cSingleDate
{
public Date SingleDate {get;set;}
}
2014/01/01
2014/01/02
2014/01/03
...
2014/09/30
I tried to find a solution using loops, but want to find out is there a cleaner solution. Maybe a Linq solution?
You can use LINQ for that:
[TestMethod]
public void TestPeriods()
{
var periods = new[]
{
new Period(new DateTime(2014, 01, 01), new DateTime(2014, 04, 30)),
new Period(new DateTime(2014, 05, 01), new DateTime(2014, 07, 31)),
new Period(new DateTime(2014, 08, 01), new DateTime(2014, 09, 30)),
};
var days = from period in periods
let numberOfDays = (period.LastDay - period.FirstDay).Days + 1
from day in Enumerable.Range(0, numberOfDays)
select period.FirstDay.AddDays(day);
var distinctDays = days.Distinct().ToArray();
distinctDays.Should().Contain(new DateTime(2014, 01, 01));
distinctDays.Should().Contain(new DateTime(2014, 02, 01));
distinctDays.Should().Contain(new DateTime(2014, 04, 30));
distinctDays.Should().NotContain(new DateTime(2014, 10, 01));
}
public class Period
{
public Period(DateTime firstDay, DateTime lastDay)
{
this.FirstDay = firstDay;
this.LastDay = lastDay;
}
public DateTime FirstDay {get;set;}
public DateTime LastDay {get;set;}
}
You can just loop through dates, and add one day to PeriodStart as long as it's less than or equal to PeriodEnd:
var dates = new List<cSingleDate>();
foreach(var date in cDates)
{
while(date.PeriodStart <= date.PeriodEnd)
{
dates.Add(new cSingleDate { SingleDate = date.PeriodStart });
date.PeriodStart = date.PeriodStart.AddDays(1);
}
}
If you don't wanna change cDates just use a temporary variable to store PeriodStart.
There are two easy ways to transform the dates from the Period class into a single list of dates. You can either extend the Period class to give you the dates and use linq to flatten it or you read both properties with linq. If the SingleDate class really only stores the date and nothing else and if it does not provide any additional functionality you may consider to just extract the dates.
// Test data.
var periods = new[]
{
new Period(new DateTime(2014, 01, 01), new DateTime(2014, 04, 30)),
new Period(new DateTime(2014, 05, 01), new DateTime(2014, 07, 31)),
new Period(new DateTime(2014, 08, 01), new DateTime(2014, 09, 30)),
};
If you can make the Period class to implement IEnumerable<DateTime> interface and thus flatten the dates for you then a simple SelectMany can be used:
var result1 =
periods
// The Period class will enumerate the dates for SelectMany:
.SelectMany(x => x)
// Uncomment this line if you want to encapsulate the date.
//.Select(x => new SingleDate() { Date = x })
.ToList();
Otherwise you need to flatten the Period dates inside the SelectMany:
var resul2 =
periods
// Here SelectMany has to enumerate the dates:
.SelectMany(x => new[]
{
x.PeriodStart,
x.PeriodEnd
})
// Uncomment this line if you want to encapsulate the date.
//.Select(x => new SingleDate() { Date = x })
.ToList();
Period-class that implements the IEnumerable<DateTime> interface to enumerate the dates:
public class Period : IEnumerable<DateTime>
{
public Period(DateTime firstDay, DateTime lastDay)
{
this.PeriodStart = firstDay;
this.PeriodEnd = lastDay;
}
public DateTime PeriodStart { get; set; }
public DateTime PeriodEnd { get; set; }
public IEnumerator<DateTime> GetEnumerator()
{
yield return PeriodStart;
yield return PeriodEnd;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
SingleDate-class without changes (you don't need it if it does not provide any additional functionality):
public class SingleDate
{
public DateTime Date { get; set; }
}
I am stuck for sometime now, now need your help.
I want to display in a dropdown only fourth Sunday of each month, say from 1-Sep-2010 to 31-Aug-2011
I only want fourth Sunday in dropdown list, how to do it using asp.net C#
Regards
Here is an approach that uses a little LINQ and the knowledge that the fourth Sunday will occur between the 22nd and 28th of a month, inclusive.
DateTime startDate = new DateTime(2010, 9, 1);
DateTime endDate = startDate.AddYears(1).AddDays(-1);
List<DateTime> fourthSundays = new List<DateTime>();
DateTime currentDate = startDate;
while (currentDate < endDate)
{
// we know the fourth sunday will be the 22-28
DateTime fourthSunday = Enumerable.Range(22, 7).Select(day => new DateTime(currentDate.Year, currentDate.Month, day)).Single(date => date.DayOfWeek == DayOfWeek.Sunday);
fourthSundays.Add(fourthSunday);
currentDate = currentDate.AddMonths(1);
}
You can then bind that List<DateTime> to the dropdown or skip the list itself in favor of adding the items as you generate them to the dropdown, like below.
yourDropdown.Items.Add(new ListItem(fourthSunday.ToString()));
For giggles, you can do the whole thing in a LINQ statement and skip (most of) the variables.
DateTime startDate = new DateTime(2010, 9, 1);
IEnumerable<DateTime> fourthSundays =
Enumerable.Range(0, 12)
.Select(item => startDate.AddMonths(item))
.Select(currentMonth =>
Enumerable.Range(22, 7)
.Select(day => new DateTime(currentMonth.Year, currentMonth.Month, day))
.Single(date => date.DayOfWeek == DayOfWeek.Sunday)
);
Got bored so here you go. Two helper methods one retrieves the Week if it exist, and the other iterates through the months
class Program
{
static void Main(string[] args)
{
DateTime startDate = new DateTime(2010, 09, 1);
foreach(DateTime dt in EachMonth( new DateTime(2010, 09, 1), new DateTime(2011, 09, 1))){
DateTime? result = GetDayByWeekOffset(DayOfWeek.Sunday, dt, 4);
Console.WriteLine("Sunday:" + (result.HasValue?result.Value.ToString("MM-dd-yyyy"):"null"));
}
Console.ReadKey();
}
public static DateTime? GetDayByWeekOffset(DayOfWeek day, DateTime month, int weekOffSet)
{
//First day of month
DateTime firstDayOfMonth = month.AddDays((-1 * month.Day) + 1);
//
int daysOffSet;
daysOffSet= ((int)day + 7 - (int)firstDayOfMonth.DayOfWeek) % 7;
DateTime firstDay = month.AddDays(daysOffSet);
// Add the number of weeks specified
DateTime resultDate = firstDay.AddDays((weekOffSet - 1) * 7);
if (resultDate.Month != firstDayOfMonth.Month){
return null;
}else{
return resultDate;
}
}
public static IEnumerable<DateTime> EachMonth(DateTime from, DateTime thru)
{
for (var month = from.Date; month.Date <= thru.Date; month = month.AddMonths(1))
yield return month;
}
}
Anthony's answer above is nice, I like it a lot. As an alternate, here is a method which is parameterized for the day of the week and the week number (i.e. if you need other combinations, like 4th Sunday, 3rd Friday, etc.) with some comments.
Call it like this for your case:
List<DateTime> sundays = DateInstances(new DateTime(2010, 9, 1), new DateTime(2011, 8, 31), DayOfWeek.Sunday, 4);
And the method itself:
public List<DateTime> DateInstances(DateTime start, DateTime end, DayOfWeek day, int weeks)
{
if (start > end)
throw new ArgumentOutOfRangeException("end", "The start date must occur before the end date");
List<DateTime> results = new List<DateTime>();
DateTime temp = start;
while (temp < end)
{
DateTime firstWeekday = new DateTime(temp.Year, temp.Month, 1);
//increment to the given day (i.e. if we want the 4th sunday, we must find the first sunday of the month)
while (firstWeekday.DayOfWeek != day)
firstWeekday = firstWeekday.AddDays(1);
//add the number of weeks (note: we already have the first instance, so subtract 1)
firstWeekday = firstWeekday.AddDays(7 * (weeks - 1));
//make sure we haven't gone over to the next month
if (firstWeekday.Month == temp.Month)
results.Add(firstWeekday);
//let's not loop forever ;)
temp = temp.AddMonths(1);
}
return results;
}