Related
I am using the nuget package Ical.Net 4.2.0 in my C# project. I've created a recurring calendar event and need to identify the start DateTime for each occurrence. I query the calendar with GetOccurrences() like this:
var now = DateTime.Parse("2021-09-16T10:00:00");
var later = now.AddHours(1);
//Repeat daily for 5 days
var rrule = new RecurrencePattern(FrequencyType.Daily, 1) { Count = 5 };
var e = new CalendarEvent
{
Start = new CalDateTime(now),
End = new CalDateTime(later),
RecurrenceRules = new List<RecurrencePattern> { rrule },
};
var calendar = new Calendar();
calendar.Events.Add(e);
var startSearch = new CalDateTime(DateTime.Parse("2021-09-16T00:00:00"));
var endSearch = new CalDateTime(DateTime.Parse("2021-09-21T23:59:59"));
var occurrences = calendar.GetOccurrences(startSearch, endSearch)
.Select(o => o.Source)
.Cast<CalendarEvent>()
.ToList();
occurrences contains five events, but looking for the "start" date, I can only find the event start date. Where can I find the start dates for each occurrence of a recurring event?
You get them in the actual Occurrence object returned by GetOcurrences (which you are discarding and actually selecting the source CalendarEvent, which is the same for all occurrences), in the Period.StartTime property (which is an IDateTime, which you can convert to .NET datetime objects)
To get all start date times (as DateTimeOffset object):
var startTimes = calendar.GetOccurrences(startSearch, endSearch)
.Select(x => x.Period.StartTime.AsDateTimeOffset);
See it in action
You can try with RecurrencePatternEvaluator
var vEvent = new Event {
DtStart = new CalDateTime(newDateTime(2017, 3, 1, 9, 0, 0)),
DtEnd = new CalDateTime(newDateTime(2017, 3, 1, 10, 0, 0))
};
var recurrenceRule = new RecurrencePattern(FrequencyDayType.Weekly, 1) {
ByDay = new IList<IWeekday> { new WeekDay(DayOfWeek.Thursday) }
};
var recurrenceEvaluator = new RecurrencePatternEvaluator(recurrenceRule);
var searchStart = new DateTime(2017, 3, 1, 0, 0, 0);
var searchEnd = new DateTime(2017, 3, 17, 0, 0, 0);
var correctOccurrences = recurrenceEvaluator.Evaluate(vEvent.DtStart, searchStart, searchEnd, false);
I'm trying to make a function in C# that returns the week difference between two dates. Its goal is to provide the same result of:
select datediff(ww,'2018-04-13','2018-04-16') as diff
In the example above there is only 3 days between these dates, but they are in different weeks, so the result should be 1.
I've tried to use .TotalDays but it's not working properly. I also tried .GetWeekOfYear but it won't return correctly when the year of the dates are different. I've seem many questions here on StackOverflow and on other forums and so far none of them match my case. This is the function I'm trying to far:
public static int GetWeekDiff(DateTime dtStart, DateTime dtEnd) {
// Doesn't work
var val = ((dtEnd - dtStart).TotalDays / 7);
val = Math.Ceiling(val);
return Convert.ToInt32(val);
// Doesn't work well between years
DateTimeFormatInfo dinfo = DateTimeFormatInfo.CurrentInfo;
var x = dinfo.Calendar.GetWeekOfYear(dtStart, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
var y = dinfo.Calendar.GetWeekOfYear(dtEnd, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
return y - x;
}
In the first part of my function, I tried what is described in this post. It didn't work
Can you help me?
Thanks in advance.
First figure how many days there are between the two dates. Divide the number of days by 7 to get full weeks.
Now figure out if there's an extra week to be counted by finding taking the number of days modulus 7 to get any remaining days. If the first date plus remaining days falls in a different week, add an extra week on to the count.
void Main()
{
var first = new DateTime(2018, 04, 13);
var second = new DateTime(2018, 04, 16);
Console.WriteLine(weekDiff(first, second));
}
public int weekDiff(DateTime d1, DateTime d2, DayOfWeek startOfWeek = DayOfWeek.Monday)
{
var diff = d2.Subtract(d1);
var weeks = (int)diff.Days / 7;
// need to check if there's an extra week to count
var remainingDays = diff.Days % 7;
var cal = CultureInfo.InvariantCulture.Calendar;
var d1WeekNo = cal.GetWeekOfYear(d1, CalendarWeekRule.FirstFullWeek, startOfWeek);
var d1PlusRemainingWeekNo = cal.GetWeekOfYear(d1.AddDays(remainingDays), CalendarWeekRule.FirstFullWeek, startOfWeek);
if (d1WeekNo != d1PlusRemainingWeekNo)
weeks++;
return weeks;
}
static void Main(string[] args)
{
DateTime date1 = new DateTime(2018, 04, 18);
DateTime date2 = new DateTime(2018, 04, 19);
System.Console.WriteLine((GetDiff(new DateTime(2018, 04, 18), new DateTime(2018, 04, 18)))); // 0
System.Console.WriteLine((GetDiff(new DateTime(2018, 04, 22), new DateTime(2018, 04, 23)))); // 1
System.Console.WriteLine((GetDiff(new DateTime(2018, 04, 16), new DateTime(2018, 04, 22)))); // 0
System.Console.WriteLine((GetDiff(new DateTime(2018, 04, 18), new DateTime(2018, 05, 03)))); // 2
}
private static int GetDiff(DateTime date1, DateTime date2)
{
date1 = SetDayToMonday(date1);
date2 = SetDayToMonday(date2);
return (int)((date2 - date1).TotalDays / 7);
}
private static DateTime SetDayToMonday(DateTime date)
{
var weekDay = date.DayOfWeek;
if (weekDay == DayOfWeek.Sunday)
return date.AddDays(-6);
else
return date.AddDays(-((int)weekDay-1));
}
First, set the day to the monday of the current week. Then count all full weeks(= /7 days as int). Easy as it is, it works probably across weeks and years.
See if this works. There could be more use cases that this doesn't cover, and the solution depends on how you define a week boundary (this assumes Sunday-Monday based on a comment above).
// Output:
// Weeks between 12/28/2017 and 1/10/2018: 2
// Weeks between 4/13/2018 and 4/16/2018: 1
// Weeks between 4/21/2018 and 4/22/2018: 0
// Weeks between 4/22/2018 and 4/23/2018: 1
void Main()
{
var datePairs = new List<KeyValuePair<DateTime, DateTime>>();
datePairs.Add(new KeyValuePair<DateTime, DateTime>(new DateTime(2017, 12, 28), new DateTime(2018, 1, 10)));
datePairs.Add(new KeyValuePair<DateTime, DateTime>(new DateTime(2018, 4, 13), new DateTime(2018, 4, 16)));
datePairs.Add(new KeyValuePair<DateTime, DateTime>(new DateTime(2018, 4, 21), new DateTime(2018, 4, 22)));
datePairs.Add(new KeyValuePair<DateTime, DateTime>(new DateTime(2018, 4, 22), new DateTime(2018, 4, 23)));
foreach (var datePair in datePairs)
{
var string1 = datePair.Key.ToShortDateString();
var string2 = datePair.Value.ToShortDateString();
Console.WriteLine($"Weeks between {string1} and {string2}: {GetWeekDiff(datePair.Key, datePair.Value)}");
}
}
public static int GetWeekDiff(DateTime dtStart, DateTime dtEnd)
{
var totalDays = (dtEnd - dtStart).TotalDays;
var weeks = (int)totalDays / 7;
var hasRemainder = totalDays % 7 > 0;
if (hasRemainder)
{
if (!(dtStart.DayOfWeek.Equals(DayOfWeek.Saturday) && dtEnd.DayOfWeek.Equals(DayOfWeek.Sunday)))
{
weeks++;
}
}
return weeks;
}
Maybe it can help
public static int GetIso8601WeekOfYear(DateTime time)
{
// Seriously cheat. If its Monday, Tuesday or Wednesday, then it'll
// be the same week# as whatever Thursday, Friday or Saturday are,
// and we always get those right
DayOfWeek day = CultureInfo.InvariantCulture.Calendar.GetDayOfWeek(time);
if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
{
time = time.AddDays(3);
}
// Return the week of our adjusted day
return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(time, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
}
Get the correct week number of a given date
Can't comment yet and already used a flag on this post on something I believed to be similar. Here is another post I found that appears to align with the solution you are trying to create:
Get the number of calendar weeks between 2 dates in C#
This is my implementation to solve a similar problem, I haven't tested in thoroughly but it seems to work.
var dt1 = DateTime.Today.AddDays(-30);
var dt2 = DateTime.Today;
var noOfDays =(int) (dt2 - dt1).TotalDays;
int reminder;
var weeks = Math.DivRem(noOfDays, 7, out reminder);
weeks = reminder > 0 ? weeks + 1 : weeks;
It returns 1 week for 6 days or less gap, which is exactly what I needed.
I am working on an algorithm to calculate a continuous overlap of multiple date ranges. It also needs to have a set number of times that it overlaps. For the example image below, I need 3 dates to overlap continuously. The valid overlapping dates would be Aug 20 - Aug 23, as Aug 24 only has 2 overlapping.
I have attempted many approaches including looping through all dates and comparing each one indivually with the next. That code looks like this.
Here is a .net fiddle for better visualization: https://dotnetfiddle.net/x3LfHR#.
private bool Compare(CompareDate a, CompareDate b)
{
DateTime? tStartA = a.ActiveDate;
DateTime? tEndA = a.ExpireDate;
DateTime? tStartB = b.ActiveDate;
DateTime? tEndB= b.ExpireDate;
bool overlap = (tStartA <= tEndB || tEndB == null) && (tStartB <= tEndA || tEndA == null);
DateTime? overlapStart = null;
DateTime? overlapEnd = null;
if (overlap)
{
//Find maximum start date.
overlapStart = (tStartA >= tStartB) ? tStartA : tStartB;
//Find Min End date between the two
overlapEnd = (tEndA <= tEndB) ? tEndA : tEndB;
if (overlapStart > this.overlapStart || this.overlapStart == null)
{
this.overlapStart = overlapStart;
}
if (overlapEnd < this.overlapEnd || this.overlapEnd == null)
{
this.overlapEnd = overlapEnd;
}
However, this approach makes it tricky to figure out continuous overlap dates. I have attempted to use the .Net Time period library at https://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET, but its not relevant in my case. Any help is appreciated.
OK - LINQ to the rescue!
Note: In order to make the comparison work, you must remove the time component and strictly use the only the date (e.g. DateTime.Date). Based on your requirements, that's exactly how you need to do it, so it shouldn't be a problem.
public List<DateTime> CompareDates(List<DateTime[]> compareRanges, int overlapLevel = 1)
{
var grouped = compareRanges.SelectMany(r => r).GroupBy(d => d);
var thresholdMatch = grouped.Where(g => g.Count() >= overlapLevel)
.Select(g => g.Key)
.OrderBy(d => d)
.ToList();
return thresholdMatch;
}
You can test the logic in a sample console app, using the skeleton code below as an example:
static void Main()
{
var setA = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
new DateTime(2017, 8, 24),
};
var setB = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
};
var setC = new[]
{
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
new DateTime(2017, 8, 24),
new DateTime(2017, 8, 25),
new DateTime(2017, 8, 26),
};
var setD = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
};
var compareList = new List<DateTime[]>
{
setA, setB, setC, setD
};
// setting the threshold to 2 will cause 8/24 to be added to the result...
// setting this to 1 (default) will return all intersections
// for now, set it to 3, per the question!
var result = CompareDates(compareList, 3);
foreach (var intersectDate in result)
{
Console.WriteLine(intersectDate);
}
}
Hope this helps, I certainly had fun with it!
P.S. I forked your fiddle: https://dotnetfiddle.net/GUzhjh.
This contains the modified version of your original program, so you should be able to play around with it a bit.
Here is a start at an algorithm:
Sort all start and end datetimes, assigning a +1 to every start and -1 to every end.
proceed from interval-start to interval-end, aggregating the period assignments above looking for a +3 value. Note this datetime.
proceed forward until your aggregate value drops below +3.
Voila! (I think.) Remember this one and continue processing.
When a second occurs, then save the longest and discard the other.
Continue until end-interval found; and report result.
This question already has answers here:
Showing Difference between two datetime values in hours
(8 answers)
Closed 8 years ago.
I have a List of DateTimes
var times = CurrentMeter.SessionTimes.ToList();
How do I find the difference between them and add the results to a new List?
I want the hours, minutes, seconds and milliseconds between each datetime.
A difference between two DateTime objects is a TimeSpan.
This function will calculate the time delta between each consecutive pair of DateTimes.
It will iterate through each time in times, subtract the previous value from it, and add the difference to your result list.
IEnumerable<TimeSpan> CalculateDeltas(IEnumerable<DateTime> times) {
var time_deltas = new List<TimeSpan>();
DateTime prev = times.First();
foreach (var t in times.Skip(1)) {
time_deltas.Add(t - prev);
prev = t;
}
return time_deltas;
}
Here's an example to calculate the difference between each item in the list. The results are stored in a List<double> and represent the total of seconds between each datetime.
var times = new List<DateTime>
{
new DateTime(2014, 5, 28, 9, 57, 12),
new DateTime(2014, 5, 28, 9, 57, 43),
new DateTime(2014, 5, 28, 9, 58, 03),
new DateTime(2014, 5, 28, 9, 59, 46),
new DateTime(2014, 5, 28, 10, 0, 22),
};
var differences = new List<double>();
for(int i = 0; i < times.Count - 1; i++)
{
differences.Add((times[i+1] - times[i]).TotalSeconds);
}
Output:
31
20
103
36
How about:
var time = new []{
DateTime.Parse("14-4-2012 00:00:00"),
DateTime.Parse("14-4-2012 01:12:34"),
DateTime.Parse("14-4-2012 12:44:33"),
DateTime.Parse("14-4-2012 23:12:42"),
};
var result = time.Zip(time.Skip(1), (a, b) => b - a)
.Select(d => new {d.Hours, d.Minutes, d.Seconds})
.ToList();
result is now:
(Note that this is a very simply approach and only works for differences < 24h. If you have greater differences, take into account d.Days etc.)
I am a bit lost on how to do this. I know how to initialize an array with values at the time of declaration. But how would I do it with a DateTime type array since it takes multiple arguments to create a date?
You mean like this?
DateTime[] dateTimes = new DateTime[]
{
new DateTime(2010, 10, 1),
new DateTime(2010, 10, 2),
// etc
};
DateTime [] startDate = new DateTime[5];
startDate[0] = new DateTime(11, 11, 10);
startDate[1] = new DateTime(11, 11, 10);
startDate[2] = new DateTime(11, 11, 10);
startDate[3] = new DateTime(11, 11, 10);
startDate[4] = new DateTime(11, 11, 10);
If you want to build an array for time span between two dates you could do something like this:
timeEndDate = timeStartDate.AddYears(1); // or .AddMonts etc..
rangeTimeSpan = timeEndDate.Subtract(timeStartDate); //declared prior as TimeSpan object
rangeTimeArray = new DateTime[rangeTimeSpan.Days]; //declared prior as DateTime[]
for (int i = 0; i < rangeTimeSpan.Days; i++)
{
timeStartDate = timeStartDate.AddDays(1);
rangeTimeArray[i] = timeStartDate;
}
For example, i want to add a DateTime array of 4 elements: DateTime[] myarray=new DateTime [4]; //the array is created
int year, month, day; //variables of date are created
for(int i=0; i<myarray.length;i++)
{
Console.WriteLine("Day");
day=Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Month");
month=Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Year");
year=Convert.ToInt32(Console.ReadLine());
DateTime date =new DateTime(year,month,day); //here is created the object DateTime, that contains day, month and year of a date
myarray[i]=date; //and then we set each date in each position of the array
}
DateTime [] "name_of_array"=new Date[int lenght_of_the_array]; //this is the array DateTime
And then when you assign the value in each position of the array:
DateTime "name_of_each_element_of_the_array"= new DateTime(int value_of_year,int value_of_month, int value_of_day);//this is each element that is added in each position of the array