using System.Linq.Expressions;
namespace PARR.API.Helpers
{
///
/// Хелпер для работы с датами в разных часовых поясах (UTC - пользователь). Используется для формирования СТАТИСТИКИ
///
public static class TimeZoneDateHelper
{
///
/// Создаёт начало дня (00:00:00) в часовом поясе пользователя и конвертирует в UTC
///
///
///
///
public static DateTime GetUtcSartOfDay(DateOnly userDate, TimeSpan userOffset)
{
var userStart = new DateTimeOffset(userDate.Year, userDate.Month, userDate.Day,
0, 0, 0,
userOffset);
return userStart.UtcDateTime;
}
///
/// Создаёт полуоткрытый интервал [start; end) в UTC для фильтрации за период (end не включается)
///
///
///
///
///
public static (DateTime StartUtc, DateTime EndUtc) GetUtcDateRange(DateOnly userStartDate, DateOnly userEndDateExclusive, TimeSpan userOffset)
{
var start = new DateTimeOffset(
userStartDate.Year, userStartDate.Month, userStartDate.Day,
0, 0, 0, userOffset).UtcDateTime;
var end = new DateTimeOffset(
userEndDateExclusive.Year, userEndDateExclusive.Month, userEndDateExclusive.Day,
0, 0, 0, userOffset).UtcDateTime;
return (start, end);
}
///
/// Применяет фильтр по дате к IQueryable
///
///
///
///
///
///
///
public static IQueryable FilterByDateRangeUtc(this IQueryable query, Expression> dateSelector, DateTime startUtc, DateTime endUtc)
{
// Строим выражение: t => t.DateCreated.UtcDateTime >= start && < end
var param = dateSelector.Parameters[0];
var body = Expression.AndAlso(
// Строим левую часть: t.DateCreated.UtcDateTime >= startUtc
Expression.GreaterThanOrEqual(
Expression.Property(dateSelector.Body, nameof(DateTimeOffset.UtcDateTime)),
Expression.Constant(startUtc, typeof(DateTime))
),
// Строим правую часть: t.DateCreated.UtcDateTime < endUtc
Expression.LessThan(
Expression.Property(dateSelector.Body, nameof(DateTimeOffset.UtcDateTime)),
Expression.Constant(endUtc, typeof(DateTime))
)
);
// Собираем финальную лямбду: t => (t.DateCreated.UtcDateTime >= start && < end)
var lambda = Expression.Lambda>(body, param);
// Применяем к запросу
return query.Where(lambda);
}
///
/// Группирует записи по дате в часовом поясе пользователя (для загруженных записей)
///
///
///
///
///
///
public static Dictionary GroupByUserDate(this IEnumerable source, Func dateSelector, TimeSpan userOffset)
{
return source.GroupBy(t => DateOnly.FromDateTime(dateSelector(t).ToOffset(userOffset).DateTime))
.ToDictionary(g => g.Key, g => g.Count());
}
}
}