/// <summary> /// Computes the next lowest date in milliseconds based on a specification and the /// from-time passed in. /// </summary> /// <param name="spec">defines the schedule</param> /// <param name="afterTimeInMillis">defines the start time</param> /// <param name="timeZone">The time zone.</param> /// <param name="timeAbacus">The time abacus.</param> /// <returns> /// a long date tick value for the next schedule occurance matching the spec /// </returns> public static long ComputeNextOccurance( ScheduleSpec spec, long afterTimeInMillis, TimeZoneInfo timeZone, TimeAbacus timeAbacus) { if (ExecutionPathDebugLog.IsEnabled && Log.IsDebugEnabled) { Log.Debug( ".computeNextOccurance Computing next occurance," + " afterTimeInTicks=" + afterTimeInMillis.TimeFromMillis(timeZone) + " as long=" + afterTimeInMillis + " spec=" + spec); } // Add the minimum resolution to the Start time to ensure we don't get the same exact time if (spec.UnitValues.ContainsKey(ScheduleUnit.SECONDS)) { afterTimeInMillis += timeAbacus.GetOneSecond(); } else { afterTimeInMillis += 60 * timeAbacus.GetOneSecond(); } return(Compute(spec, afterTimeInMillis, timeZone, timeAbacus)); }
/// <summary> /// Computes the next lowest date in milliseconds based on a specification and the /// from-time passed in and returns the delta from the current time. /// </summary> /// <param name="spec">The schedule.</param> /// <param name="afterTimeInMillis">defines the start time.</param> /// <param name="timeZone">The time zone.</param> /// <param name="timeAbacus">The time abacus.</param> /// <returns> /// a long millisecond value representing the delta between current time and the next schedule occurance matching the spec /// </returns> public static long ComputeDeltaNextOccurance( ScheduleSpec spec, long afterTimeInMillis, TimeZoneInfo timeZone, TimeAbacus timeAbacus) { return(ComputeNextOccurance(spec, afterTimeInMillis, timeZone, timeAbacus) - afterTimeInMillis); }
public void CheckCorrect(ScheduleSpec spec, String now, String expected) { var provider = Thread.CurrentThread.CurrentCulture; var nowDate = DateTimeOffset.ParseExact(now, "yyyy-MM-d H:mm:ss", provider); var expectedDate = DateTimeOffset.ParseExact(expected, "yyyy-MM-d H:mm:ss", provider); var result = ScheduleComputeHelper.ComputeNextOccurance(spec, nowDate.TimeInMillis(), TimeZoneInfo.Local); var resultDate = result.TimeFromMillis(expectedDate.Offset); if (resultDate != expectedDate) { Log.Debug(".checkCorrect Difference in result found, spec=" + spec); Log.Debug(".checkCorrect now=" + nowDate.ToString(TimeFormat) + " long=" + nowDate.TimeInMillis()); Log.Debug(".checkCorrect expected=" + expectedDate.ToString(TimeFormat) + " long=" + expectedDate.TimeInMillis()); Log.Debug(".checkCorrect result=" + resultDate.ToString(TimeFormat) + " long=" + resultDate.TimeInMillis()); Assert.Fail(); } }
public void CheckCorrectWZone(ScheduleSpec spec, String nowWZone, String expectedWZone) { var nowDate = DateTimeParser.ParseDefaultMSecWZone(nowWZone); var nowDateJava = DateTimeOffsetHelper.MillisToJavaMillis(nowDate); var expectedDate = DateTimeParser.ParseDefaultMSecWZone(expectedWZone); var result = ScheduleComputeHelper.ComputeNextOccurance(spec, nowDate, TimeZoneInfo.Local); var resultDate = result.TimeFromMillis(null); if (result != expectedDate) { Log.Debug(".checkCorrect Difference in result found, spec=" + spec); Log.Debug(".checkCorrect now=" + nowDate.ToString(TimeFormat) + " long=" + nowDate); Log.Debug(".checkCorrect expected=" + expectedDate.ToString(TimeFormat) + " long=" + expectedDate); Log.Debug(".checkCorrect result=" + resultDate.ToString(TimeFormat) + " long=" + resultDate); Assert.Fail(); } }
public void TestCompress() { IDictionary <ScheduleUnit, ICollection <int> > unitValues = new Dictionary <ScheduleUnit, ICollection <int> >(); unitValues = (new ScheduleSpec()).UnitValues; // Populate Month with all valid values var monthValues = new SortedSet <int>(); for (int i = ScheduleUnit.MONTHS.Min(); i <= ScheduleUnit.MONTHS.Max(); i++) { monthValues.Add(i); } unitValues.Put(ScheduleUnit.MONTHS, monthValues); // Construct spec, test that month was replaced with wildcards ScheduleSpec spec = new ScheduleSpec(unitValues, null, null, null); Assert.IsTrue(spec.UnitValues.Get(ScheduleUnit.MONTHS) == null); }
public void CheckCorrectWZone(ScheduleSpec spec, String nowWZone, String expectedWZone) { var nowDateEx = DateTimeParser.ParseDefaultExWZone(nowWZone); var nowDate = nowDateEx.TimeInMillis; //var nowDateJava = DateTimeOffsetHelper.MillisToJavaMillis(nowDate); var expectedDateEx = DateTimeParser.ParseDefaultExWZone(expectedWZone); var expectedDate = expectedDateEx.TimeInMillis; var result = ScheduleComputeHelper.ComputeNextOccurance(spec, nowDate, nowDateEx.TimeZone, TimeAbacusMilliseconds.INSTANCE); var resultEx = DateTimeEx.GetInstance(expectedDateEx.TimeZone, result); var resultDate = result.TimeFromMillis(null); if (result != expectedDate) { Log.Debug(".checkCorrect Difference in result found, spec=" + spec); Log.Debug(".checkCorrect now=" + nowDate.ToString(TimeFormat) + " long=" + nowDate); Log.Debug(".checkCorrect expected=" + expectedDate.ToString(TimeFormat) + " long=" + expectedDate); Log.Debug(".checkCorrect result=" + resultDate.ToString(TimeFormat) + " long=" + resultDate); Assert.Fail(); } }
public void TestCompute() { ScheduleSpec spec = null; // Try next "5 minutes past the hour" spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.MINUTES, 5); CheckCorrect(spec, "2004-12-9 15:45:01", "2004-12-9 16:05:00"); CheckCorrect(spec, "2004-12-9 16:04:59", "2004-12-9 16:05:00"); CheckCorrect(spec, "2004-12-9 16:05:00", "2004-12-9 17:05:00"); CheckCorrect(spec, "2004-12-9 16:05:01", "2004-12-9 17:05:00"); CheckCorrect(spec, "2004-12-9 16:05:01", "2004-12-9 17:05:00"); CheckCorrect(spec, "2004-12-9 23:58:01", "2004-12-10 00:05:00"); // Try next "5, 10 and 15 minutes past the hour" spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.MINUTES, 5); spec.AddValue(ScheduleUnit.MINUTES, 10); spec.AddValue(ScheduleUnit.MINUTES, 15); CheckCorrect(spec, "2004-12-9 15:45:01", "2004-12-9 16:05:00"); CheckCorrect(spec, "2004-12-9 16:04:59", "2004-12-9 16:05:00"); CheckCorrect(spec, "2004-12-9 16:05:00", "2004-12-9 16:10:00"); CheckCorrect(spec, "2004-12-9 16:10:00", "2004-12-9 16:15:00"); CheckCorrect(spec, "2004-12-9 16:14:59", "2004-12-9 16:15:00"); CheckCorrect(spec, "2004-12-9 16:15:00", "2004-12-9 17:05:00"); // Try next "0 and 30 and 59 minutes past the hour" spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.MINUTES, 0); spec.AddValue(ScheduleUnit.MINUTES, 30); spec.AddValue(ScheduleUnit.MINUTES, 59); CheckCorrect(spec, "2004-12-9 15:45:01", "2004-12-9 15:59:00"); CheckCorrect(spec, "2004-12-9 15:59:01", "2004-12-9 16:00:00"); CheckCorrect(spec, "2004-12-9 16:04:59", "2004-12-9 16:30:00"); CheckCorrect(spec, "2004-12-9 16:30:00", "2004-12-9 16:59:00"); CheckCorrect(spec, "2004-12-9 16:59:30", "2004-12-9 17:00:00"); // Try minutes combined with seconds spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.MINUTES, 0); spec.AddValue(ScheduleUnit.MINUTES, 30); spec.AddValue(ScheduleUnit.SECONDS, 0); spec.AddValue(ScheduleUnit.SECONDS, 30); CheckCorrect(spec, "2004-12-9 15:59:59", "2004-12-9 16:00:00"); CheckCorrect(spec, "2004-12-9 16:00:00", "2004-12-9 16:00:30"); CheckCorrect(spec, "2004-12-9 16:00:29", "2004-12-9 16:00:30"); CheckCorrect(spec, "2004-12-9 16:00:30", "2004-12-9 16:30:00"); CheckCorrect(spec, "2004-12-9 16:29:59", "2004-12-9 16:30:00"); CheckCorrect(spec, "2004-12-9 16:30:00", "2004-12-9 16:30:30"); CheckCorrect(spec, "2004-12-9 17:00:00", "2004-12-9 17:00:30"); // Try hours combined with seconds spec = new ScheduleSpec(); for (var i = 10; i <= 14; i++) { spec.AddValue(ScheduleUnit.HOURS, i); } spec.AddValue(ScheduleUnit.SECONDS, 15); CheckCorrect(spec, "2004-12-9 15:59:59", "2004-12-10 10:00:15"); CheckCorrect(spec, "2004-12-10 10:00:15", "2004-12-10 10:01:15"); CheckCorrect(spec, "2004-12-10 10:01:15", "2004-12-10 10:02:15"); CheckCorrect(spec, "2004-12-10 14:01:15", "2004-12-10 14:02:15"); CheckCorrect(spec, "2004-12-10 14:59:15", "2004-12-11 10:00:15"); // Try hours combined with minutes spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.HOURS, 9); spec.AddValue(ScheduleUnit.MINUTES, 5); CheckCorrect(spec, "2004-12-9 15:59:59", "2004-12-10 9:05:00"); CheckCorrect(spec, "2004-11-30 15:59:59", "2004-12-1 9:05:00"); CheckCorrect(spec, "2004-11-30 9:04:59", "2004-11-30 9:05:00"); CheckCorrect(spec, "2004-12-31 9:05:01", "2005-01-01 9:05:00"); // Try day of month as the 31st spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.DAYS_OF_MONTH, 31); CheckCorrect(spec, "2004-11-30 15:59:59", "2004-12-31 0:00:00"); CheckCorrect(spec, "2004-12-30 15:59:59", "2004-12-31 0:00:00"); CheckCorrect(spec, "2004-12-31 00:00:00", "2004-12-31 0:01:00"); CheckCorrect(spec, "2005-01-01 00:00:00", "2005-01-31 0:00:00"); CheckCorrect(spec, "2005-02-01 00:00:00", "2005-03-31 0:00:00"); CheckCorrect(spec, "2005-04-01 00:00:00", "2005-05-31 0:00:00"); // Try day of month as the 29st, for february testing spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.DAYS_OF_MONTH, 29); CheckCorrect(spec, "2004-11-30 15:59:59", "2004-12-29 0:00:00"); CheckCorrect(spec, "2004-12-29 00:00:00", "2004-12-29 0:01:00"); CheckCorrect(spec, "2004-12-29 00:01:00", "2004-12-29 0:02:00"); CheckCorrect(spec, "2004-02-28 15:59:59", "2004-02-29 0:00:00"); CheckCorrect(spec, "2003-02-28 15:59:59", "2003-03-29 0:00:00"); CheckCorrect(spec, "2005-02-27 15:59:59", "2005-03-29 0:00:00"); // Try 4:00 every day spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.HOURS, 16); spec.AddValue(ScheduleUnit.MINUTES, 0); CheckCorrect(spec, "2004-10-01 15:59:59", "2004-10-01 16:00:00"); CheckCorrect(spec, "2004-10-01 00:00:00", "2004-10-01 16:00:00"); CheckCorrect(spec, "2004-09-30 16:00:00", "2004-10-01 16:00:00"); CheckCorrect(spec, "2004-12-30 16:00:00", "2004-12-31 16:00:00"); CheckCorrect(spec, "2004-12-31 16:00:00", "2005-01-01 16:00:00"); // Try every weekday at 10 am - scrum time! spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.HOURS, 10); spec.AddValue(ScheduleUnit.MINUTES, 0); for (var i = 1; i <= 5; i++) { spec.AddValue(ScheduleUnit.DAYS_OF_WEEK, i); } CheckCorrect(spec, "2004-12-05 09:50:59", "2004-12-06 10:00:00"); CheckCorrect(spec, "2004-12-06 09:59:59", "2004-12-06 10:00:00"); CheckCorrect(spec, "2004-12-07 09:50:00", "2004-12-07 10:00:00"); CheckCorrect(spec, "2004-12-08 09:00:00", "2004-12-08 10:00:00"); CheckCorrect(spec, "2004-12-09 08:00:00", "2004-12-09 10:00:00"); CheckCorrect(spec, "2004-12-10 09:50:50", "2004-12-10 10:00:00"); CheckCorrect(spec, "2004-12-11 00:00:00", "2004-12-13 10:00:00"); CheckCorrect(spec, "2004-12-12 09:00:50", "2004-12-13 10:00:00"); CheckCorrect(spec, "2004-12-13 09:50:50", "2004-12-13 10:00:00"); CheckCorrect(spec, "2004-12-13 10:00:00", "2004-12-14 10:00:00"); CheckCorrect(spec, "2004-12-13 10:00:01", "2004-12-14 10:00:00"); // Every Monday and also on the 1st and 15th of each month, at midnight // (tests the or between DAYS_OF_MONTH and DAYS_OF_WEEK) spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.DAYS_OF_MONTH, 1); spec.AddValue(ScheduleUnit.DAYS_OF_MONTH, 15); spec.AddValue(ScheduleUnit.HOURS, 0); spec.AddValue(ScheduleUnit.MINUTES, 0); spec.AddValue(ScheduleUnit.SECONDS, 0); spec.AddValue(ScheduleUnit.DAYS_OF_WEEK, 1); CheckCorrect(spec, "2004-12-05 09:50:59", "2004-12-06 00:00:00"); CheckCorrect(spec, "2004-12-06 00:00:00", "2004-12-13 00:00:00"); //CheckCorrect(spec, "2004-12-07 00:80:00", "2004-12-13 00:00:00"); CheckCorrect(spec, "2004-12-12 23:00:00", "2004-12-13 00:00:00"); CheckCorrect(spec, "2004-12-13 23:00:00", "2004-12-15 00:00:00"); CheckCorrect(spec, "2004-12-14 23:00:00", "2004-12-15 00:00:00"); CheckCorrect(spec, "2004-12-15 23:00:00", "2004-12-20 00:00:00"); CheckCorrect(spec, "2004-12-18 23:00:00", "2004-12-20 00:00:00"); CheckCorrect(spec, "2004-12-20 00:01:00", "2004-12-27 00:00:00"); CheckCorrect(spec, "2004-12-27 00:01:00", "2005-01-01 00:00:00"); CheckCorrect(spec, "2005-01-01 00:01:00", "2005-01-03 00:00:00"); CheckCorrect(spec, "2005-01-03 00:01:00", "2005-01-10 00:00:00"); CheckCorrect(spec, "2005-01-10 00:01:00", "2005-01-15 00:00:00"); CheckCorrect(spec, "2005-01-15 00:01:00", "2005-01-17 00:00:00"); CheckCorrect(spec, "2005-01-17 00:01:00", "2005-01-24 00:00:00"); CheckCorrect(spec, "2005-01-24 00:01:00", "2005-01-31 00:00:00"); CheckCorrect(spec, "2005-01-31 00:01:00", "2005-02-01 00:00:00"); // Every second month on every second weekday spec = new ScheduleSpec(); for (var i = 1; i <= 12; i += 2) { spec.AddValue(ScheduleUnit.MONTHS, i); } for (var i = 0; i <= 6; i += 2) // Adds Sunday, Tuesday, Thursday, Saturday { spec.AddValue(ScheduleUnit.DAYS_OF_WEEK, i); } CheckCorrect(spec, "2004-09-01 00:00:00", "2004-09-02 00:00:00"); // Sept 1 2004 is a Wednesday CheckCorrect(spec, "2004-09-02 00:00:00", "2004-09-02 00:01:00"); CheckCorrect(spec, "2004-09-02 23:59:00", "2004-09-04 00:00:00"); CheckCorrect(spec, "2004-09-04 23:59:00", "2004-09-05 00:00:00"); // Sept 5 2004 is a Sunday CheckCorrect(spec, "2004-09-05 23:57:00", "2004-09-05 23:58:00"); CheckCorrect(spec, "2004-09-05 23:58:00", "2004-09-05 23:59:00"); CheckCorrect(spec, "2004-09-05 23:59:00", "2004-09-07 00:00:00"); CheckCorrect(spec, "2004-09-30 23:58:00", "2004-09-30 23:59:00"); // Sept 30 in a Thursday CheckCorrect(spec, "2004-09-30 23:59:00", "2004-11-02 00:00:00"); // Every second month on every second weekday spec = new ScheduleSpec(); for (var i = 1; i <= 12; i += 2) { spec.AddValue(ScheduleUnit.MONTHS, i); } for (var i = 0; i <= 6; i += 2) // Adds Sunday, Tuesday, Thursday, Saturday { spec.AddValue(ScheduleUnit.DAYS_OF_WEEK, i); } CheckCorrect(spec, "2004-09-01 00:00:00", "2004-09-02 00:00:00"); // Sept 1 2004 is a Wednesday CheckCorrect(spec, "2004-09-02 00:00:00", "2004-09-02 00:01:00"); CheckCorrect(spec, "2004-09-02 23:59:00", "2004-09-04 00:00:00"); CheckCorrect(spec, "2004-09-04 23:59:00", "2004-09-05 00:00:00"); // Sept 5 2004 is a Sunday CheckCorrect(spec, "2004-09-05 23:57:00", "2004-09-05 23:58:00"); CheckCorrect(spec, "2004-09-05 23:58:00", "2004-09-05 23:59:00"); CheckCorrect(spec, "2004-09-05 23:59:00", "2004-09-07 00:00:00"); // Every 5 seconds, between 9am and until 4pm, all weekdays except Saturday and Sunday spec = new ScheduleSpec(); for (var i = 0; i <= 59; i += 5) { spec.AddValue(ScheduleUnit.SECONDS, i); } for (var i = 1; i <= 5; i++) { spec.AddValue(ScheduleUnit.DAYS_OF_WEEK, i); } for (var i = 9; i <= 15; i++) { spec.AddValue(ScheduleUnit.HOURS, i); } CheckCorrect(spec, "2004-12-12 20:00:00", "2004-12-13 09:00:00"); // Dec 12 2004 is a Sunday CheckCorrect(spec, "2004-12-13 09:00:01", "2004-12-13 09:00:05"); CheckCorrect(spec, "2004-12-13 09:00:05", "2004-12-13 09:00:10"); CheckCorrect(spec, "2004-12-13 09:00:11", "2004-12-13 09:00:15"); CheckCorrect(spec, "2004-12-13 09:00:15", "2004-12-13 09:00:20"); CheckCorrect(spec, "2004-12-13 09:00:24", "2004-12-13 09:00:25"); CheckCorrect(spec, "2004-12-13 15:59:50", "2004-12-13 15:59:55"); CheckCorrect(spec, "2004-12-13 15:59:55", "2004-12-14 09:00:00"); CheckCorrect(spec, "2004-12-14 12:27:35", "2004-12-14 12:27:40"); CheckCorrect(spec, "2004-12-14 12:29:55", "2004-12-14 12:30:00"); CheckCorrect(spec, "2004-12-17 00:03:00", "2004-12-17 09:00:00"); CheckCorrect(spec, "2004-12-17 15:59:50", "2004-12-17 15:59:55"); CheckCorrect(spec, "2004-12-17 15:59:55", "2004-12-20 09:00:00"); // Feb 14, 12pm spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.MONTHS, 2); spec.AddValue(ScheduleUnit.DAYS_OF_MONTH, 14); spec.AddValue(ScheduleUnit.HOURS, 12); spec.AddValue(ScheduleUnit.MINUTES, 0); CheckCorrect(spec, "2004-12-12 20:00:00", "2005-02-14 12:00:00"); CheckCorrect(spec, "2003-12-12 20:00:00", "2004-02-14 12:00:00"); CheckCorrect(spec, "2004-02-01 20:00:00", "2004-02-14 12:00:00"); // Dec 31, 23pm and 50 seconds (countdown) spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.MONTHS, 12); spec.AddValue(ScheduleUnit.DAYS_OF_MONTH, 31); spec.AddValue(ScheduleUnit.HOURS, 23); spec.AddValue(ScheduleUnit.MINUTES, 59); spec.AddValue(ScheduleUnit.SECONDS, 50); CheckCorrect(spec, "2004-12-12 20:00:00", "2004-12-31 23:59:50"); CheckCorrect(spec, "2004-12-31 23:59:55", "2005-12-31 23:59:50"); // CST timezone 7:00:00am spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.HOURS, 7); spec.AddValue(ScheduleUnit.MINUTES, 0); spec.AddValue(ScheduleUnit.SECONDS, 0); spec.OptionalTimeZone = "Central Standard Time"; CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-10:00", "2008-02-02 03:00:00.000GMT-10:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-9:00", "2008-02-02 04:00:00.000GMT-9:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-8:00", "2008-02-02 05:00:00.000GMT-8:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-7:00", "2008-02-02 06:00:00.000GMT-7:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-6:00", "2008-02-01 07:00:00.000GMT-6:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-5:00", "2008-02-01 08:00:00.000GMT-5:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-4:00", "2008-02-01 09:00:00.000GMT-4:00"); // EST timezone 7am, any minute spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.HOURS, 7); spec.AddValue(ScheduleUnit.SECONDS, 0); spec.OptionalTimeZone = "Eastern Standard Time"; CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-7:00", "2008-02-02 05:00:00.000GMT-7:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-6:00", "2008-02-01 06:01:00.000GMT-6:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-5:00", "2008-02-01 07:00:00.000GMT-5:00"); CheckCorrectWZone(spec, "2008-02-01 06:00:00.000GMT-4:00", "2008-02-01 08:00:00.000GMT-4:00"); }
private static long Compute( ScheduleSpec spec, long afterTimeInMillis, TimeZoneInfo timeZone, TimeAbacus timeAbacus) { long remainderMicros = -1; 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.CalendarSet(afterTimeInMillis, after); if (remainderMicros == -1) { remainderMicros = remainder; } var result = new ScheduleCalendar { Milliseconds = after.Millisecond }; ICollection <int> minutesSet = spec.UnitValues.Get(ScheduleUnit.MINUTES); ICollection <int> hoursSet = spec.UnitValues.Get(ScheduleUnit.HOURS); ICollection <int> monthsSet = spec.UnitValues.Get(ScheduleUnit.MONTHS); ICollection <int> secondsSet = null; bool isSecondsSpecified = false; if (spec.UnitValues.ContainsKey(ScheduleUnit.SECONDS)) { isSecondsSpecified = true; secondsSet = spec.UnitValues.Get(ScheduleUnit.SECONDS); } 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.CalendarGet(after, remainder); continue; } return(GetTime(result, after.Year, spec.OptionalTimeZone, timeZone, timeAbacus, remainder)); } }
/// <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 <Int32> daysOfMonthSet = spec.UnitValues.Get(ScheduleUnit.DAYS_OF_MONTH); ICollection <Int32> daysOfWeekSet = spec.UnitValues.Get(ScheduleUnit.DAYS_OF_WEEK); ICollection <Int32> secondsSet = spec.UnitValues.Get(ScheduleUnit.SECONDS); ICollection <Int32> minutesSet = spec.UnitValues.Get(ScheduleUnit.MINUTES); ICollection <Int32> 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); }
public void TestWaitAndSpecTogether() { var startDate = new DateTime(2004, 12, 9, 15, 27, 10, 500, DateTimeKind.Local); var startTime = startDate.UtcMillis(); _service.Time = startTime; // Add a specification var spec = new ScheduleSpec(); spec.AddValue(ScheduleUnit.MONTHS, 12); spec.AddValue(ScheduleUnit.DAYS_OF_MONTH, 9); spec.AddValue(ScheduleUnit.HOURS, 15); spec.AddValue(ScheduleUnit.MINUTES, 27); spec.AddValue(ScheduleUnit.SECONDS, 20); _service.Add(ScheduleComputeHelper.ComputeDeltaNextOccurance(spec, _service.Time, TimeZoneInfo.Local, TimeAbacusMilliseconds.INSTANCE), _callbacks[3], _slots[1][1]); spec.AddValue(ScheduleUnit.SECONDS, 15); _service.Add(ScheduleComputeHelper.ComputeDeltaNextOccurance(spec, _service.Time, TimeZoneInfo.Local, TimeAbacusMilliseconds.INSTANCE), _callbacks[4], _slots[2][0]); // Add some more callbacks _service.Add(5000, _callbacks[0], _slots[0][0]); _service.Add(10000, _callbacks[1], _slots[0][1]); _service.Add(15000, _callbacks[2], _slots[1][0]); // GetInstance send a times reflecting various seconds later and check who got a callback _service.Time = startTime + 1000; SupportScheduleCallback.SetCallbackOrderNum(0); EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 0, 0, 0, 0, 0 }); _service.Time = startTime + 2000; EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 0, 0, 0, 0, 0 }); _service.Time = startTime + 4000; EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 0, 0, 0, 0, 0 }); _service.Time = startTime + 5000; EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 1, 0, 0, 0, 2 }); _service.Time = startTime + 9000; EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 0, 0, 0, 0, 0 }); _service.Time = startTime + 10000; EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 0, 3, 0, 4, 0 }); _service.Time = startTime + 11000; EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 0, 0, 0, 0, 0 }); _service.Time = startTime + 15000; EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 0, 0, 5, 0, 0 }); _service.Time = startTime + int.MaxValue; EvaluateSchedule(); CheckCallbacks(_callbacks, new[] { 0, 0, 0, 0, 0 }); }