/// <summary> /// This method computes the end date when given the start date, a regular time step size, /// and the number of time steps that should be added to the start date. /// </summary> /// <param name="startDate">The start date for the calculation</param> /// <param name="unit">Code for the time step units (e.g. hour, day, month...)</param> /// <param name="stepSize">The number of units per time step (e.g. Size=10 for 10-day time steps)</param> /// <param name="numSteps">The number of time steps to add to the start date</param> /// <returns>The end date of the calculation</returns> public static DateTime IncrementDate(DateTime startDate, TimeStepUnitCode unit, short stepSize, int numSteps) { DateTime calcDate; // The end date of the calculation DateTime date = startDate; // The DateTime class's various 'Add' methods lend themselves to // a switch statement so that whichever units the time step is measured in, // we can call the corresponding 'Add' method. switch (unit) { case TimeStepUnitCode.Minute: calcDate = date.AddMinutes(stepSize * numSteps); break; case TimeStepUnitCode.Hour: calcDate = date.AddHours(stepSize * numSteps); break; case TimeStepUnitCode.Day: calcDate = date.AddDays(stepSize * numSteps); break; case TimeStepUnitCode.Week: calcDate = date.AddDays(stepSize * numSteps * 7); break; case TimeStepUnitCode.Month: // For now, we only handle monthly time steps that end on regular calendar month // See https://github.com/hydrologics/Oasis/issues/163 date = date.RoundMonthEnd(0); calcDate = date.AddMonthsByEnd(0, stepSize * numSteps); break; case TimeStepUnitCode.Year: // For now, we only handle yearly time steps that end on regular calendar year // See https://github.com/hydrologics/Oasis/issues/163 date = new DateTime(date.Year + 1, 1, 1).AddMinutes(-1); calcDate = date.AddYears(stepSize * numSteps); break; default: calcDate = date; break; } return(calcDate); }
/// <summary> /// This method fills values into the given array of date values, according to the /// regular time step size specified by the given time step unit and quantity. The /// array must have been allocated prior to calling this method, and the size of the /// array must be at least the given number of time steps to fill. /// </summary> /// <param name="timeStepUnit">code value for Minute,Hour,Day,Week,Month, Year, or Irregular</param> /// <param name="timeStepQuantity">The number of the given unit that defines the time step. /// For instance, if the time step is 6 hours long, then this value is 6.</param> /// <param name="nReqValues">the number of time steps requested to fill into the array</param> /// <param name="dateArray">the array of DateTime values that the method will fill</param> /// <param name="reqStartDate">the DateTime value of the first time step</param> public static void FillDateArray( TimeStepUnitCode timeStepUnit, short timeStepQuantity, int nReqValues, DateTime[] dateArray, DateTime reqStartDate) { // Element zero of the array comes directly from the input parameter dateArray[0] = reqStartDate; TimeSpan span; // We will loop through the length of the array and fill in the date values. However, the action // to be performed within the loop corresponds to the time step unit. In a previous version of // the code, this method simply called IncrementDate, which provided satisfying encapsulation // of code (avoiding duplication of effort). However, this method was found to be a performance // problem, so the code now attempts to optimize by minimizing the calculations that occur // within the loop. Where possible, it also calls the DateTime.Add method, using a TimeSpan // parameter, which is faster than calling AddDays or other methods. This means that the // method is noticeably slower for the Month and Year units, but this is mitigated by the // fact that usually there are fewer time steps in a monthly or yearly series. switch (timeStepUnit) { case TimeStepUnitCode.Minute: span = new TimeSpan(0, 0, timeStepQuantity, 0); for (int i = 1; i < nReqValues; i++) { dateArray[i] = dateArray[i - 1].Add(span); } break; case TimeStepUnitCode.Hour: span = new TimeSpan(0, timeStepQuantity, 0, 0); for (int i = 1; i < nReqValues; i++) { dateArray[i] = dateArray[i - 1].Add(span); } break; case TimeStepUnitCode.Day: span = new TimeSpan(timeStepQuantity, 0, 0, 0); for (int i = 1; i < nReqValues; i++) { dateArray[i] = dateArray[i - 1].Add(span); } break; case TimeStepUnitCode.Week: span = new TimeSpan(timeStepQuantity * 7, 0, 0, 0); for (int i = 1; i < nReqValues; i++) { dateArray[i] = dateArray[i - 1].Add(span); } break; case TimeStepUnitCode.Month: // For now, we do not handle monthly time steps that are shifted. That is, the // time steps must end on the end of the standard calendar months. Future work // should make it possible to create shifted months only once a *shift* parameter // is specified. See https://github.com/hydrologics/Oasis/issues/163 . For now, // the shift parameter is always assumed zero. dateArray[0] = RoundMonthEnd(reqStartDate, 0); for (int i = 1; i < nReqValues; i++) { dateArray[i] = dateArray[i - 1].AddMonthsByEnd(0, timeStepQuantity); } break; case TimeStepUnitCode.Year: // For now, we do not handle yearly time steps that are shifted. That is, the // time steps must end on the end of the standard calendar year. Future work // should make it possible to create shifted years only once a *shift* parameter // is specified. See https://github.com/hydrologics/Oasis/issues/163 . For now, // the shift parameter is always assumed zero. dateArray[0] = new DateTime(reqStartDate.Year, 12, 31); for (int i = 1; i < nReqValues; i++) { dateArray[i] = dateArray[i - 1].AddYears(timeStepQuantity); } break; default: for (int i = 1; i < nReqValues; i++) { dateArray[i] = dateArray[i - 1]; } break; } }
/// <summary> /// This method determines how many regular time steps are contained between the given /// start date and end date, when given a regular time step size. /// The method does not care whether the input start date comes before the input end /// date. It simply returns the absolute value of the number of steps between the given dates. /// Please note that the returned value is not inclusive of the first time step. That is, if the /// start date is 1/1 and the end date is 1/1, then the number of days between the given dates /// is returned as zero. /// </summary> /// <param name="startDate">The date when the count begins</param> /// <param name="endDate">The date when the count ends</param> /// <param name="unit">Code for the time step units (e.g. hour, day, month...)</param> /// <param name="stepSize">The number of units per time step (e.g. Size=10 for 10-day time steps)</param> /// <returns></returns> public static int CountSteps(DateTime startDate, DateTime endDate, TimeStepUnitCode unit, short stepSize) { DateTime calcDate; // Date value that will be iterated for the counting process int i = 0; // Counter for how many time steps // Method does not care whether the input start date comes before the input end // date. It simply returns the absolute value of the number of steps between the given dates. if (endDate < startDate) { calcDate = endDate; endDate = startDate; startDate = calcDate; } calcDate = startDate; // The DateTime class's various 'Add' methods lend themselves to // a switch statement so that whichever units the time step is measured in, // we can call the corresponding 'Add' method. The counting loop inside of // the switch statement is deliberately selected to be faster than a switch // statement inside of a loop. switch (unit) { case TimeStepUnitCode.Minute: for (i = 0; calcDate < endDate; i++) { calcDate = calcDate.AddMinutes(stepSize); } break; case TimeStepUnitCode.Hour: for (i = 0; calcDate < endDate; i++) { calcDate = calcDate.AddHours(stepSize); } break; case TimeStepUnitCode.Day: for (i = 0; calcDate < endDate; i++) { calcDate = calcDate.AddDays(stepSize); } break; case TimeStepUnitCode.Week: for (i = 0; calcDate < endDate; i++) { calcDate = calcDate.AddDays(stepSize * 7); } break; case TimeStepUnitCode.Month: // For now, we only handle monthly time steps that end on regular calendar month // See https://github.com/hydrologics/Oasis/issues/163 calcDate = RoundMonthEnd(calcDate, 0); for (i = 0; calcDate < endDate; i++) { calcDate = calcDate.AddMonthsByEnd(0, stepSize); } break; case TimeStepUnitCode.Year: // For now, we only handle yearly time steps that end on regular calendar year // See https://github.com/hydrologics/Oasis/issues/163 calcDate = new DateTime(calcDate.Year + 1, 1, 1).AddMinutes(-1); for (i = 0; calcDate < endDate; i++) { calcDate = calcDate.AddYears(stepSize); } break; } return(i); }