private async Task UpdateReminderAsync(StoredReminder reminder) { //add a small threshold to prevent off by one errors var futureTime = Math.Max(MinReminderFuture.TotalMinutes, reminder.Minutes) + EventDiscoveryOverlap; var evts = (await calendarService.Get(reminder.UserId, DateTimeOffset.Now, DateTimeOffset.Now.AddMinutes(futureTime))) ?? new Event[0]; var events = (evts).Where(v => v.Start >= DateTimeOffset.Now); var existingInstances = new List <ReminderInstance>(reminder.Instances); foreach (var e in events) { var instance = reminder.Instances.Where(v => v.EventId == e.Id && v.FeedId == e.FeedId).FirstOrDefault(); var shouldFire = e.Start.AddMinutes(-reminder.Minutes) <= DateTimeOffset.Now; var eventHash = e.GenerateHash(); if (null != instance) { existingInstances.Remove(instance); if (instance.Hash != eventHash) { instance = await reminderRepository.UpdateInstanceAsync(instance.Id, eventHash); await FireOrInstallAsync(reminder, e, instance, shouldFire, eventHash); } } else { instance = new ReminderInstance() { Id = Guid.NewGuid().ToString(), EventId = e.Id, FeedId = e.FeedId, Hash = eventHash }; await reminderRepository.AddInstanceAsync(reminder.Id, instance); await FireOrInstallAsync(reminder, e, instance, shouldFire, eventHash); } } foreach (var instance in existingInstances) { if (instance.RemindRemovalUntil.HasValue && instance.RemindRemovalUntil > DateTimeOffset.Now.UtcDateTime) { await PostReminder(reminder, new ReminderDelivery { ReminderId = reminder.Id, ClientState = reminder.ClientState, Event = null, EventId = instance.EventId, FeedId = instance.FeedId, Removed = true }); } await reminderRepository.RemoveInstanceAsync(instance.Id); } }