Вот так
Код: Выделить всё
SELECT
SUM("Amount")
FROM
public."Transactions"
GROUP BY
CAST("CreatedAt" AS date) AT TIME ZONE 'America/Chicago'
Я видел этот подход Мехди и пытался изменить его для PostgreSql.
Вот что у меня есть — построитель выражений:
Код: Выделить всё
public class AtTimeZoneExpression : SqlFunctionExpression
{
private readonly IReadOnlyCollection _params;
public AtTimeZoneExpression(IReadOnlyCollection parameters)
: base("notimportant", true, typeof(DateTimeOffset), RelationalTypeMapping.NullMapping)
{
_params = parameters;
}
protected override Expression Accept(ExpressionVisitor visitor)
{
if (visitor is not QuerySqlGenerator sqlGenerator)
return base.Accept(visitor);
if (_params.First().TypeMapping.DbType == System.Data.DbType.DateTimeOffset)
visitor.Visit(new SqlFragmentExpression("CAST( "));
visitor.Visit(_params.First());
if (_params.First().TypeMapping.DbType == System.Data.DbType.DateTimeOffset)
visitor.Visit(new SqlFragmentExpression(" as date)"));
visitor.Visit(new SqlFragmentExpression(" AT TIME ZONE "));
visitor.Visit(_params.Skip(1).First());
return this;
}
protected override void Print([NotNull] ExpressionPrinter expressionPrinter)
{
expressionPrinter.Append("AT TIME ZONE Expression");
}
}
Код: Выделить всё
public static class QueryHelper
{
public static DateTimeOffset ToTimeZone(this DateTimeOffset source, string timeZone)
{
var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
var date = TimeZoneInfo.ConvertTimeFromUtc(source.UtcDateTime, tz);
return new DateTimeOffset(date, tz.GetUtcOffset(date));
}
}
Код: Выделить всё
modelBuilder.HasDbFunction(typeof(QueryHelper).GetMethod(nameof(QueryHelper.ToTimeZone), new[] { typeof(DateTimeOffset), typeof(string) }))
.HasTranslation(args => new AtTimeZoneExpression(args));
Код: Выделить всё
var activityPerDay = await transactionQuery
.GroupBy(t => t.CreatedAt.ToTimeZone(timeZoneId).Date)
.Select(dayGroup => new ItemMetrics
{
DateTime = dayGroup.Key,
Volume = dayGroup.Sum(t => t.Amount ?? 0)
})
.ToListAsync(cancellationToken);
Вот что я получаю
Код: Выделить всё
t => t.CreatedAt.ToTimeZone(timeZoneId).Date
Код: Выделить всё
CreatedAt
System.ArgumentException: аргумент метки времени для AtTimeZone имел неизвестный тип хранилища NULL (параметр ' timestamp')
в Npgsql.EntityFrameworkCore.PostgreSQL.Query.NpgsqlSqlExpressionFactory.AtTimeZone(временная метка SqlExpression, SqlExpression timeZone, Type type, RelationalTypeMapping typeMapping)
в Npgsql. EntityFrameworkCore.PostgreSQL.Query.NpgsqlSqlExpressionFactory.AtUtc(временная метка SqlExpression, RelationalTypeMapping typeMapping)
at Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal.NpgsqlDateTimeMemberTranslator.TranslateDateTimeOffset(SqlExpression экземпляр, для участника)
at Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal.NpgsqlDateTimeMemberTranslator.Translate (экземпляр SqlExpression, член MemberInfo, тип returnType, IDiagnosticsLogger
Код: Выделить всё
1 logger) at Microsoft.EntityFrameworkCore.Query.RelationalMemberTranslatorProvider.c__DisplayClass6_0.b__0(IMemberTranslator t) at System.Linq.Enumerable.SelectEnumerableIterator
Я также пробовал добавлять NodaTime в контекст и использовать переводимые функции.
Код: Выделить всё
builder.Services.AddDbContext(options => options.UseNpgsql(
"",
o => o.UseNodaTime()));
Подробнее здесь: https://stackoverflow.com/questions/791 ... postgresql