Skip to content

Commit

Permalink
SqlServer: Implement DateDiffWeek
Browse files Browse the repository at this point in the history
Resolves #17170
  • Loading branch information
ralmsdeveloper authored and smitpatel committed Sep 9, 2019
1 parent d658254 commit fdab156
Show file tree
Hide file tree
Showing 3 changed files with 240 additions and 0 deletions.
92 changes: 92 additions & 0 deletions src/EFCore.SqlServer/Extensions/SqlServerDbFunctionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Globalization;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.SqlServer.Internal;

Expand Down Expand Up @@ -880,6 +881,97 @@ public static int DateDiffNanosecond(
? (int?)DateDiffNanosecond(_, startTimeSpan.Value, endTimeSpan.Value)
: null;

/// <summary>
/// Counts the number of week boundaries crossed between the startDate and endDate.
/// Corresponds to SQL Server's DATEDIFF(WEEK,startDate,endDate).
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="startDate">Starting date for the calculation.</param>
/// <param name="endDate">Ending date for the calculation.</param>
/// <returns>Number of week boundaries crossed between the dates.</returns>
public static int DateDiffWeek(
[CanBeNull] this DbFunctions _,
DateTime startDate,
DateTime endDate)
{
checked
{
var days = (endDate.Date - startDate.Date).Days;
var weeks = (int)days / 7;
var remainingDays = days % 7;

if (remainingDays > 0)
{
var calendar = CultureInfo.InvariantCulture.Calendar;

var first = calendar
.GetWeekOfYear(
startDate,
CalendarWeekRule.FirstFullWeek,
DayOfWeek.Sunday);

var second = calendar
.GetWeekOfYear(
startDate.AddDays(remainingDays),
CalendarWeekRule.FirstFullWeek,
DayOfWeek.Sunday);

if (first != second)
{
weeks++;
}
}

return weeks;
}
}

/// <summary>
/// Counts the number of week boundaries crossed between the startDate and endDate.
/// Corresponds to SQL Server's DATEDIFF(WEEK,startDate,endDate).
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="startDate">Starting date for the calculation.</param>
/// <param name="endDate">Ending date for the calculation.</param>
/// <returns>Number of week boundaries crossed between the dates.</returns>
public static int? DateDiffWeek(
[CanBeNull] this DbFunctions _,
DateTime? startDate,
DateTime? endDate)
=> (startDate.HasValue && endDate.HasValue)
? (int?)DateDiffWeek(_, startDate.Value, endDate.Value)
: null;

/// <summary>
/// Counts the number of week boundaries crossed between the startDate and endDate.
/// Corresponds to SQL Server's DATEDIFF(WEEK,startDate,endDate).
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="startDate">Starting date for the calculation.</param>
/// <param name="endDate">Ending date for the calculation.</param>
/// <returns>Number of week boundaries crossed between the dates.</returns>
public static int DateDiffWeek(
[CanBeNull] this DbFunctions _,
DateTimeOffset startDate,
DateTimeOffset endDate)
=> DateDiffWeek(_, startDate.UtcDateTime, endDate.UtcDateTime);

/// <summary>
/// Counts the number of week boundaries crossed between the startDate and endDate.
/// Corresponds to SQL Server's DATEDIFF(WEEK,startDate,endDate).
/// </summary>
/// <param name="_">The DbFunctions instance.</param>
/// <param name="startDate">Starting date for the calculation.</param>
/// <param name="endDate">Ending date for the calculation.</param>
/// <returns>Number of week boundaries crossed between the dates.</returns>
public static int? DateDiffWeek(
[CanBeNull] this DbFunctions _,
DateTimeOffset? startDate,
DateTimeOffset? endDate)
=> (startDate.HasValue && endDate.HasValue)
? (int?)DateDiffWeek(_, startDate.Value, endDate.Value)
: null;

/// <summary>
/// Validate if the given string is a valid date.
/// Corresponds to the SQL Server's ISDATE('date').
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,30 @@ private readonly Dictionary<MethodInfo, string> _methodInfoDateDiffMapping
nameof(SqlServerDbFunctionsExtensions.DateDiffNanosecond),
new[] { typeof(DbFunctions), typeof(TimeSpan?), typeof(TimeSpan?) }),
"NANOSECOND"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffWeek),
new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }),
"WEEK"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffWeek),
new[] { typeof(DbFunctions), typeof(DateTime?), typeof(DateTime?) }),
"WEEK"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffWeek),
new[] { typeof(DbFunctions), typeof(DateTimeOffset), typeof(DateTimeOffset) }),
"WEEK"
},
{
typeof(SqlServerDbFunctionsExtensions).GetRuntimeMethod(
nameof(SqlServerDbFunctionsExtensions.DateDiffWeek),
new[] { typeof(DbFunctions), typeof(DateTimeOffset?), typeof(DateTimeOffset?) }),
"WEEK"
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,130 @@ FROM [Orders] AS [o]
}
}

[ConditionalFact]
public virtual void DateDiff_Week_datetime()
{
using (var context = CreateContext())
{
var count = context.Orders
.Count(c => EF.Functions.DateDiffWeek(
c.OrderDate,
new DateTime(1998, 5, 6, 0, 0, 0)) == 5);

Assert.Equal(16, count);

AssertSql(
@"SELECT COUNT(*)
FROM [Orders] AS [o]
WHERE (DATEDIFF(WEEK, [o].[OrderDate], '1998-05-06T00:00:00.000') = 5) AND DATEDIFF(WEEK, [o].[OrderDate], '1998-05-06T00:00:00.000') IS NOT NULL");
}
}

[ConditionalFact]
public virtual void DateDiff_Week_datetimeoffset()
{
using (var context = CreateContext())
{
var count = context.Orders
.Count(c => EF.Functions.DateDiffWeek(
c.OrderDate,
new DateTimeOffset(1998, 5, 6, 0, 0, 0, TimeSpan.Zero)) == 5);

Assert.Equal(16, count);

AssertSql(
@"SELECT COUNT(*)
FROM [Orders] AS [o]
WHERE (DATEDIFF(WEEK, CAST([o].[OrderDate] AS datetimeoffset), '1998-05-06T00:00:00.0000000+00:00') = 5) AND DATEDIFF(WEEK, CAST([o].[OrderDate] AS datetimeoffset), '1998-05-06T00:00:00.0000000+00:00') IS NOT NULL");
}
}

[ConditionalFact]
public virtual void DateDiff_Week_parameters_null()
{
using (var context = CreateContext())
{
var count = context.Orders
.Count(c => EF.Functions.DateDiffWeek(
null,
new DateTimeOffset(1998, 5, 6, 0, 0, 0, TimeSpan.Zero)) == 5);

Assert.Equal(0, count);

AssertSql(
@"@__p_0='False'
SELECT COUNT(*)
FROM [Orders] AS [o]
WHERE @__p_0 = CAST(1 AS bit)");
}
}

[ConditionalFact]
public virtual void DateDiff_Week_server_vs_client_eval_datetime()
{
using (var context = CreateContext())
{
var endDate = new DateTime(1998, 5, 6, 0, 0, 0);

var orders = context.Orders
.OrderBy(p => p.OrderID)
.Take(200)
.Select(c => new
{
Weeks = EF.Functions.DateDiffWeek(c.OrderDate, endDate),
c.OrderDate
});

foreach (var order in orders)
{
var weeks = EF.Functions.DateDiffWeek(order.OrderDate, endDate);

Assert.Equal(weeks, order.Weeks);
}

AssertSql(
@"@__p_0='200'
@__endDate_2='1998-05-06T00:00:00' (Nullable = true) (DbType = DateTime)
SELECT TOP(@__p_0) DATEDIFF(WEEK, [o].[OrderDate], @__endDate_2) AS [Weeks], [o].[OrderDate]
FROM [Orders] AS [o]
ORDER BY [o].[OrderID]");
}
}

[ConditionalFact]
public virtual void DateDiff_Week_server_vs_client_eval_datetimeoffset()
{
using (var context = CreateContext())
{
var endDate = new DateTimeOffset(1998, 5, 6, 0, 0, 0, TimeSpan.Zero);

var orders = context.Orders
.OrderBy(p => p.OrderID)
.Take(200)
.Select(c => new
{
Weeks = EF.Functions.DateDiffWeek(c.OrderDate, endDate),
c.OrderDate
});

foreach (var order in orders)
{
var weeks = EF.Functions.DateDiffWeek(order.OrderDate, endDate);

Assert.Equal(weeks, order.Weeks);
}

AssertSql(
@"@__p_0='200'
@__endDate_2='1998-05-06T00:00:00.0000000+00:00' (Nullable = true)
SELECT TOP(@__p_0) DATEDIFF(WEEK, CAST([o].[OrderDate] AS datetimeoffset), @__endDate_2) AS [Weeks], [o].[OrderDate]
FROM [Orders] AS [o]
ORDER BY [o].[OrderID]");
}
}
[ConditionalFact]
public virtual void IsDate_not_valid()
{
Expand Down

0 comments on commit fdab156

Please sign in to comment.