Where is the occurrence "start" date in Ical.Net recurring event? - c#

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

Related

C# arrange the items by matching start and end value

I have a input in this way,
Start= 2, End= 3
Start= 6, End= 7
Start= 3, End= 4
Start= 0, End= 1
Start= 4, End= 5
Start= 1, End= 2
Start= 6, End= 7
where I have to display the output that are sorted by their matching start- and end values. The first element are the object whose Start doesn't have any matching End values, and after that the records(Start) have the matching end values.
I needed to have the output in the following ways:
Start = 0, End = 1
Start = 1, End = 2
Start = 2, End = 3
Start = 3, End = 4
Start = 4, End = 5
Start = 5, End = 6
Start = 6, End = 7
What I have tried so far:
Created a List of Inputs
var inputs = new List
{
new Input{Start = 2, End = 3},
new Input{Start = 6, End = 7},
new Input{Start = 3, End = 4},
new Input{Start = 0, End = 1},
new Input{Start = 5, End = 6},
new Input{Start = 4, End = 5},
new Input{Start = 1, End = 2}
};
Get the list of all the Ends
var ends = inputs.Select(t => t.End);
Filter from the input where value of End matched with the Start of the next Objects.
var filtered = inputs.Where(t => ends.Any(to => to == t.Start)).ToList();
Get the list where Start doesn't have the matching ends:
var output = inputs.Where(t => !ends.Any(to => to == t.Start)).ToList();
Now filter the results as:
while (filtered.Count != 0)
{
var outs = filtered.Where(i => output.Any(l => i.Start == l.End)).FirstOrDefault();
output.Add(outs);
filtered.Remove(outs);
}
foreach (Input s in output)
{
Console.WriteLine($"Start = {s.Start}, End = {s.End}");
}
Dotnet fiddle:
I have somehow able to solve this problem with this approach. But I have a doubt that this is not a proper ways of solving. Because I have a repetitive filter for getting the matched and unmatched records and, I have also a doubt on the while loop implementation.
Any suggestion for a better approach of solving this problem?
If the End is always equals Start + 1. Then, you could take the max value of Start and with a simple for loop, you check, if exists, return value, if not, insert the new value.
Here is an extension method that would accept almost all type of collections or arrays :
public static class Extensions
{
public static IEnumerable<Input?> SortAndFill(this IEnumerable<Input?> source)
{
if(source == null) yield break;
var max = source?.Max(x=> x.Start) ?? 0;
if (max == 0) yield break;
max++;
for (int x = 0; x < max; x++)
{
var item = source.FirstOrDefault(s => s.Start == x);
if (item == null)
item = new Input(x, x + 1);
yield return item;
}
}
}
usage :
var result = inputs.SortAndFill().ToArray();

Reversing a list of objects with a timespan property, while maintaining the time difference between the objects

I'd like to reverse a list of objects with a TimeSpan property, which should maintain it's TimeSpan difference when reversing.
To give an example, consider a route from A to D with the following TimeSpans:
(A 12:00), (B 12:15), (C 12:40), (D 13:40).
Between A and B there is a 15 minute difference, between B and C there is a 25 minute difference and so on. I'd like to reverse this list in an efficient manner, where the result list would look like:
(D: 12:00), (C 13:00), (B 13:25), (A 13:40).
My first idea was creating a list of time differences and using that and the start time to create the new objects with the correct times, however I feel like the solution could be better.
Edit: Added my (working) sample solution. Any feedback is appreciated.
private IList<Activity> ReverseActivities(IList<Activity> activities)
{
IList<TimeSpan> timeDifferences = GetTimeDifferences(activities);
IList<Activity> resultList = new List<Activity>();
TimeSpan timeOfDay = activities.First().TimeOfDay;
for (int i = activities.Count - 1; i >= 0; i--)
{
resultList.Add(new Activity(activities[i].Name, timeOfDay));
timeOfDay = timeOfDay.Add(timeDifferences[i]);
}
return resultList;
}
private IList<TimeSpan> GetTimeDifferences(IList<Activity> activities)
{
IList<TimeSpan> timeDifferences = new List<TimeSpan>();
Activity prev = activities.First();
if (activities.Count > 1)
{
foreach (var curr in activities)
{
timeDifferences.Add(curr.TimeOfDay - prev.TimeOfDay);
prev = curr;
}
}
return timeDifferences;
}
Activity looks as follows:
public class Activity
{
public Activity(string name, TimeSpan timeOfDay)
{
this.Name = name;
this.TimeOfDay = timeOfDay;
}
public string Name { get; }
public TimeSpan TimeOfDay { get; }
}
One trick we can use is to have a single loop that finds the corresponding item from the end of the list based on the current index. We can do this like:
for (int i = 0; i < activities.Count; i++)
var correspondingIndex = activities.Count - i - 1;
Notice that:
When i is 0, correspondingIndex is the last index in the array.
When i is 1, correspondingIndex is the second-to-last index in the array.
When i is activities.Count - 1 (the last index), correspondingIndex is 0
Using this trick, we can get the corresponding time differences at the same time as we populate a new list of Activity objects.
Hopefully this code makes it a little clearer:
public static IList<Activity> ReverseActivities(IList<Activity> activities)
{
// If activities is null or contains less than 2 items, return it
if ((activities?.Count ?? 0) < 2) return activities;
// This will contain the reversed list
var reversed = new List<Activity>();
for (int i = 0; i < activities.Count; i++)
{
// Get the corresponding index from the end of the list
var correspondingIndex = activities.Count - i - 1;
// Get the timespan from the corresponding items from the end of the list
var timeSpan = i == 0
? TimeSpan.Zero
: activities[correspondingIndex + 1].TimeOfDay -
activities[correspondingIndex].TimeOfDay;
// The new TimeOfDay will be the previous item's TimeOfDay plus the TimeSpan above
var timeOfDay = i == 0
? activities[i].TimeOfDay
: reversed[i - 1].TimeOfDay + timeSpan;
reversed.Add(new Activity(activities[correspondingIndex].Name, timeOfDay));
}
return reversed;
}
In use, this would look like:
var original = new List<Activity>
{
new Activity("A", new TimeSpan(0, 12, 0)),
new Activity("B", new TimeSpan(0, 12, 15)),
new Activity("C", new TimeSpan(0, 12, 40)),
new Activity("D", new TimeSpan(0, 13, 40))
};
var reversed = ReverseActivities(original);
Here's the output in the debug window (compare original and reversed):
This is quite simple using a bit of TimeSpan maths.
IList<Activity> input = new List<Activity>()
{
new Activity("A", TimeSpan.Parse("12:00")),
new Activity("B", TimeSpan.Parse("12:15")),
new Activity("C", TimeSpan.Parse("12:40")),
new Activity("D", TimeSpan.Parse("13:40")),
};
TimeSpan min = input.Min(x => x.TimeOfDay);
TimeSpan max = input.Max(x => x.TimeOfDay);
IList<Activity> output =
input
.Select(x => new Activity(
x.Name,
x.TimeOfDay.Subtract(max).Duration().Add(min)))
.OrderBy(x => x.TimeOfDay)
.ToList();
That gives me:
I tested this and it works:
DateTime[] times = { new DateTime(2020, 06, 17, 12, 00, 00),
new DateTime(2020, 06, 17, 12, 15, 00), new DateTime(2020, 06, 17, 12, 40, 00),
new DateTime(2020, 06, 17, 13, 40, 00) };
List<DateTime> newTimes = new List<DateTime>();
newTimes.Add(times[0]);
for (int i = 1; i < times.Length; i++) {
DateTime d = newTimes[i - 1].Add(times[times.Length - i] - times[times.Length - i - 1]);
newTimes.Add(d);
}
Using LinkedList:
static void Main(string[] args)
{
var list = new List<Location>
{
new Location{Name = "A", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 0, 0)) },
new Location{Name = "B", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 15, 0)) },
new Location{Name = "C", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(12, 40, 0)) },
new Location{Name = "D", TimeOffset = DateTimeOffset.MinValue.Add(new TimeSpan(13, 40, 0)) },
};
var route = new LinkedList<Location>(list);
WriteToConsole("Before: ", route);
var reversedRoute = Reverse(route);
Console.WriteLine();
WriteToConsole("After: ", reversedRoute);
Console.WriteLine(); Console.ReadKey();
}
public static LinkedList<Location> Reverse(LinkedList<Location> route)
{
LinkedList<Location> retVal = new LinkedList<Location>();
DateTimeOffset timeOffset = DateTimeOffset.MinValue;
var currentNode = route.Last;
while (currentNode != null)
{
var next = currentNode.Next;
if (next == null)
{
// last node, use the first node offset
timeOffset = DateTimeOffset.MinValue.Add(route.First.Value.TimeOffset - timeOffset);
}
else
{
timeOffset = timeOffset.Add(next.Value.TimeOffset - currentNode.Value.TimeOffset);
}
retVal.AddLast(new Location { Name = currentNode.Value.Name, TimeOffset = timeOffset });
currentNode = currentNode.Previous;
}
return retVal;
}
public static void WriteToConsole(string title, LinkedList<Location> route)
{
Console.Write($"{title}: ");
foreach (var i in route)
{
Console.Write($"\t({i.Name}, {i.TimeOffset.Hour:D2}:{i.TimeOffset.Minute:D2})");
}
}

how do I ask an if argument on a specific field which in a list of objects that contain a list in c#?

I have a list of objects in which every object is containing a list itself. how do I get the the JellyFishID field or the Amount field for using an IF argument
(I'm currently using Foreach):`
public static List<Report> DataSorted = new List<Report> {
new Report() { IsGoldUser=true, Date=new DateTime(2016, 3, 12,11, 59, 33), IsBurningWater=true, Type=Type.Shore, ZoneID = 1 ,
ReportDetails =new List<ReportDetail> { new ReportDetail() { Amount = Amount.Few, Jellyfish = new Jellyfish { JellyfishID = 1, Venom = Venom.Strong } } } },
new Report() { IsGoldUser=true, Date=new DateTime(2016, 3, 12, 11, 59, 33), IsBurningWater=true, Type=Type.Shore, ZoneID = 1 ,
ReportDetails =new List<ReportDetail> { new ReportDetail() { Amount = Amount.Few, Jellyfish = new Jellyfish { JellyfishID = 1, Venom = Venom.Strong } } } },
new Report() { IsGoldUser=true, Date=new DateTime(2016, 3, 12, 11, 59, 33), IsBurningWater=true, Type=Type.Shore, ZoneID = 1 ,
ReportDetails =new List<ReportDetail> { new ReportDetail() { Amount = Amount.Few, Jellyfish = new Jellyfish { JellyfishID = 1, Venom = Venom.Strong } } } },
new Report() { IsGoldUser=true, Date=new DateTime(2016, 3, 12, 11, 59, 33), IsBurningWater=true, Type=Type.Shore, ZoneID = 1 ,
ReportDetails =new List<ReportDetail> { new ReportDetail() { Amount = Amount.Few, Jellyfish = new Jellyfish { JellyfishID = 1, Venom = Venom.Strong } } } },
foreach (var item in DataSorted)
{
if (item.ReportDetails....) //???I want here to Make an Argument about The Amount field or the JellyFishID field in the list above....
}
You don't describe exactly what you want to check, but with LINQ to Objects you have a lot of possiblities. At first, you need to reference the correct namespace with
using System.Linq;
at the top of your source code file.
Now, if you want to check if any items of your list contains a jellyfish with a given ID, you can use:
if (item.ReportDetails.Any(t => t.Jellyfish.JellyfishID == 1)) //...
Additionally you can have conditions inside a Where-function to filter your list and search only for jellyfish with a few amount:
if (item.ReportDetails.Where(t => t.Amount == Amount.Few).
Any(t => t.Jellyfish.JellyfishID == 1)) //...
There is a lot of information avaliable about LINQ, a lot of examples are in the MSDN (for example this intro page), but there are alternatives like this one: 101 Linq examples. It even has a tag on StackOverflow.

List with dates, addHours to list linq

I have a List with dates to show it in a grid and I would like add one hour to range2:
private void grid_loaded(object sender, RoutedEventArgs e)
{
var data = new List<EntityViewModel>
{
new EntityViewModel { aDay = new DateTime(1980, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(1983, 1, 1, 12, 23, 12) },
new EntityViewModel { aDay = new DateTime(1985, 6, 14, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(1990, 7, 3, 12, 23, 12) },
new EntityViewModel { aDay = new DateTime(1995, 8, 1, 4, 23, 12) },
new EntityViewModel { aDay = new DateTime(1996, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = new DateTime(2000, 1, 1, 12, 0, 12) },
new EntityViewModel { aDay = DateTime.Now }
};
var range = data.Where(i => i.aDay.Year >= 1983 && i.aDay.Year <= 1996).ToList();
var range2 = range.Where(i => i.aDay.Date.IsDaylightSavingTime() == true).ToList();
}
I tried this:
var range3 = range.Where(i => i.aDay.Date.IsDaylightSavingTime() == true).ToList();
range3.ForEach(i => i.aDay.AddHours(1));
this:
foreach (var item in range2.Where(x => x.aDay != null))
{
item.aDay.AddHours(1);
}
and this:
var range5 = range2.Where(i => i.aDay != null).Select(i => { i.aDay.AddHours(1); return i; }).ToList();
But it doesn't do anything, are always the same hour.
AddHours does NOT alter the initial date, but return a new one.
You need to select these dates:
var range5 = range2.Where(i => i.aDay != null).Select(i => i.aDay.AddHours(1));
To get the altered dates or
range3.ForEach(i => i.aDay = i.aDay.AddHours(1));
To alter the dates of the items in the list.
DateTime is a structure, so you have to do:
var date = DateTime.Now;
date = date.AddHours(1);
in your case:
range3.ForEach(i => i.aDay = i.aDay.AddHours(1));
AddHours returns a new DateTime, it does not change the current instance.
http://msdn.microsoft.com/en-us/library/system.datetime.addhours.aspx
for (int i = 0; i < range2.Count; ++i) {
range2[i].aDay = range2[i].aDay.AddHours(1);
}

DateTime Ticks, check for overlapping dates C#

Lets say I 2 longs a start and end, which are really two date times converted to ticks. How would I tell if these two values overlap?
As MPelletier mentioned: you need two pairs of DateTimes:
var start1 = new DateTime(2013, 4, 10).Ticks;
var end1 = new DateTime(2013, 4, 20).Ticks;
var start2 = new DateTime(2013, 4, 9).Ticks;
var end2 = new DateTime(2013, 4, 11).Ticks;
if (start2 < end1)
{
//// Overlapping
}

Categories