Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,16 @@ SqlExpression Upper()
private SqlExpression? TranslateDateTime(SqlExpression instance, MemberInfo member)
=> member.Name switch
{
"Year" or "Years" => GetDatePartExpression(instance, "year"),
// PostgreSQL has no year zero, whereas NodaTime uses astronomical year numbering.
"Year" when GetDatePartExpression(instance, "year") is var year
=> _sqlExpressionFactory.Case(
[
new CaseWhenClause(
_sqlExpressionFactory.LessThan(year, _sqlExpressionFactory.Constant(0)),
_sqlExpressionFactory.Add(year, _sqlExpressionFactory.Constant(1)))
],
year),
"Years" => GetDatePartExpression(instance, "year"),
"Month" or "Months" => GetDatePartExpression(instance, "month"),
"DayOfYear" => GetDatePartExpression(instance, "doy"),
"Day" or "Days" => GetDatePartExpression(instance, "day"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,48 @@ await AssertQuery(
"""
SELECT n."Id", n."DateInterval", n."Duration", n."Instant", n."InstantRange", n."Interval", n."LocalDate", n."LocalDate2", n."LocalDateRange", n."LocalDateTime", n."LocalTime", n."Long", n."OffsetTime", n."Period", n."TimeZoneId", n."ZonedDateTime"
FROM "NodaTimeTypes" AS n
WHERE date_part('year', n."LocalDateTime")::int = 2018
WHERE CASE
WHEN date_part('year', n."LocalDateTime")::int < 0 THEN date_part('year', n."LocalDateTime")::int + 1
ELSE date_part('year', n."LocalDateTime")::int
END = 2018
""");
}

[ConditionalTheory]
[InlineData(false, -100)]
[InlineData(true, -100)]
[InlineData(false, 0)]
[InlineData(true, 0)]
[InlineData(false, 1)]
[InlineData(true, 1)]
public async Task Year_astronomical(bool async, int year)
{
await using var context = CreateContext();
await context.Database.CreateExecutionStrategy().ExecuteAsync(
async () =>
{
await using var transaction = await context.Database.BeginTransactionAsync();

var entity = await context.NodaTimeTypes.SingleAsync();
entity.LocalDateTime = new LocalDateTime(year, 12, 31, 12, 0);
await context.SaveChangesAsync();
Fixture.TestSqlLoggerFactory.Clear();

var query = context.NodaTimeTypes.Select(t => t.LocalDateTime.Year);
Assert.Equal(year, async ? await query.SingleAsync() : query.Single());

AssertSql(
"""
SELECT CASE
WHEN date_part('year', n."LocalDateTime")::int < 0 THEN date_part('year', n."LocalDateTime")::int + 1
ELSE date_part('year', n."LocalDateTime")::int
END
FROM "NodaTimeTypes" AS n
LIMIT 2
""");
});
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task Month(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,48 @@ await AssertQuery(
"""
SELECT n."Id", n."DateInterval", n."Duration", n."Instant", n."InstantRange", n."Interval", n."LocalDate", n."LocalDate2", n."LocalDateRange", n."LocalDateTime", n."LocalTime", n."Long", n."OffsetTime", n."Period", n."TimeZoneId", n."ZonedDateTime"
FROM "NodaTimeTypes" AS n
WHERE date_part('year', n."LocalDate")::int = 2018
WHERE CASE
WHEN date_part('year', n."LocalDate")::int < 0 THEN date_part('year', n."LocalDate")::int + 1
ELSE date_part('year', n."LocalDate")::int
END = 2018
""");
}

[ConditionalTheory]
[InlineData(false, -100)]
[InlineData(true, -100)]
[InlineData(false, 0)]
[InlineData(true, 0)]
[InlineData(false, 1)]
[InlineData(true, 1)]
public async Task Year_astronomical(bool async, int year)
{
await using var context = CreateContext();
await context.Database.CreateExecutionStrategy().ExecuteAsync(
async () =>
{
await using var transaction = await context.Database.BeginTransactionAsync();

var entity = await context.NodaTimeTypes.SingleAsync();
entity.LocalDate = new LocalDate(year, 12, 31);
await context.SaveChangesAsync();
Fixture.TestSqlLoggerFactory.Clear();

var query = context.NodaTimeTypes.Select(t => t.LocalDate.Year);
Assert.Equal(year, async ? await query.SingleAsync() : query.Single());

AssertSql(
"""
SELECT CASE
WHEN date_part('year', n."LocalDate")::int < 0 THEN date_part('year', n."LocalDate")::int + 1
ELSE date_part('year', n."LocalDate")::int
END
FROM "NodaTimeTypes" AS n
LIMIT 2
""");
});
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task Month(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ WHERE date_part('year', n."Period")::int = 2018
""");
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task Years_negative(bool async)
{
await AssertQuery(
async,
ss => ss.Set<NodaTimeTypes>().Select(t => Period.FromYears(-t.Id).Years));

AssertSql(
"""
SELECT date_part('year', make_interval(years => -n."Id"))::int
FROM "NodaTimeTypes" AS n
""");
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task Months(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,58 @@ await AssertQuery(
"""
SELECT n."Id", n."DateInterval", n."Duration", n."Instant", n."InstantRange", n."Interval", n."LocalDate", n."LocalDate2", n."LocalDateRange", n."LocalDateTime", n."LocalTime", n."Long", n."OffsetTime", n."Period", n."TimeZoneId", n."ZonedDateTime"
FROM "NodaTimeTypes" AS n
WHERE date_part('year', n."ZonedDateTime" AT TIME ZONE 'UTC')::int = 2018
WHERE CASE
WHEN date_part('year', n."ZonedDateTime" AT TIME ZONE 'UTC')::int < 0 THEN date_part('year', n."ZonedDateTime" AT TIME ZONE 'UTC')::int + 1
ELSE date_part('year', n."ZonedDateTime" AT TIME ZONE 'UTC')::int
END = 2018
""");
}

[ConditionalTheory]
[InlineData(false, -100)]
[InlineData(true, -100)]
[InlineData(false, 0)]
[InlineData(true, 0)]
[InlineData(false, 1)]
[InlineData(true, 1)]
public async Task Year_astronomical(bool async, int year)
{
await using var context = CreateContext();
await context.Database.CreateExecutionStrategy().ExecuteAsync(
async () =>
{
await using var transaction = await context.Database.BeginTransactionAsync();

var entity = await context.NodaTimeTypes.SingleAsync();
entity.ZonedDateTime = new LocalDateTime(year, 12, 31, 12, 0).InUtc();
await context.SaveChangesAsync();
Fixture.TestSqlLoggerFactory.Clear();

var query = context.NodaTimeTypes.Select(
t => new
{
t.ZonedDateTime.Year,
YearInZone = t.ZonedDateTime.ToInstant().InZone(DateTimeZoneProviders.Tzdb["Europe/Berlin"]).Year
});
var result = async ? await query.SingleAsync() : query.Single();
Assert.Equal(year, result.Year);
Assert.Equal(entity.ZonedDateTime.WithZone(DateTimeZoneProviders.Tzdb["Europe/Berlin"]).Year, result.YearInZone);

AssertSql(
"""
SELECT CASE
WHEN date_part('year', n."ZonedDateTime" AT TIME ZONE 'UTC')::int < 0 THEN date_part('year', n."ZonedDateTime" AT TIME ZONE 'UTC')::int + 1
ELSE date_part('year', n."ZonedDateTime" AT TIME ZONE 'UTC')::int
END AS "Year", CASE
WHEN date_part('year', n."ZonedDateTime" AT TIME ZONE 'Europe/Berlin')::int < 0 THEN date_part('year', n."ZonedDateTime" AT TIME ZONE 'Europe/Berlin')::int + 1
ELSE date_part('year', n."ZonedDateTime" AT TIME ZONE 'Europe/Berlin')::int
END AS "YearInZone"
FROM "NodaTimeTypes" AS n
LIMIT 2
""");
});
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task Month(bool async)
Expand Down
Loading