fix(core): Доработка методов расчета NextRun по алгоритму ЕСПП, добавлена проверка, что расчитанная дата новее чем refDate.
This commit is contained in:
@@ -70,23 +70,12 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
|
||||
var nextRun = referenceDate;
|
||||
|
||||
//var finalOffset = new TimeSpan(3, 0, 0) + (offset ?? TimeSpan.Zero);
|
||||
var finalOffset = offset ?? TimeSpan.Zero;
|
||||
|
||||
var offsetReferenceDate = referenceDate.ToOffset(finalOffset);
|
||||
|
||||
logger.LogDebug("Дата после добавления offset - {offsetReferenceDate}", offsetReferenceDate);
|
||||
|
||||
////---------
|
||||
//// Для рассчета по МСК времени, потому что в ЮТС может быть еще ВСК, а по МСК это уже ПНД
|
||||
//var offsetReferenceDate = referenceDate.ToOffset(new TimeSpan(3, 0, 0));
|
||||
////---------
|
||||
|
||||
//// может быть offset ,это работает когда смещение для УЗ ЕСПП и применение ЗО РГ
|
||||
//if (offset.HasValue)
|
||||
// offsetReferenceDate = offsetReferenceDate.Off(offsetReferenceDate.Offset + offset);
|
||||
// //offsetReferenceDate = offsetReferenceDate.Add(offset.Value);
|
||||
|
||||
|
||||
switch (esppSchedule.TypeSchedule.Id)
|
||||
{
|
||||
@@ -108,6 +97,9 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
case (int)EsppSchTypeScheduleEnum.Annually2:
|
||||
nextRun = GetNextDateAnnually2(esppSchedule.Values, offsetReferenceDate);
|
||||
break;
|
||||
default:
|
||||
logger.LogError("Неподдерживаемый тип расписания ЕСПП: {TypeId}", esppSchedule.TypeSchedule.Id);
|
||||
throw new NotSupportedException($"Тип расписания {esppSchedule.TypeSchedule.Id} не поддерживается.");
|
||||
}
|
||||
|
||||
logger.LogDebug("Рассчитанная дата, с учетом offset: {nextRun}", nextRun);
|
||||
@@ -139,6 +131,14 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
return esppSchedule;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Регулярно. Каждые 1 час, 2 часа, Каждые пол года...
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private DateTimeOffset GetNextDateRegularly(List<EsppScheduleValDto> values, DateTimeOffset referenceDate)
|
||||
{
|
||||
var regDict = new Dictionary<string, int>
|
||||
@@ -159,27 +159,63 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
{"Каждые полгода", 4380},
|
||||
{"Каждые 1,5 года", 13140},
|
||||
{"Каждые 3 года", 26280},
|
||||
|
||||
};
|
||||
|
||||
|
||||
if (!regDict.TryGetValue(values[0].Value.Value, out int regNum))
|
||||
var key = values[0].Value.Value;
|
||||
if (!regDict.TryGetValue(key, out int regMinutes))
|
||||
{
|
||||
logger.LogError("Неизвестное значение: {Value}", values[0].Value.Value);
|
||||
throw new ArgumentException($"Неизвестное значение: {values[0].Value.Value}");
|
||||
logger.LogError("Неизвестное значение: {Value}", key);
|
||||
throw new ArgumentException($"Неизвестное значение: {key}");
|
||||
}
|
||||
|
||||
#region Старая логика
|
||||
|
||||
//var calcDay = referenceDate;
|
||||
|
||||
////TODO: вот это повторяется от метода к методу
|
||||
////Если итоговая дата указывает на прошлое, то повторяем расчёт и уходим в рекурсию
|
||||
//if (calcDay < DateTimeOffset.UtcNow /* || calcDay <= referenceDate*/)
|
||||
// calcDay = GetNextDateRegularly(values, calcDay.AddHours(regMinutes));
|
||||
|
||||
//return calcDay;
|
||||
|
||||
#endregion
|
||||
|
||||
var nowInTargetZone = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
|
||||
var calcDay = referenceDate;
|
||||
|
||||
//TODO: вот это повторяется от метода к методу
|
||||
//Если итоговая дата указывает на прошлое, то повторяем расчёт и уходим в рекурсию
|
||||
if (calcDay < DateTimeOffset.UtcNow)
|
||||
calcDay = GetNextDateRegularly(values, calcDay.AddHours(regNum));
|
||||
// Если referenceDate далеко в прошлом, не шагаем по минутам в цикле.
|
||||
// Сразу вычисляем, сколько целых интервалов нужно прибавить, чтобы догнать текущее время.
|
||||
if (calcDay < nowInTargetZone)
|
||||
{
|
||||
double totalMinutesPast = (nowInTargetZone - calcDay).TotalMinutes;
|
||||
|
||||
// Считаем, сколько полных интервалов помещается в этот отрезок времени
|
||||
long intervalsToSkip = (long)Math.Ceiling(totalMinutesPast / regMinutes);
|
||||
|
||||
calcDay = calcDay.AddMinutes(intervalsToSkip * regMinutes);
|
||||
}
|
||||
|
||||
// Проверяем условия и делаем микро-шаги, если необходимо
|
||||
while (true)
|
||||
{
|
||||
if (calcDay >= referenceDate && calcDay >= nowInTargetZone)
|
||||
{
|
||||
return calcDay;
|
||||
}
|
||||
calcDay = calcDay.AddMinutes(regMinutes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Еженедельно, каждый понедельник, вторник, среду...
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private DateTimeOffset GetNextDateWeekly(List<EsppScheduleValDto> values, DateTimeOffset referenceDate)
|
||||
{
|
||||
if (!dwDict.TryGetValue(values[0].Value.Value, out int dwNum))
|
||||
@@ -188,71 +224,155 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
throw new ArgumentException($"Неизвестное значение: {values[0].Value.Value}");
|
||||
}
|
||||
|
||||
#region Старая логика
|
||||
//var calcDay = referenceDate;
|
||||
//if (calcDay < DateTimeOffset.UtcNow)
|
||||
//{
|
||||
// var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
// calcDay = new DateTimeOffset(
|
||||
// nowInTargetTz.Year,
|
||||
// nowInTargetTz.Month,
|
||||
// nowInTargetTz.Day,
|
||||
// referenceDate.Hour,
|
||||
// referenceDate.Minute,
|
||||
// referenceDate.Second,
|
||||
// referenceDate.Offset
|
||||
// );
|
||||
//}
|
||||
|
||||
|
||||
//if (((int)calcDay.DayOfWeek) != dwNum)
|
||||
// calcDay = OffsetToDayOfWeek(dwNum, calcDay);
|
||||
|
||||
////Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
//if (calcDay < DateTimeOffset.UtcNow || calcDay <= referenceDate)
|
||||
// calcDay = GetNextDateWeekly(values, calcDay.AddDays(7));//а вот тут что-то новенькое
|
||||
|
||||
//return calcDay;
|
||||
#endregion
|
||||
|
||||
var nowInTargetZone = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
|
||||
var calcDay = referenceDate;
|
||||
if (calcDay < DateTimeOffset.UtcNow)
|
||||
|
||||
// Сдвигаем дату вперед до нужного дня недели
|
||||
if ((int)calcDay.DayOfWeek != dwNum)
|
||||
{
|
||||
var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
calcDay = new DateTimeOffset(
|
||||
nowInTargetTz.Year,
|
||||
//DateTimeOffset.UtcNow.Year,
|
||||
nowInTargetTz.Month,
|
||||
//DateTimeOffset.UtcNow.Month,
|
||||
nowInTargetTz.Day,
|
||||
//DateTimeOffset.UtcNow.Day,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
);
|
||||
calcDay = OffsetToDayOfWeek(dwNum, calcDay);
|
||||
}
|
||||
|
||||
// Если получившаяся дата все еще глубоко в прошлом,
|
||||
// не шагаем неделями в цикле, а мгновенно прыгаем ближе к текущему времени математикой.
|
||||
if (calcDay < nowInTargetZone)
|
||||
{
|
||||
double totalDaysPast = (nowInTargetZone - calcDay).TotalDays;
|
||||
|
||||
if (((int)calcDay.DayOfWeek) != dwNum)
|
||||
calcDay = OffsetToDayOfWeek(dwNum, calcDay);
|
||||
// Вычисляем, сколько полных недель (по 7 дней) нужно прибавить
|
||||
long weeksToSkip = (long)Math.Ceiling(totalDaysPast / 7);
|
||||
|
||||
//TODO: вот это повторяется от метода к методу
|
||||
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
if (calcDay < DateTimeOffset.UtcNow)
|
||||
calcDay = GetNextDateWeekly(values, calcDay.AddDays(7));//а вот тут что-то новенькое
|
||||
calcDay = calcDay.AddDays(weeksToSkip * 7);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Условие выхода: дата в будущем относительно точки старта И текущего времени
|
||||
if (calcDay >= referenceDate && calcDay >= nowInTargetZone)
|
||||
{
|
||||
return calcDay;
|
||||
}
|
||||
|
||||
// Если условия не выполнены, делаем шаг ровно в одну неделю (7 дней)
|
||||
calcDay = calcDay.AddDays(7);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ежемесячно, 1, 2, 3... числа
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private DateTimeOffset GetNextDateMonthly(List<EsppScheduleValDto> values, DateTimeOffset referenceDate)
|
||||
{
|
||||
if (!int.TryParse(values[0].Value.Value, out int dayNum))
|
||||
if (!int.TryParse(values[0].Value.Value, out int targetDay))
|
||||
{
|
||||
logger.LogError("Неизвестное значение дня месяца: {Value}", values[0].Value.Value);
|
||||
throw new ArgumentException($"Неизвестное значение: {values[0].Value.Value}");
|
||||
}
|
||||
|
||||
var calcDay = new DateTimeOffset(referenceDate.Year, referenceDate.Month, dayNum, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
if (referenceDate < DateTimeOffset.UtcNow)
|
||||
#region старая логика, не учтено попадание например 31 числа на февраль. Нет сравнения что расчитанная дата новее или равна refDate
|
||||
//var calcDay = new DateTimeOffset(referenceDate.Year, referenceDate.Month, targetDay, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
//if (referenceDate < DateTimeOffset.UtcNow)
|
||||
//{
|
||||
// var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
// calcDay = new DateTimeOffset(
|
||||
// nowInTargetTz.Year,
|
||||
// nowInTargetTz.Month,
|
||||
// targetDay,
|
||||
// referenceDate.Hour,
|
||||
// referenceDate.Minute,
|
||||
// referenceDate.Second,
|
||||
// referenceDate.Offset
|
||||
// );
|
||||
//}
|
||||
|
||||
////Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
//if (calcDay < DateTimeOffset.UtcNow /*|| calcDay <= referenceDate*/)
|
||||
// calcDay = GetNextDateMonthly(values, calcDay.AddMonths(1));
|
||||
|
||||
//return calcDay;
|
||||
#endregion
|
||||
|
||||
// Начинаем проверку с текущего месяца и года переданной referenceDate
|
||||
var baseDate = referenceDate;
|
||||
int monthsToAdd = 0;
|
||||
|
||||
var nowInTargetZone = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
|
||||
while (true)
|
||||
{
|
||||
var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
calcDay = new DateTimeOffset(
|
||||
nowInTargetTz.Year,
|
||||
nowInTargetTz.Month,
|
||||
dayNum,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
);
|
||||
//calcDay = new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, dayNum, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
// Каждую итерацию мы отталкиваемся от исходной точки и прибавляем +1, +2, +3 месяца
|
||||
var nextMonthDate = baseDate.AddMonths(monthsToAdd);
|
||||
int year = nextMonthDate.Year;
|
||||
int month = nextMonthDate.Month;
|
||||
|
||||
// Если в этом конкретном месяце НЕТ нужного нам дня (например, 31 числа в апреле или 30 в феврале)
|
||||
if (targetDay > DateTime.DaysInMonth(year, month))
|
||||
{
|
||||
// Пропускаем этот месяц и идем на следующий круг цикла
|
||||
monthsToAdd++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Если день существует, безопасно собираем дату
|
||||
var calcDay = new DateTimeOffset(
|
||||
year, month, targetDay,
|
||||
referenceDate.Hour, referenceDate.Minute, referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
);
|
||||
|
||||
//TODO: вот это повторяется от метода к методу
|
||||
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
if (calcDay < DateTimeOffset.UtcNow)
|
||||
calcDay = GetNextDateMonthly(values, calcDay.AddMonths(1));
|
||||
|
||||
// Условие выхода: дата строго в будущем относительно referenceDate и текущего UTC-времени
|
||||
if (calcDay >= referenceDate && calcDay >= nowInTargetZone)
|
||||
{
|
||||
return calcDay;
|
||||
}
|
||||
|
||||
// Если дата правильная, но она в прошлом — переходим к следующему месяцу
|
||||
monthsToAdd++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ежемесячно-2, Первый понедельник, Третий вторник
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
private DateTimeOffset GetNextDateMonthly2(List<EsppScheduleValDto> values, DateTimeOffset referenceDate)
|
||||
{
|
||||
if (!orderDict.TryGetValue(values[0].Value.Value, out int orderNum))
|
||||
@@ -267,23 +387,65 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
throw new ArgumentException($"Неизвестное значение: {values[1].Value.Value}");
|
||||
}
|
||||
|
||||
#region Старая логика
|
||||
//var startDay = referenceDate < DateTimeOffset.UtcNow
|
||||
// ? new DateTimeOffset(DateTimeOffset.UtcNow.Year, DateTimeOffset.UtcNow.Month, 1, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset)
|
||||
// : new DateTimeOffset(referenceDate.Year, referenceDate.Month, 1, referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
// ? new DateTimeOffset(
|
||||
// DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset).Year,
|
||||
// DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset).Month,
|
||||
// 1,
|
||||
// referenceDate.Hour,
|
||||
// referenceDate.Minute,
|
||||
// referenceDate.Second,
|
||||
// referenceDate.Offset
|
||||
// )
|
||||
// : new DateTimeOffset(
|
||||
// referenceDate.Year,
|
||||
// referenceDate.Month,
|
||||
// 1,
|
||||
// referenceDate.Hour,
|
||||
// referenceDate.Minute,
|
||||
// referenceDate.Second,
|
||||
// referenceDate.Offset
|
||||
// );
|
||||
|
||||
var startDay = referenceDate < DateTimeOffset.UtcNow
|
||||
? new DateTimeOffset(
|
||||
DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset).Year,
|
||||
DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset).Month,
|
||||
1,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
)
|
||||
: new DateTimeOffset(
|
||||
referenceDate.Year,
|
||||
referenceDate.Month,
|
||||
//var daysList = FindAllDaysInMonth(dwNum, startDay);
|
||||
|
||||
//DateTimeOffset calcDay;
|
||||
//if (orderNum == 5) // Последний
|
||||
//{
|
||||
// calcDay = daysList.Last();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// if (orderNum > daysList.Count)
|
||||
// {
|
||||
// logger.LogError("Запрошенный порядковый день недели ({OrderNum}) не существует в месяце {Month}/{Year}. Найдено только {Count} дней.", orderNum, startDay.Month, startDay.Year, daysList.Count);
|
||||
// throw new InvalidOperationException($"Запрошенный порядковый день недели ({orderNum}) не существует в месяце {startDay.Month}/{startDay.Year}. Найдено только {daysList.Count} дней.");
|
||||
// }
|
||||
// else
|
||||
// calcDay = daysList[orderNum - 1];
|
||||
//}
|
||||
|
||||
//// Если дата <= текущей, переходим на следующий месяц
|
||||
//if (calcDay <= DateTimeOffset.UtcNow /*|| calcDay <= referenceDate*/)
|
||||
// return GetNextDateMonthly2(values, startDay.AddMonths(1));
|
||||
|
||||
//return calcDay;
|
||||
#endregion
|
||||
|
||||
var nowInTargetZone = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
|
||||
// Начинаем расчет с текущего месяца referenceDate
|
||||
int monthsToAdd = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var currentMonthDate = referenceDate.AddMonths(monthsToAdd);
|
||||
|
||||
// Безопасно формируем первое число для проверяемого месяца
|
||||
var startDayOfMonth = new DateTimeOffset(
|
||||
currentMonthDate.Year,
|
||||
currentMonthDate.Month,
|
||||
1,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
@@ -291,32 +453,47 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
referenceDate.Offset
|
||||
);
|
||||
|
||||
var daysList = FindAllDaysInMonth(dwNum, startDay);
|
||||
// Ищем все нужные дни недели в этом месяце
|
||||
var daysList = FindAllDaysInMonth(dwNum, startDayOfMonth);
|
||||
|
||||
DateTimeOffset calcDay;
|
||||
if (orderNum == 5) // Последний
|
||||
|
||||
if (orderNum == 5) // Последний день недели в месяце (например, последняя пятница)
|
||||
{
|
||||
calcDay = daysList.Last();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Умная защита: если в текущем месяце нет 5-го вторника,
|
||||
// мы не выкидываем ошибку приложения, а мягко идем искать в следующем месяце
|
||||
if (orderNum > daysList.Count)
|
||||
{
|
||||
logger.LogError("Запрошенный порядковый день недели ({OrderNum}) не существует в месяце {Month}/{Year}. Найдено только {Count} дней.", orderNum, startDay.Month, startDay.Year, daysList.Count);
|
||||
throw new InvalidOperationException($"Запрошенный порядковый день недели ({orderNum}) не существует в месяце {startDay.Month}/{startDay.Year}. Найдено только {daysList.Count} дней.");
|
||||
monthsToAdd++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
||||
calcDay = daysList[orderNum - 1];
|
||||
}
|
||||
|
||||
// Если дата <= текущей, переходим на следующий месяц
|
||||
if (calcDay <= DateTimeOffset.UtcNow)
|
||||
return GetNextDateMonthly2(values, startDay.AddMonths(1));
|
||||
|
||||
// 3. Условие выхода (точно такое же, как во всех предыдущих методах)
|
||||
if (calcDay >= referenceDate && calcDay >= nowInTargetZone)
|
||||
{
|
||||
return calcDay;
|
||||
}
|
||||
|
||||
// Если дата правильная, но она в прошлом — переходим к следующему календарному месяцу
|
||||
monthsToAdd++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ежегодно, Январь 17, Февраль 20
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private DateTimeOffset GetNextDateAnnually(List<EsppScheduleValDto> values, DateTimeOffset referenceDate)
|
||||
{
|
||||
|
||||
@@ -332,26 +509,73 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
throw new ArgumentException($"Неизвестное значение: {values[0].Value.Value}");
|
||||
}
|
||||
|
||||
//var year = (referenceDate.Year < DateTimeOffset.UtcNow.Year) ? DateTimeOffset.UtcNow.Year : referenceDate.Year;
|
||||
//сравниваем года в одном часовом поясе
|
||||
var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
var year = (referenceDate.Year < nowInTargetTz.Year)
|
||||
? nowInTargetTz.Year
|
||||
: referenceDate.Year;
|
||||
#region Старая логика
|
||||
////сравниваем года в одном часовом поясе
|
||||
//var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
//var year = (referenceDate.Year < nowInTargetTz.Year)
|
||||
// ? nowInTargetTz.Year
|
||||
// : referenceDate.Year;
|
||||
|
||||
//var calcDay = new DateTimeOffset(
|
||||
// year, monthNum, dayNum,
|
||||
// referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
|
||||
////Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
//if (calcDay < DateTimeOffset.UtcNow /*|| calcDay <= referenceDate*/)
|
||||
// calcDay = GetNextDateAnnually(values, calcDay.AddYears(1));
|
||||
|
||||
//return calcDay;
|
||||
#endregion
|
||||
|
||||
var nowInTargetZone = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
|
||||
// Начинаем расчет с года переданной referenceDate
|
||||
int yearsToAdd = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Вычисляем текущий проверяемый год относительно исходной referenceDate
|
||||
var currentYearDate = referenceDate.AddYears(yearsToAdd);
|
||||
int year = currentYearDate.Year;
|
||||
|
||||
// ЗАЩИТА: Если в этом году нет такого дня (например, 29 февраля в невисокосном году)
|
||||
if (dayNum > DateTime.DaysInMonth(year, monthNum))
|
||||
{
|
||||
// Просто пропускаем этот год и переходим к следующему
|
||||
yearsToAdd++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Если день существует, безопасно собираем дату
|
||||
var calcDay = new DateTimeOffset(
|
||||
year, monthNum, dayNum,
|
||||
referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
|
||||
//TODO: вот это повторяется от метода к методу
|
||||
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
if (calcDay < DateTimeOffset.UtcNow)
|
||||
calcDay = GetNextDateAnnually(values, calcDay.AddYears(1));
|
||||
year,
|
||||
monthNum,
|
||||
dayNum,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
);
|
||||
|
||||
// Проверяем условия выхода (абсолютно одинаково для всех методов)
|
||||
if (calcDay >= referenceDate && calcDay >= nowInTargetZone)
|
||||
{
|
||||
return calcDay;
|
||||
}
|
||||
|
||||
// Если дата в прошлом — переходим на следующий год
|
||||
yearsToAdd++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Ежегодно-2, Первый понедельник января
|
||||
/// </summary>
|
||||
/// <param name="values"></param>
|
||||
/// <param name="referenceDate"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
private DateTimeOffset GetNextDateAnnually2(List<EsppScheduleValDto> values, DateTimeOffset referenceDate)
|
||||
{
|
||||
var monthADict = new Dictionary<string, int>
|
||||
@@ -386,36 +610,92 @@ namespace PARR.Core.Services.NextRunServices.Subservices
|
||||
throw new ArgumentException($"Неизвестное значение: {values[1].Value.Value}");
|
||||
}
|
||||
|
||||
//var year = (referenceDate.Year < DateTimeOffset.UtcNow.Year) ? DateTimeOffset.UtcNow.Year : referenceDate.Year;
|
||||
// сравниваем года в одном часовом поясе
|
||||
var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
var year = (referenceDate.Year < nowInTargetTz.Year)
|
||||
? nowInTargetTz.Year
|
||||
: referenceDate.Year;
|
||||
|
||||
//if (year == DateTimeOffset.UtcNow.Year && referenceDate.Month < DateTimeOffset.UtcNow.Month)
|
||||
if (year == nowInTargetTz.Year && referenceDate.Month < nowInTargetTz.Month)
|
||||
year += 1;
|
||||
#region Старая логика
|
||||
|
||||
var calcDay = new DateTimeOffset(
|
||||
year, monthNum, 1,
|
||||
referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
//// сравниваем года в одном часовом поясе
|
||||
//var nowInTargetTz = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
//var year = (referenceDate.Year < nowInTargetTz.Year)
|
||||
// ? nowInTargetTz.Year
|
||||
// : referenceDate.Year;
|
||||
|
||||
List<DateTimeOffset> daysList = FindAllDaysInMonth(dwNum, calcDay);
|
||||
////if (year == DateTimeOffset.UtcNow.Year && referenceDate.Month < DateTimeOffset.UtcNow.Month)
|
||||
//if (year == nowInTargetTz.Year && referenceDate.Month < nowInTargetTz.Month)
|
||||
// year += 1;
|
||||
|
||||
if (orderNum == 5)//5-последний
|
||||
//var calcDay = new DateTimeOffset(
|
||||
// year, monthNum, 1,
|
||||
// referenceDate.Hour, referenceDate.Minute, referenceDate.Second, referenceDate.Offset);
|
||||
|
||||
//List<DateTimeOffset> daysList = FindAllDaysInMonth(dwNum, calcDay);
|
||||
|
||||
//if (orderNum == 5)//5-последний
|
||||
// calcDay = daysList.Last();
|
||||
//else
|
||||
// calcDay = daysList[orderNum - 1];
|
||||
|
||||
////Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
//if (calcDay < DateTimeOffset.UtcNow /*|| calcDay <= referenceDate*/)
|
||||
// calcDay = GetNextDateAnnually2(values, calcDay.AddYears(1));
|
||||
|
||||
//return calcDay;
|
||||
|
||||
#endregion
|
||||
|
||||
var nowInTargetZone = DateTimeOffset.UtcNow.ToOffset(referenceDate.Offset);
|
||||
|
||||
// Начинаем расчет с года переданной referenceDate
|
||||
int yearsToAdd = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Сдвигаем проверяемый год относительно исходной даты
|
||||
var currentYearDate = referenceDate.AddYears(yearsToAdd);
|
||||
int year = currentYearDate.Year;
|
||||
|
||||
// Формируем первое число целевого месяца для проверяемого года
|
||||
var startDayOfMonth = new DateTimeOffset(
|
||||
year,
|
||||
monthNum,
|
||||
1,
|
||||
referenceDate.Hour,
|
||||
referenceDate.Minute,
|
||||
referenceDate.Second,
|
||||
referenceDate.Offset
|
||||
);
|
||||
|
||||
// Ищем все нужные дни недели в этом месяце
|
||||
var daysList = FindAllDaysInMonth(dwNum, startDayOfMonth);
|
||||
|
||||
DateTimeOffset calcDay;
|
||||
|
||||
if (orderNum == 5) // 5 означает "Последний день недели в месяце"
|
||||
{
|
||||
calcDay = daysList.Last();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Умная защита: если в этом году в этом месяце НЕТ 5-го выбранного дня недели
|
||||
if (orderNum > daysList.Count)
|
||||
{
|
||||
// Мы не падаем с ошибкой, а просто переходим к проверке следующего года
|
||||
yearsToAdd++;
|
||||
continue;
|
||||
}
|
||||
|
||||
calcDay = daysList[orderNum - 1];
|
||||
}
|
||||
|
||||
//TODO: вот это повторяется от метода к методу
|
||||
//Если итоговая дата указывает на прошлое, то повторяем расчёт и ухоим в рекурсию
|
||||
if (calcDay < DateTimeOffset.UtcNow)
|
||||
calcDay = GetNextDateAnnually2(values, calcDay.AddYears(1));
|
||||
|
||||
if (calcDay >= referenceDate && calcDay >= nowInTargetZone)
|
||||
{
|
||||
return calcDay;
|
||||
}
|
||||
|
||||
// Если вычисленная дата в прошлом — переходим на следующий год
|
||||
yearsToAdd++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<DateTimeOffset> FindAllDaysInMonth(int dwNum, DateTimeOffset startDay)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user