/// <summary> /// Determine the next valid day of month based on the given specification of valid days in month and /// valid days in week. If both days in week and days in month are supplied, the days are OR-ed. /// </summary> /// <param name="spec"></param> /// <param name="after"></param> /// <param name="result"></param> /// <returns></returns> private static int DetermineDayOfMonth( ScheduleSpec spec, DateTimeEx after, ScheduleCalendar result) { ICollection <int> daysOfMonthSet = spec.UnitValues.Get(ScheduleUnit.DAYS_OF_MONTH); ICollection <int> daysOfWeekSet = spec.UnitValues.Get(ScheduleUnit.DAYS_OF_WEEK); ICollection <int> secondsSet = spec.UnitValues.Get(ScheduleUnit.SECONDS); ICollection <int> minutesSet = spec.UnitValues.Get(ScheduleUnit.MINUTES); ICollection <int> hoursSet = spec.UnitValues.Get(ScheduleUnit.HOURS); int dayOfMonth; // If days of week is a wildcard, just go by days of month if (spec.OptionalDayOfMonthOperator != null || spec.OptionalDayOfWeekOperator != null) { var isWeek = false; var op = spec.OptionalDayOfMonthOperator; if (spec.OptionalDayOfMonthOperator == null) { op = spec.OptionalDayOfWeekOperator; isWeek = true; } // may return the current day or a future day in the same month, // and may advance the "after" date to the next month int currentYYMMDD = GetTimeYYYYMMDD(after); IncreaseAfterDayOfMonthSpecialOp(op.Operator, op.Day, op.Month, isWeek, after); int rolledYYMMDD = GetTimeYYYYMMDD(after); // if rolled then reset time portion if (rolledYYMMDD > currentYYMMDD) { result.Second = (NextValue(secondsSet, 0)); result.Minute = (NextValue(minutesSet, 0)); result.Hour = (NextValue(hoursSet, 0)); return(after.GetFieldValue(DateTimeFieldEnum.DAY_OF_MONTH)); } // rolling backwards is not allowed else if (rolledYYMMDD < currentYYMMDD) { throw new IllegalStateException( "Failed to evaluate special date op, rolled date less then current date"); } else { var work = new DateTimeEx(after); work.SetFieldValue(DateTimeFieldEnum.SECOND, result.Second); work.SetFieldValue(DateTimeFieldEnum.MINUTE, result.Minute); work.SetFieldValue(DateTimeFieldEnum.HOUR_OF_DAY, result.Hour); if (work <= after) // new date is not after current date, so bump { after.AddUsingField(DateTimeFieldEnum.DAY_OF_MONTH, 1); result.Second = NextValue(secondsSet, 0); result.Minute = NextValue(minutesSet, 0); result.Hour = NextValue(hoursSet, 0); IncreaseAfterDayOfMonthSpecialOp(op.Operator, op.Day, op.Month, isWeek, after); } return(after.GetFieldValue(DateTimeFieldEnum.DAY_OF_MONTH)); } } else if (daysOfWeekSet == null) { dayOfMonth = NextValue(daysOfMonthSet, after.Day); if (dayOfMonth != after.Day) { result.Second = NextValue(secondsSet, 0); result.Minute = NextValue(minutesSet, 0); result.Hour = NextValue(hoursSet, 0); } if (dayOfMonth == -1) { dayOfMonth = NextValue(daysOfMonthSet, 0); after.AddMonths(1, DateTimeMathStyle.Java); } } // If days of weeks is not a wildcard and days of month is a wildcard, go by days of week only else if (daysOfMonthSet == null) { // Loop to find the next day of month that works for the specified day of week values while (true) { dayOfMonth = after.Day; int dayOfWeek = (int)after.DayOfWeek; // TODO // // Check the DayOfWeek logic in this section. The former code reads something // like the following: // // Calendar.Get(after, SupportClass.CalendarManager.DAY_OF_WEEK) - 1; // // Java calendars are one based which means that subtracting one makes them // zero-based. CLR DateTimes are zero-based so there should be no need to // tweak the dates to make this work. // If the day matches neither the day of month nor the day of week if (!daysOfWeekSet.Contains(dayOfWeek)) { result.Second = NextValue(secondsSet, 0); result.Minute = NextValue(minutesSet, 0); result.Hour = NextValue(hoursSet, 0); after.AddDays(1, DateTimeMathStyle.Java); } else { break; } } } // Both days of weeks and days of month are not a wildcard else { // Loop to find the next day of month that works for either day of month OR day of week while (true) { dayOfMonth = after.Day; int dayOfWeek = (int)after.DayOfWeek; // TODO // // See my discussion above about day of week conversion // If the day matches neither the day of month nor the day of week if ((!daysOfWeekSet.Contains(dayOfWeek)) && (!daysOfMonthSet.Contains(dayOfMonth))) { result.Second = NextValue(secondsSet, 0); result.Minute = NextValue(minutesSet, 0); result.Hour = NextValue(hoursSet, 0); after.AddDays(1, DateTimeMathStyle.Java); } else { break; } } } return(dayOfMonth); }
private static long Compute( ScheduleSpec spec, long afterTimeInMillis, TimeZoneInfo timeZone, TimeAbacus timeAbacus) { ICollection <int> minutesSet = spec.UnitValues.Get(ScheduleUnit.MINUTES); ICollection <int> hoursSet = spec.UnitValues.Get(ScheduleUnit.HOURS); ICollection <int> monthsSet = spec.UnitValues.Get(ScheduleUnit.MONTHS); bool isSecondsSpecified = spec.UnitValues.ContainsKey(ScheduleUnit.SECONDS); ICollection <int> secondsSet = isSecondsSpecified ? spec.UnitValues.Get(ScheduleUnit.SECONDS) : null; bool isMillisecondsSpecified = spec.UnitValues.ContainsKey(ScheduleUnit.MILLISECONDS); ICollection <int> millisecondsSet = isMillisecondsSpecified ? spec.UnitValues.Get(ScheduleUnit.MILLISECONDS) : null; bool isMicrosecondsSpecified = spec.UnitValues.ContainsKey(ScheduleUnit.MICROSECONDS); ICollection <int> microsecondsSet = isMillisecondsSpecified ? spec.UnitValues.Get(ScheduleUnit.MICROSECONDS) : null; while (true) { DateTimeEx after; if (spec.OptionalTimeZone != null) { try { timeZone = TimeZoneHelper.GetTimeZoneInfo(spec.OptionalTimeZone); after = DateTimeEx.GetInstance(timeZone); } catch (TimeZoneNotFoundException) { // this behavior ensures we are consistent with Java, but IMO, it's bad behavior... // basically, if the timezone is not found, we default to UTC. timeZone = TimeZoneInfo.Utc; after = DateTimeEx.GetInstance(timeZone); } } else { after = DateTimeEx.GetInstance(timeZone); } var remainder = timeAbacus.DateTimeSet(afterTimeInMillis, after); var result = new ScheduleCalendar { Milliseconds = after.Millisecond }; if (isMicrosecondsSpecified) { long nextValue = NextValue(microsecondsSet, (int)remainder); remainder = nextValue; if (nextValue == -1) { nextValue = NextValue(microsecondsSet, 0); remainder = nextValue; after.AddMilliseconds(1); } } else { result.Milliseconds = after.Millisecond; } if (isMillisecondsSpecified) { result.Milliseconds = NextValue(millisecondsSet, after.Millisecond); if (result.Milliseconds == -1) { result.Milliseconds = NextValue(millisecondsSet, 0); after.AddSeconds(1); } } else { result.Milliseconds = after.Millisecond; } if (isSecondsSpecified) { result.Second = NextValue(secondsSet, after.Second); if (result.Second == -1) { result.Second = NextValue(secondsSet, 0); after.AddMinutes(1); } } result.Minute = NextValue(minutesSet, after.Minute); if (result.Minute != after.Minute) { result.Second = NextValue(secondsSet, 0); } if (result.Minute == -1) { result.Minute = NextValue(minutesSet, 0); after.AddHours(1); } result.Hour = NextValue(hoursSet, after.Hour); if (result.Hour != after.Hour) { result.Second = NextValue(secondsSet, 0); result.Minute = NextValue(minutesSet, 0); } if (result.Hour == -1) { result.Hour = NextValue(hoursSet, 0); after.AddDays(1, DateTimeMathStyle.Java); } // This call may change second, minute and/or hour parameters // They may be reset to minimum values if the day rolled result.DayOfMonth = DetermineDayOfMonth(spec, after, result); bool dayMatchRealDate = false; while (!dayMatchRealDate) { if (CheckDayValidInMonth(timeZone, result.DayOfMonth, after.Month, after.Year)) { dayMatchRealDate = true; } else { after.AddMonths(1, DateTimeMathStyle.Java); } } int currentMonth = after.Month; result.Month = NextValue(monthsSet, currentMonth); if (result.Month != currentMonth) { result.Second = NextValue(secondsSet, 0); result.Minute = NextValue(minutesSet, 0); result.Hour = NextValue(hoursSet, 0); result.DayOfMonth = DetermineDayOfMonth(spec, after, result); } if (result.Month == -1) { result.Month = NextValue(monthsSet, 0); after.AddYears(1); } // Perform a last valid date check, if failing, try to compute a new date based on this altered after date int year = after.Year; if (!CheckDayValidInMonth(timeZone, result.DayOfMonth, result.Month, year)) { afterTimeInMillis = timeAbacus.DateTimeGet(after, remainder); continue; } return(GetTime(result, after.Year, spec.OptionalTimeZone, timeZone, timeAbacus, remainder)); } }