< Summary

Information
Class: Services.OzonReturnsServices.ReturnsIncrementalWindow
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/OzonReturnsServices/ReturnsIncrementalWindow.cs
Line coverage
100%
Covered lines: 9
Uncovered lines: 0
Coverable lines: 9
Total lines: 48
Line coverage: 100%
Branch coverage
100%
Covered branches: 8
Total branches: 8
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
LocalToUtc(...)100%11100%
Resolve(...)100%88100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/OzonReturnsServices/ReturnsIncrementalWindow.cs

#LineLine coverage
 1namespace Services.OzonReturnsServices;
 2
 3/// <summary>
 4/// Расчёт нижней границы периода при инкрементальной загрузке возвратов.
 5/// Вынесен отдельно от джоба: это чистая арифметика дат, и именно в ней легче всего ошибиться.
 6/// </summary>
 7public static class ReturnsIncrementalWindow
 8{
 9    /// <summary>
 10    /// Перехлёст: Ozon проставляет visual_status_change_moment на своей стороне и отдаёт возврат
 11    /// в выдаче не мгновенно, поэтому от watermark отступаем назад, чтобы не потерять записи,
 12    /// появившиеся «задним числом» относительно прошлого прогона.
 13    /// </summary>
 114    public static readonly TimeSpan DefaultOverlap = TimeSpan.FromHours(2);
 15
 16    /// <summary>
 17    /// Календарная дата по местному времени -> момент в UTC.
 18    ///
 19    /// Пользователь на странице и cron задают даты в местном часовом поясе, а Ozon фильтрует по UTC.
 20    /// Раньше локальное значение просто помечалось как UTC через SpecifyKind, из-за чего окно
 21    /// уезжало на величину смещения: для широкого окна это безобидно, но инкрементальный шаг
 22    /// с двухчасовым перехлёстом такой сдвиг уже способен обнулить.
 23    /// </summary>
 24    public static DateTime LocalToUtc(DateTime local) =>
 325        DateTime.SpecifyKind(local, DateTimeKind.Local).ToUniversalTime();
 26
 27    /// <summary>
 28    /// Нижняя граница для клиента: <paramref name="watermark"/> минус перехлёст, но не раньше
 29    /// <paramref name="windowStart"/>. Без watermark (возвратов ещё нет) читаем окно целиком.
 30    /// </summary>
 31    /// <param name="watermark">Максимальный VisualStatusChangeMoment среди сохранённых возвратов клиента.</param>
 32    /// <param name="windowStart">Начало страховочного окна (now - monthsCount).</param>
 33    /// <param name="overlap">Перехлёст; по умолчанию <see cref="DefaultOverlap"/>.</param>
 34    public static DateTime Resolve(DateTime? watermark, DateTime windowStart, TimeSpan? overlap = null)
 35    {
 736        if (!watermark.HasValue)
 137            return windowStart;
 38
 39        // Ozon отдаёт момент в UTC, но значение, прочитанное из MariaDB, приходит с Kind=Unspecified —
 40        // фильтр запроса собирается как UTC, поэтому приводим явно.
 641        var watermarkUtc = watermark.Value.Kind == DateTimeKind.Local
 642            ? watermark.Value.ToUniversalTime()
 643            : DateTime.SpecifyKind(watermark.Value, DateTimeKind.Utc);
 44
 645        var candidate = watermarkUtc - (overlap ?? DefaultOverlap);
 646        return candidate > windowStart ? candidate : windowStart;
 47    }
 48}