Files
parr_api/PARR.API/Helpers/TimeZoneDateHelper.cs

97 lines
4.5 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Linq.Expressions;
namespace PARR.API.Helpers
{
/// <summary>
/// Хелпер для работы с датами в разных часовых поясах (UTC - пользователь). Используется для формирования СТАТИСТИКИ
/// </summary>
public static class TimeZoneDateHelper
{
/// <summary>
/// Создаёт начало дня (00:00:00) в часовом поясе пользователя и конвертирует в UTC
/// </summary>
/// <param name="userDate"></param>
/// <param name="userOffset"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Создаёт полуоткрытый интервал [start; end) в UTC для фильтрации за период (end не включается)
/// </summary>
/// <param name="userStartDate"></param>
/// <param name="userEndDateExclusive"></param>
/// <param name="userOffset"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Применяет фильтр по дате к IQueryable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="query"></param>
/// <param name="dateSelector"></param>
/// <param name="startUtc"></param>
/// <param name="endUtc"></param>
/// <returns></returns>
public static IQueryable<T> FilterByDateRangeUtc<T>(this IQueryable<T> query, Expression<Func<T, DateTimeOffset>> 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<Func<T, bool>>(body, param);
// Применяем к запросу
return query.Where(lambda);
}
/// <summary>
/// Группирует записи по дате в часовом поясе пользователя (для загруженных записей)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="dateSelector"></param>
/// <param name="userOffset"></param>
/// <returns></returns>
public static Dictionary<DateOnly, int> GroupByUserDate<T>(this IEnumerable<T> source, Func<T, DateTimeOffset> dateSelector, TimeSpan userOffset)
{
return source.GroupBy(t => DateOnly.FromDateTime(dateSelector(t).ToOffset(userOffset).DateTime))
.ToDictionary(g => g.Key, g => g.Count());
}
}
}