I have this while loop to get next working day excluding holidays and sundays.
But it calculates by adding 1 day. And i want that number of day to be given by the user. I get that input from the below TextBox (TboxIzin).
How can execute that while loop to do the calculation for given number of times?
int i = 1;
int sayi;
IzinIslem i1 = new IzinIslem();
int.TryParse(i1.TboxIzin.Text, out sayi);
public static DateTime GetNextWeekDay(DateTime date,
IList<Holiday> holidays, IList<DayOfWeek> weekendDays)
{
int i = 1;
int sayi;
IzinIslem i1 = new IzinIslem();
int.TryParse(i1.TboxIzin.Text, out sayi);
// always start with tomorrow, and truncate time to be safe
date = date.Date.AddDays(1);
// calculate holidays for both this year and next year
var holidayDates = holidays.Select(x => x.GetDate(date.Year))
.Union(holidays.Select(x => x.GetDate(date.Year + 1)))
.Where(x => x != null)
.Select(x => x.Value)
.OrderBy(x => x).ToArray();
// increment to get working day
while (true)
{
if (weekendDays.Contains(date.DayOfWeek) ||
holidayDates.Contains(date))
date = date.AddDays(1);
else
return date;
}
}
I get not all code paths return a value
error when i try nesting while loops.
while is a conditional loop. Here you put a non-condition in the clause and immediately follow up with a condition. Put the condition in the while clause:
while(weekendDays.Contains(date.DayOfWeek) || holidayDates.Contains(date)) {
date = date.AddDays(1);
}
return date;
The actual reason you're getting the error is that the compiler cannot predict if your if condition will ever resolve to false. If it doesn't, then your function never returns.
With the modified while loop, that may still happen, but it will result in an infinite loop, and the compiler is fine if you shoot yourself in the foot that way.
You can change your else clause to break out of the loop. And then return out of the loop.
while (true)
{
if (weekendDays.Contains(date.DayOfWeek) ||
holidayDates.Contains(date))
date = date.AddDays(1);
else
break;
}
return date;
Let's get rif of all UI in the GetNextWeekDay (like int.TryParse(i1.TboxIzin.Text, out sayi);):
public static DateTime GetNextWeekDay(DateTime date,
IEnumerable<Holiday> holidays,
IEnumerable<DayOfWeek> weekendDays) {
// public method arguments validation
if (null == holidays)
throw new ArgumentNullException(nameof(holidays));
else if (null == weekendDays)
throw new ArgumentNullException(nameof(weekendDays));
HashSet<DayOfWeek> wends = new HashSet<DayOfWeek>(weekendDays);
// Current Year and Next years - .AddYear(1)
HashSet<DateTime> hds = new HashSet<DateTime>(holidays
.Select(item => item.Date)
.Concate(holidays.Select(item => item.Date.AddYear(1))));
for (var day = date.Date.AddDays(1); ; day = day.AddDays(1))
if (!wends.Contains(day.DayOfWeek) && ! hds.Contains(day))
return day;
}
Or if you prefer Linq, the loop can be rewritten as
return Enumerable
.Range(1, 1000)
.Select(day => date.Date.AddDays(day))
.TakeWhile(item => item.Year - date.Year <= 1)
.First(item => !wends.Contains(item.DayOfWeek) && !hds.Contains(item));
Related
I have database values called start and length.
start is the start time of a booking (1->09:00, 2->10:00 etc) and length is the length in hours.
i then have an array of start times and end times. I want to be able to check whether each start and end pair are already booked. I so far have it figured that if the start times are the same, it is booked, or if the end times are the same, it is also booked. But if the start and end time are inbetween the comparison times, it will return not booked, which is false.
I am trying to write a LINQ query to test whether a booking is already in the database. So far I have
var proposedRequest = db.requests.Include(r => r.rooms);
proposedRequest = proposedRequest.Where(r => r.booked.Equals(1));
proposedRequest = proposedRequest.Where(r => r.roundID.Equals(roundID));
proposedRequest = proposedRequest.Where(r => r.day.Equals(day));
int[] startTimes = new int[length];
int[] endTimes = new int[length];
for(var q=0;q<length;q++)
{
startTimes[q] = time + q;
endTimes[q] = time + q + 1;
}
proposedRequest = proposedRequest.Where(s => startTimes.Contains(s.start) || endTimes.Contains(s.start+s.length));
Now, this only works for if the new booking starts at the same time as the booking already in the DB, or if it ends at the same time. This doesn't look at the following case
there is a records in the db where start -> 2 and length ->3.
so this booking runs from 10:00->13:00.
but say I am checking this against an entry that starts at 11:00 and ends at 12. It would not come back as booked already because the start and end times do not match.
What is the best way to solve this?
the only way i could see fit is to loop through my startTime and endTime arrays and have another clause for each pair that would produce something like the following:
.Where((s => s.startTime<startTime[i] && (s.startTime + s.Length) > endTime[i]) || (s => s.startTime<startTime[i+1] && (s.startTime + s.Length) > endTime[i+1]))
but i dont think this is possible.
Based on this answer, two ranges overlap if (StartA <= EndB) and (EndA >= StartB)
In your case:
StartA = s.start
EndA = s.start + s.length
StartB = time
EndB = time + length
So your last condition should be like this:
proposedRequest = proposedRequest.Where(s => s.start <= time + length &&
s.start + s.length >= time);
This will return objects that have your StartTime, EndTime, and a boolean that signifies if it booked already.
var proposedRequest = db.requests
.Include(r => r.rooms)
.Where(r => r.booked.Equals(1))
.Where(r => r.roundID.Equals(roundID))
.Where(r => r.day.Equals(day))
.ToList();
//int[] startTimes = new int[length];
//int[] endTimes = new int[length];
//for(var q=0;q<length;q++)
//{
// startTimes[q] = time + q;
// endTimes[q] = time + q + 1;
//}
var times=Enumerable
.Range(time,length)
.Select(r=>
new {
StartTime=r,
EndTime=r+1,
Booked=proposedRequest.Any(pr=>pr.StartTime<=r && pr.StartTime+pr.Length>r)
}).ToList();
I have an initial and a final date range = 1/1/2015 - 1/30/2015
I have these date ranges that represent dates of unavailability.
1/5/2015 - 1/10/2015
1/15/2015 - 1/20/2015
1/22/2015 - 1/28/2015
I want this output, mainly the dates of availability from the main range:
A: 1/1/2015 - 1/4/2015
B: 1/11/2015 - 1/14/2015
C: 1/21/2015 - 1/21/2015
D: 1/29/2015 - 1/30/2015
I tried to generate a sequential date range like this in order to get the exception dates with Except() but I think I'm complicating the thing.
//dtStartDate = 1/1/2015
//dtEndDate = 1/30/2015
var days = (int)(dtEndDate - dtStartDate).TotalDays + 1;
var completeSeq = Enumerable.Range(0, days).Select(x => dtStartDate.AddDays(x)).ToArray();
How can I get the gap of date ranges from period of time.
I other words how can I get the A, B, C and D from this picture
http://www.tiikoni.com/tis/view/?id=ebe851c
If these dates overlap, they must not be considered only where is a gap.
----------UPDATE-----------
I think if I do this:
var range = Enumerable.Range(0, (int)(1/10/2015 - 1/5/2015).TotalDays + 1).Select(i => 1/5/2015.AddDays(i));
var missing = completeSeq.Except(range).ToArray();
for each date range I will have the exclusion of each date range given but still cannot get the gap!
I saw your question in my morning today and really liked it, but was busy the whole day. So, got a chance to play with your question and believe me I enjoyed it. Here is my code:-
DateTime startDate = new DateTime(2015, 1, 1);
DateTime endDate = new DateTime(2015, 1, 30);
int totalDays = (int)(endDate - startDate).TotalDays + 1;
availability.Add(new Availability { StartDate = endDate, EndDate = endDate });
var result = from x in Enumerable.Range(0, totalDays)
let d = startDate.AddDays(x)
from a in availability.Select((v, i) => new { Value = v, Index = i })
where (a.Index == availability.Count - 1 ?
d <= a.Value.StartDate : d < a.Value.StartDate)
&& (a.Index != 0 ? d > availability[a.Index - 1].EndDate : true)
group new { d, a } by a.Value.StartDate into g
select new
{
AvailableDates = String.Format("{0} - {1}",g.Min(x => x.d),
g.Max(x => x.d))
};
This, definitely need explanation so here it is:-
Step 1: Create a range of dates from Jan 01 till Jan 30 using Enumerable.Range
Step 2: Since after the second unavailable date range, we need to limit the dates selected from last endate till current object startdate, I have calculated index so that we can get access to the last enddate.
Step 3: Once we get the index, all we need to do is filter the dates except for first date range since we didn't have last object in this case.
Step 4: For the last item since we don't have the max range I am adding the endDate to our unavailable list (hope this makes sense).
Here is the Working Fiddle, if you get confused just remove group by and other filters and debug and see the resulting output it will look fairly easy :)
using System;
using System.Collections.Generic;
using System.Linq;
public static class Program {
public static void Main() {
Tuple<DateTime,DateTime> range=Tuple.Create(new DateTime(2015,1,1),new DateTime(2015,1,30));
Tuple<DateTime,DateTime>[] exclude=new[] {
Tuple.Create(new DateTime(2015,1,5),new DateTime(2015,1,10)),
Tuple.Create(new DateTime(2015,1,15),new DateTime(2015,1,20)),
Tuple.Create(new DateTime(2015,1,22),new DateTime(2015,1,28))
};
foreach(Tuple<DateTime,DateTime> r in ExcludeIntervals(range,exclude)) {
Console.WriteLine("{0} - {1}",r.Item1,r.Item2);
}
}
public static IEnumerable<Tuple<DateTime,DateTime>> ExcludeIntervals(Tuple<DateTime,DateTime> range,IEnumerable<Tuple<DateTime,DateTime>> exclude) {
IEnumerable<Tuple<DateTime,bool>> dates=
new[] { Tuple.Create(range.Item1.AddDays(-1),true),Tuple.Create(range.Item2.AddDays(1),false) }.
Concat(exclude.SelectMany(r => new[] { Tuple.Create(r.Item1,false),Tuple.Create(r.Item2,true) })).
OrderBy(d => d.Item1).ThenBy(d => d.Item2); //Get ordered list of time points where availability can change.
DateTime firstFreeDate=default(DateTime);
int count=1; //Count of unavailability intervals what is currently active. Start from 1 to threat as unavailable before range starts.
foreach(Tuple<DateTime,bool> date in dates) {
if(date.Item2) { //false - start of unavailability interval. true - end of unavailability interval.
if(--count==0) { //Become available.
firstFreeDate=date.Item1.AddDays(1);
}
} else {
if(++count==1) { //Become unavailable.
DateTime lastFreeDate=date.Item1.AddDays(-1);
if(lastFreeDate>=firstFreeDate) { //If next unavailability starts right after previous ended, then no gap.
yield return Tuple.Create(firstFreeDate,lastFreeDate);
}
}
}
}
}
}
ideone.com
Got a little oopy...
public class DateRange
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
public bool HasStart
{
get { return Start != DateTime.MinValue; }
}
public bool IsInRange(DateTime date)
{
return (date >= this.Start && date <= this.End);
}
public List<DateRange> GetAvailableDates(DateRange excludedRange)
{
return GetAvailableDates(new List<DateRange>(){excludedRange});
}
public List<DateRange> GetAvailableDates(List<DateRange> excludedRanges)
{
if (excludedRanges == null)
{
return new List<DateRange>() { this };
}
var list = new List<DateRange>();
var aRange = new DateRange();
var date = this.Start;
while (date <= this.End)
{
bool isInARange = excludedRanges.Any(er => er.HasStart && er.IsInRange(date));
if (!isInARange)
{
if (!aRange.HasStart)
{
aRange.Start = date;
}
aRange.End = date;
}
else
{
if (aRange.HasStart)
{
list.Add(aRange);
aRange = new DateRange();
}
}
date = date.AddDays(1);
}
if (aRange.HasStart)
{
list.Add(aRange);
}
return list;
}
}
I have an entity collection of Readings.
Each Reading is linked to an entity called Meter.
(And each Meter holds multiple readings).
each Reading holds a field for meter id (int) and a field for time.
Here is some simplified code to demonstrate it:
public class Reading
{
int Id;
int meterId;
DateTime time;
}
public class Meter
{
int id;
ICollection<Readings> readings;
}
Given a specific period and list of meterids,
what would be the most efficient way to get for each Meter
the first and last reading in that time period?
I am able to iterate through all meters and for each meter to obatin
first and last reading for the period,
but I was wandering if there is a more efficient way to acheive this.
And a bonus question: same question, but with multiple periods of time to get data for,
instead of just one period.
I am not exactly sure how you want this data, but you could project it into an anonymous type:
var metersFirstAndLastReading = meters.Select(m => new
{
Meter = m,
FirstReading = m.readings.OrderBy(r => r.time).First(),
LastReading = m.readings.OrderBy(r => r.time).Last()
});
You can then read your result list like this (this example is just meant as an illustration):
foreach(var currentReading in metersFirstAndLastReading)
{
string printReadings = String.Format("Meter id {0}, First = {1}, Last = {2}",
currentReading.Meter.id.ToString(),
currentReading.FirstReading.time.ToString(),
currentReading.LastReading.time.ToString());
// Do something...
}
Another option would be to create properties in Meter which dynamically return the first and last readings:
public class Meter
{
public int id;
public List<Reading> readings;
public Reading FirstReading
{
get
{
return readings.OrderBy(r => r.time).First();
}
}
public Reading LastReading
{
get
{
return readings.OrderBy(r => r.time).Last();
}
}
}
EDIT: I misunderstood the question a little.
Here is the implementation to determine the first and last readings for a meter including a date range (assuming meterIdList is an ICollection<int> of IDs and begin and end is the specified date range)
var metersFirstAndLastReading = meters
.Where(m => meterIdList.Contains(m.id))
.Select(m => new
{
Meter = m,
FirstReading = m.readings
.Where(r => r.time >= begin && r.time <= end)
.OrderBy(r => r.time)
.FirstOrDefault(),
LastReading = m.readings
.Where(r => r.time >= begin && r.time <= end)
.OrderByDescending(r => r.time)
.FirstOrDefault()
});
You won't be able to use properties now (as you need to supply parameters) so methods will work just fine as an alternative:
public class Meter
{
public int id;
public List<Reading> readings;
public Reading GetFirstReading(DateTime begin, DateTime end)
{
var filteredReadings = readings.Where(r => r.time >= begin && r.time <= end);
if(!HasReadings(begin, end))
{
throw new ArgumentOutOfRangeException("No readings available during this period");
}
return filteredReadings.OrderBy(r => r.time).First();
}
public Reading GetLastReading(DateTime begin, DateTime end)
{
var filteredReadings = readings.Where(r => r.time >= begin && r.time <= end);
if(!HasReadings(begin, end))
{
throw new ArgumentOutOfRangeException("No readings available during this period");
}
return filteredReadings.OrderBy(r => r.time).Last();
}
public bool HasReadings(DateTime begin, DateTime end)
{
return readings.Any(r => r.time >= begin && r.time <= end);
}
}
I have a very similar data model where this code is used to get the oldest readings, i just changed it to also include the newest.
I use query syntax to do something like this:
var query = from reading in db.Readings
group reading by reading.meterId
into readingsPerMeter
let oldestReadingPerMeter = readingsPerMeter.Min(g => g.time)
let newestReadingPerMeter = readingsPerMeter.Max(g => g.time)
from reading in readingsPerMeter
where reading.time == oldestReadingPerMeter || reading.time == newestReadingPerMeter
select reading; //returns IQueryable<Reading>
That would result in a only the newest and oldest reading for each meter.
The reason i think this is efficient is because its one lookup to the DB to get all the readings for each meter, instead of several lookups for each meter. We have ~40000 meters with ~30mil readings. i just tested the lookup on our data it took about 10s
The sql preformed is a crossjoin between two sub selects for each of the min and max dates.
UPDATE:
Since this is queryable you should be able to supply a period after, like this:
query.Where(r=>r.time > someTime1 && r.time < someTime2)
Or put it into the original query, i just like it seperated like this. The query isnt executed yet since we havent performed an action that fetches the data yet.
Create a new class as the return type called Result, which looks like this
public class Result
{
public int MeterId;
public Readings Start;
public Readings Last;
}
I emulated your situation by making a list of Meters and populating some data, your query should be pretty much the same though
var reads = Meters.Where(x => x.readings != null)
.Select(x => new Result
{
MeterId = x.id,
Start = x.readings.Select(readings => readings).OrderBy(readings=>readings.time).FirstOrDefault(),
Last = x.readings.Select(readings=>readings).OrderByDescending(readings=>readings.time).FirstOrDefault()
});
public IEnumerable<Reading> GetFirstAndLastInPeriod
(IEnumerable<Reading> readings, DateTime begin, DateTime end)
{
return
from reading in readings
let span = readings.Where(item => item.time >= begin && item.time <= end)
where reading.time == span.Max(item => item.time)
|| reading.time == span.Min(item => item.time)
select reading;
}
meters.Where(mt=>desiredMeters.Contains(mt)).Select(mt=>
new{
mt.Id,
First = mt.Readings.Where(<is in period>).OrderBy(rd=>rd.Time).FirstOrDefault(),
Last = mt.Readings.Where(<is in period>).OrderBy(rd=>rd.Time).LastOrDefault()
});
If you have lots of readings per meter, this will not perform well, and you should consider Readings to be of SortedList class.
my solution will return exact what u want (List of all Meters containing Readings within given Time Period)
public IList<Reading[]> GetFirstAndLastReadings(List<Meter> meterList, DateTime start, DateTime end)
{
IList<Reading[]> fAndlReadingsList = new List<Reading[]>();
meterList.ForEach(x => x.readings.ForEach(y =>
{
var readingList = new List<Reading>();
if (y.time >= startTime && y.time <= endTime)
{
readingList.Add(y);
fAndlReadingsList.Add(new Reading[] { readingList.OrderBy(reading => reading.time).First(), readingList.OrderBy(reading => reading.time).Last() });
}
}));
return fAndlReadingsList;
}
I got some very nice leads, thank to all the responders.
Here is the solution that worked for me:
/// <summary>
/// Fills the result data with meter readings matching the filters.
/// only take first and last reading for each meter in period.
/// </summary>
/// <param name="intervals">time intervals</param>
/// <param name="meterIds">list of meter ids.</param>
/// <param name="result">foreach meter id , a list of relevant meter readings</param>
private void AddFirstLastReadings(List<RangeFilter<DateTime>> intervals, List<int> meterIds, Dictionary<int, List<MeterReading>> result)
{
foreach (RangeFilter<DateTime> interval in intervals)
{
var metersFirstAndLastReading = m_context.Meter.Where(m => meterIds.Contains(m.Id)).Select(m => new
{
MeterId = m.Id,
FirstReading = m.MeterReading
.Where(r => r.TimeStampLocal >= interval.FromVal && r.TimeStampLocal < interval.ToVal)
.OrderBy(r => r.TimeStampLocal)
.FirstOrDefault(),
LastReading = m.MeterReading
.Where(r => r.TimeStampLocal >= interval.FromVal && r.TimeStampLocal < interval.ToVal)
.OrderByDescending(r => r.TimeStampLocal)
.FirstOrDefault()
});
foreach (var firstLast in metersFirstAndLastReading)
{
MeterReading firstReading = firstLast.FirstReading;
MeterReading lastReading = firstLast.LastReading;
if (firstReading != null)
{
result[firstLast.MeterId].Add(firstReading);
}
if (lastReading != null && lastReading != firstReading)
{
result[firstLast.MeterId].Add(lastReading);
}
}
}
}
}
I have a list of dates that are apart by a month in the sense that all dates are the "First Monday of the month". In some cases months are missing so I need to write a function to determine if all dates are consecutive
So for example if this was the list of dates, the function would return true as all items are the "First Friday of the month" and there are no gaps. This example below would return true.
var date = new DateTime(2013, 1, 4);
var date1 = new DateTime(2013, 2, 1);
var date2 = new DateTime(2013, 3, 1);
var date3 = new DateTime(2013, 4, 5);
var dateArray = new DateTime[]{date, date1, date2, date3};
bool isConsecutive = IsThisListConsecutive(dateArray);
where this example below would return false because, even though they are also all "First Friday of the month", its missing the March 2013 item.
var date = new DateTime(2013, 1, 4);
var date1 = new DateTime(2013, 2, 1);
var date3 = new DateTime(2013, 4, 5);
var dateArray = new DateTime[]{date, date1, date3};
bool isConsecutive = IsThisListConsecutive(dateArray);
so i am trying to figure out the right logic for the IsThisListConsecutive() method:
Here was my first try: (Note I already know upfront that all dates are same day of week and same week of month so the only thing i am looking for is a missing slot)
private bool IsThisListConsecutive(IEnumerable<DateTime> orderedSlots)
{
DateTime firstDate = orderedSlots.First();
int count = 0;
foreach (var slot in orderedSlots)
{
if (slot.Month != firstDate.AddMonths(count).Month)
{
return false;
}
count++;
}
return true;
}
This code above works exept if the list crosses over from one year to another. I wanted to get any advice on a better way to create this function and how that line could be rewritten to deal with dates that cross over years.
So to implement this we'll start with a simple helper method that takes a sequence and returns a sequence of pairs that make up each item with it's previous item.
public static IEnumerable<Tuple<T, T>> Pair<T>(this IEnumerable<T> source)
{
T previous;
using (var iterator = source.GetEnumerator())
{
if (iterator.MoveNext())
previous = iterator.Current;
else
yield break;
while(iterator.MoveNext())
{
yield return Tuple.Create(previous, iterator.Current);
previous = iterator.Current;
}
}
}
We'll also use this simple method to determine if two dates are in the same month:
public static bool AreSameMonth(DateTime first, DateTime second)
{
return first.Year == second.Year
&& first.Month == second.Month;
}
Using that, we can easily grab the month of each date and see if it's the month after the previous month. If it's true for all of the pairs, then we have consecutive months.
private static bool IsThisListConsecutive(IEnumerable<DateTime> orderedSlots)
{
return orderedSlots.Pair()
.All(pair => AreSameMonth(pair.Item1.AddMonths(1), pair.Item2));
}
Note: This is completely untested, and the date checks are probably pretty bad or somewhat redundant, but that’s the best I could come up with right now ^^
public bool AreSameWeekdayEveryMonth(IEnumerable<DateTime> dates)
{
var en = dates.GetEnumerator();
if (en.MoveNext())
{
DayOfWeek weekday = en.Current.DayOfWeek;
DateTime previous = en.Current;
while (en.MoveNext())
{
DateTime d = en.Current;
if (d.DayOfWeek != weekday || d.Day > 7)
return false;
if (d.Month != previous.Month && ((d - previous).Days == 28 || (d - previous).Days == 35))
return false;
previous = d;
}
}
return true;
}
I would recommend looking at the TimeSpan structure. Thanks to operator overload you can get a TimeSpan by substracting two dates and then receive a TimeSpan that expresses the difference between the two dates.
http://msdn.microsoft.com/en-us/library/system.timespan.aspx
okay, your code doesnt work when the years cross over becuase jan 1st may be a monday on one year and a tuesday on the next. If I was doing this, I would first check that
a) they are the same day of the week in each month (use DateTime.DayOfWeek)
b) they are the same week of the month in each month*
use extension method DayOfMonth (see link)
* Calculate week of month in .NET *
(you said you already know a & b to be true so lets go on to the third condition)
c) we have to determine if they are in consecutive months
//order the list of dates & place it into an array for ease of looping
DateTime[] orderedSlots = slots.OrderBy( t => t).ToArray<DateTime>();
//create a variable to hold the date from the previous month
DateTime temp = orderedSlots[0];
for(i= 1; index < orderedSlots.Length; index++)
{
if((orderedSlots[index].Month != temp.AddMonths(1).Month |
orderedSlots[index].Year != temp.AddMonths(1).Year)){
return false;
}
previousDate = orderedSlots[index];
}
return true;
if you need to check conditions a & b as well add change the if statement as follows
if( orderedSlots[index].Month != temp.AddMonths(1).Month |
orderedSlots[index].Year != temp.AddMonths(1).Year) |
orderedSlots[index].DayOfWeek != temp.DayOfWeek |
orderedSlots[index].GetWeekOfMonth != temp.AddMonths(1).GetWeekOfMonth){
return false;
}
remember that to use the get week of month extension method you have to include the code in
Calculate week of month in .NET
I'm sure there are typos as I did this in a text editor.
Well, here is my initial thought on how I would approach this problem.
First, is to define a function that will turn the dates into the ordinal values corresponding to the order in which they should appear.
int ToOrdinal(DateTime d, DateTime baseline) {
if (d.Day <= 7
&& d.DayInWeek == baseline.DayInWeek) {
// Since there is only one "First Friday" a month, and there are
// 12 months in year we can easily compose the ordinal.
// (As per default.kramer's comment, months normalized to [0,11].)
return d.Year * 12 + (d.Month - 1);
} else {
// Was not correct "kind" of day -
// Maybe baseline is Tuesday, but d represents Wednesday or
// maybe d wasn't in the first week ..
return 0;
}
}
var dates = ..;
var baseline = dates.FirstOrDefault();
var ordinals = dates.Select(d => ToOrdinal(d, baseline));
Then, for the dates provided, we end up with ordinal sequences like:
[24156 + 0, 24156 + 1, 24156 + 2, 24156 + 3]
And
[24156 + 0, 24156 + 1, /* !!!! */ 24156 + 3]
From here it is just a trivial matter of iterating the list and ensuring that the integers occur in sequence without gaps or stalls - that is, each item/integer is exactly one more than the previous.
I could be misinterpreting what you are trying to do, but I think this will work, assuming you don't have to handle ancient dates. See if there are any gaps in the dates converted to "total months"
int totalMonths = date.Year * 12 + (date.Month - 1);
I have this code, it failed because thisMonthSundays are empty:
public ActionResult TradeUKKPISearchesData() //show dropdownlist in the view
{
var now = DateTime.Now;
var lastMonth = now.AddMonths(-1);
var thisMonthSundays = GetDatesOfSundays(now.Year, now.Month).OrderByDescending(x => x.Date);
var lastMonthSundays = GetDatesOfSundays(lastMonth.Year, lastMonth.Month).OrderByDescending(x => x.Date); //problem here, must add some sort of check here?
var sundaysToTakeFromLastMonth = 4;
var sundays = thisMonthSundays.Concat(lastMonthSundays.Skip(Math.Max(0, lastMonthSundays.Count() - sundaysToTakeFromLastMonth)).Take(sundaysToTakeFromLastMonth));
var allSundaysInThisMonth = new SundaysInMonthViewModel
{
AllSundays = sundays.Select(x => new SelectListItem
{
Value = x.ToString("dd/MM/yyyy"),
Text = x.ToString("dd/MM/yyyy"),
})
};
var selectedSunday = new SundaysInMonthViewModel
{
SelectedSunday = thisMonthSundays.Where(x => x <= now).Last() //failed here
};
return View(allSundaysInThisMonth);
}
private IEnumerable<DateTime> GetDatesOfSundays(int year, int month)
{
var ci = CultureInfo.InvariantCulture;
for (int i=1; i <= ci.Calendar.GetDaysInMonth(year, month); i++)
{
var date = new DateTime(year, month, i);
if ((date.DayOfWeek == DayOfWeek.Sunday) && (date <= DateTime.Now))
{
yield return date; //skips all for this month
}
}
}
I need to fix this, please help with ideas?
thanks
As the Octobar month do not have SUnday so far, the variable SelectedSunday is empty....
You can use LastOrDefault() instead :
SelectedSunday = thisMonthSundays.Where(x => x <= now).LastOrDefault() ;
Note : The Default value for DateTime Type is DateTime.Min which is 1/1/0001 12:00:00 AM.
There are some mistakes in your code here.
Using var is not something you want to do everywhere.
You should never use arbitrary values in your functions. Instead of checking that the days are prior to today, you should add a limit parameter to your function and pass DateTime.Now
on the call.
Your function is already returning all the Sundays of a given month that are prior to today. Your Linq Request is just a replication of code and will return the whole collection every-time.
Since today is 10-01 and that we are Monday, there is no Sundays on October prior to today. This is why your collection is empty.