public static Instant NextValid( this Instant instant, Month month = Month.Every, Week week = Week.Every, Day day = Day.Every, WeekDay weekDay = WeekDay.Every, Hour hour = Hour.Zeroth, Minute minute = Minute.Zeroth, Second second = Second.Zeroth, [CanBeNull] CalendarSystem calendarSystem = null, [CanBeNull] DateTimeZone timeZone = null) { // Never case, if any are set to never, we'll never get a valid date. if ((month == Month.Never) || (week == Week.Never) || (day == Day.Never) || (weekDay == WeekDay.Never) || (hour == Hour.Never) || (minute == Minute.Never) || (second == Second.Never)) { return(Instant.MaxValue); } if (calendarSystem == null) { calendarSystem = CalendarSystem.Iso; } if (timeZone == null) { timeZone = DateTimeZone.Utc; } Debug.Assert(calendarSystem != null); Debug.Assert(timeZone != null); // Move to next second. instant = instant.CeilingSecond(); // Every second case. if ((month == Month.Every) && (day == Day.Every) && (weekDay == WeekDay.Every) && (hour == Hour.Every) && (minute == Minute.Every) && (second == Second.Every) && (week == Week.Every)) { return(instant); } // Get days and months. int[] days = Days(day).OrderBy(dy => dy).ToArray(); int[] months = month.Months().ToArray(); // Remove months where the first day isn't in the month. int firstDay = days.First(); if (firstDay > 28) { // 2000 is a leap year, so February has 29 days. months = months.Where(mn => calendarSystem.GetDaysInMonth(2000, mn) >= firstDay).ToArray(); if (months.Length < 1) { return(Instant.MaxValue); } } // Get zoned date time. ZonedDateTime zdt = new ZonedDateTime(instant, timeZone, calendarSystem); int y = zdt.Year; int m = zdt.Month; int d = zdt.Day; int h = zdt.Hour; int n = zdt.Minute; int s = zdt.Second; int[] weeks = week.Weeks().ToArray(); IsoDayOfWeek[] weekDays = weekDay.WeekDays().ToArray(); int[] hours = hour.Hours().OrderBy(i => i).ToArray(); int[] minutes = minute.Minutes().OrderBy(i => i).ToArray(); int[] seconds = second.Seconds().ToArray(); do { foreach (int currentMonth in months) { if (currentMonth < m) { continue; } if (currentMonth > m) { d = 1; h = n = s = 0; } m = currentMonth; foreach (int currentDay in days) { if (currentDay < d) { continue; } if (currentDay > d) { h = n = s = 0; } d = currentDay; // Check day is valid for this month. if (d > calendarSystem.GetDaysInMonth(y, m)) { break; } // We have a potential day, check week and week day zdt = timeZone.AtLeniently(new LocalDateTime(y, m, d, h, n, s, calendarSystem)); if ((week != Week.Every) && (!weeks.Contains(zdt.WeekOfWeekYear))) { continue; } if ((weekDay != WeekDay.Every) && (!weekDays.Contains(zdt.IsoDayOfWeek))) { continue; } // We have a date match, check time. foreach (int currentHour in hours) { if (currentHour < h) { continue; } if (currentHour > h) { n = s = 0; } h = currentHour; foreach (int currentMinute in minutes) { if (currentMinute < n) { continue; } if (currentMinute > n) { s = 0; } n = currentMinute; foreach (int currentSecond in seconds) { if (currentSecond < s) { continue; } return (timeZone.AtLeniently( new LocalDateTime(y, m, d, h, n, currentSecond, calendarSystem)).ToInstant()); } n = s = 0; } h = n = s = 0; } d = 1; } d = 1; h = n = s = 0; } y++; // Don't bother checking max year. if (y >= calendarSystem.MaxYear) { return(Instant.MaxValue); } // Start next year m = d = 1; h = n = s = 0; } while (true); }