Skip to content
Merged
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
23 changes: 22 additions & 1 deletion net/DevExtreme.AspNet.Data.Tests/DataSourceLoaderTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using DevExtreme.AspNet.Data.ResponseModel;
using DevExtreme.AspNet.Data.Helpers;
using DevExtreme.AspNet.Data.ResponseModel;
using System;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -56,6 +57,26 @@ public void Load_RequireTotalCount() {
Assert.Equal(new[] { 3, 4 }, result.data.Cast<int>());
}

class DateTimeOffsetTestDto {
public int Id;
public DateTimeOffset At;
}

[Fact]
public void Load_FilterByDateTimeOffset_RoundTripsThroughRemoteQueryString() {
var items = new[] {
new DateTimeOffsetTestDto { Id = 1, At = new DateTimeOffset(2024, 6, 1, 4, 30, 0, TimeSpan.Zero) },
new DateTimeOffsetTestDto { Id = 2, At = new DateTimeOffset(2024, 6, 2, 4, 30, 0, TimeSpan.Zero) }
};

var options = new SampleLoadOptions {
Filter = new[] { "At", "=", "2024-06-01T10:00:00+05:30" }
};
var result = DataSourceLoader.Load(items, options);

Assert.Equal(new[] { 1 }, result.data.Cast<DateTimeOffsetTestDto>().Select(i => i.Id));
}

[Fact]
public void Load_GroupOnly() {
var data = new[] {
Expand Down
11 changes: 11 additions & 0 deletions net/DevExtreme.AspNet.Data.Tests/DynamicBindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,17 @@ public void T819075() {
}).totalCount);
}

[Fact]
public void T819075_DateTimeOffset() {
dynamic sourceItem = new ExpandoObject();
sourceItem.p = new DateTimeOffset(2024, 6, 1, 4, 30, 0, TimeSpan.Zero);

Assert.Equal(1, DataSourceLoader.Load(new[] { sourceItem }, new SampleLoadOptions {
Filter = new[] { "p", "2024-06-01T10:00:00+05:30" },
RequireTotalCount = true
}).totalCount);
}

[Fact]
public void Issue413() {
// https://github.com/DevExpress/DevExtreme.AspNet.Data/issues/413#issuecomment-580766581
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,28 @@ public void InvalidValueHandling() {
Assert.Equal("False", compiler.Compile(new[] { "byte", "-3" }).Body.ToString());
Assert.Equal("False", compiler.Compile(new[] { "byte", "257" }).Body.ToString());
Assert.Equal("False", compiler.Compile(new[] { "int", "not-int" }).Body.ToString());
Assert.Equal("False", compiler.Compile(new[] { "dateTimeOffset", "not-a-date" }).Body.ToString());
}

[Fact]
public void DateTimeOffset_DifferentOffset_MatchesSameInstant() {
var obj = new Structs {
dateTimeOffset = new DateTimeOffset(2024, 6, 1, 4, 30, 0, TimeSpan.Zero)
};

// Same instant, expressed with a client-side offset that differs from the stored value's offset.
AssertEvaluation(obj, "dateTimeOffset", "=", "2024-06-01T10:00:00+05:30");
AssertEvaluation(obj, "dateTimeOffset", "<=", "2024-06-01T10:00:00+05:30");
AssertEvaluation(obj, "dateTimeOffset", ">=", "2024-06-01T10:00:00+05:30");
}

[Fact]
public void NullableDateTimeOffset_DifferentOffset_MatchesSameInstant() {
var obj = new NullableStructs {
dateTimeOffset = new DateTimeOffset(2024, 6, 1, 4, 30, 0, TimeSpan.Zero)
};

AssertEvaluation(obj, "dateTimeOffset", "=", "2024-06-01T10:00:00+05:30");
}


Expand Down
45 changes: 44 additions & 1 deletion net/DevExtreme.AspNet.Data.Tests/UtilsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,49 @@ public void ConvertClientValue_Numeric() {
Assert.Equal(123L, output);
}

}
[Fact]
public void ConvertClientValue_DateTimeOffset_PreservesExplicitOffset() {
var output = Utils.ConvertClientValue("2024-06-01T10:00:00+05:30", typeof(DateTimeOffset));

Assert.IsType<DateTimeOffset>(output);
Assert.Equal(new DateTimeOffset(2024, 6, 1, 10, 0, 0, TimeSpan.FromHours(5.5)), output);
Assert.Equal(TimeSpan.FromHours(5.5), ((DateTimeOffset)output).Offset);
}

[Fact]
public void ConvertClientValue_DateTimeOffset_Utc() {
var output = Utils.ConvertClientValue("2024-06-01T04:30:00Z", typeof(DateTimeOffset));

Assert.Equal(new DateTimeOffset(2024, 6, 1, 4, 30, 0, TimeSpan.Zero), output);
}

[Fact]
public void ConvertClientValue_DateTimeOffset_DifferentOffsets_SameInstantAreEqual() {
var fromClient = (DateTimeOffset)Utils.ConvertClientValue("2024-06-01T10:00:00+05:30", typeof(DateTimeOffset));
var utcEquivalent = new DateTimeOffset(2024, 6, 1, 4, 30, 0, TimeSpan.Zero);

Assert.Equal(utcEquivalent, fromClient);
}

[Fact]
public void ConvertClientValue_NullableDateTimeOffset_FromString() {
var output = Utils.ConvertClientValue("2024-06-01T10:00:00+05:30", typeof(DateTimeOffset?));

Assert.Equal(new DateTimeOffset(2024, 6, 1, 10, 0, 0, TimeSpan.FromHours(5.5)), output);
}

[Fact]
public void ConvertClientValue_DateTimeOffset_FromDateTime_UsesLocalOffset() {
// Compatibility path: Newtonsoft-based callers may already have parsed the value into a DateTime.
var date = new DateTime(2024, 6, 1, 10, 0, 0);
var output = Utils.ConvertClientValue(date, typeof(DateTimeOffset));

Assert.Equal(new DateTimeOffset(date), output);
}

[Fact]
public void ConvertClientValue_DateTimeOffset_InvalidString_Throws() {
Assert.ThrowsAny<FormatException>(() => Utils.ConvertClientValue("not-a-date", typeof(DateTimeOffset)));
}
}
}
3 changes: 3 additions & 0 deletions net/DevExtreme.AspNet.Data/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ public static object ConvertClientValue(object value, Type type) {
if(type == typeof(DateTime) && value is String)
return DateTime.Parse((string)value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);

if(type == typeof(DateTimeOffset) && value is String)
return DateTimeOffset.Parse((string)value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);

if(type == typeof(DateTimeOffset) && value is DateTime date)
return new DateTimeOffset(date);

Expand Down
1 change: 1 addition & 0 deletions net/Sample/Controllers/NorthwindController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public async Task<IActionResult> Orders(DataSourceLoadOptions loadOptions) {
//----------------------------------------
o.OrderDateOnly,
o.OrderTimeOnly,
o.OrderDateTimeOffset,
//----------------------------------------
o.Freight,
o.ShipCountry,
Expand Down
2 changes: 2 additions & 0 deletions net/Sample/Models/Order.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public Order() {

public TimeOnly? OrderTimeOnly { get; set; }

public DateTimeOffset? OrderDateTimeOffset { get; set; }

//----------------------------------------

[Column(TypeName = "datetime")]
Expand Down
17 changes: 15 additions & 2 deletions net/Sample/SQL.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,23 @@ ALTER TABLE [Orders]
ADD [OrderTimeOnly] time NULL
GO
UPDATE [Orders] SET [OrderTimeOnly] = CONVERT(time, DATEADD(hour, ([OrderID] + 1), [OrderDate]))
GO
ALTER TABLE [Orders]
ADD [OrderDateTimeOffset] datetimeoffset NULL
GO
-- (-14:00..+14:00)
-- IF EXISTS (
-- SELECT 1
-- FROM [Orders]
-- WHERE ([OrderID] - 10247) NOT BETWEEN -840 AND 840
-- )
-- THROW 50000, 'OrderDateTimeOffset offset minutes must be between -840 and 840.', 1
-- GO
UPDATE [Orders] SET [OrderDateTimeOffset] = TODATETIMEOFFSET([OrderDate], ([OrderID] - 10247))

---

dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet package add Microsoft.EntityFrameworkCore.Tools --project Sample
dotnet package add Microsoft.EntityFrameworkCore.SqlServer --project Sample

Scaffold-DbContext "Data Source=(localdb)\MSSQLLocalDB; Database=Northwind; Integrated Security=True; MultipleActiveResultSets=True; App=EntityFramework" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
8 changes: 8 additions & 0 deletions net/Sample/Views/Home/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
//groupInterval: "quarter" // N|A
}
},
{
dataField: "orderDateTimeOffset",
dataType: "date",
headerFilter: {
groupInterval: "quarter"
}
},

{
dataField: "freight",
headerFilter: {
Expand Down
Loading