Description
openedon May 26, 2022
Describe the bug
A UTC DateTime
is converted to a local DateTime
before being passed to custom JsonConverters
inside of ExpressionToSql.ApplyCustomConverters
.
To Reproduce
using Microsoft.Azure.Cosmos;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
const string account = "https://localhost:8081";
const string key = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
using var client = new CosmosClient(account, key);
var container = client.GetContainer("NotEven", "MakingARequest");
Console.WriteLine("******* Incorrect *******");
var now = DateTime.UtcNow.Date;
Console.WriteLine("Date: " + now);
Console.WriteLine("DateTimeKind: " + now.Kind);
Console.WriteLine("Query: " + container.GetItemLinqQueryable<MyDocument>() .Where(q => q.StartDate <= now));
Console.WriteLine();
Console.WriteLine("******* Correct *******");
now = DateTime.Now.Date;
Console.WriteLine("Date: " + now);
Console.WriteLine("DateTimeKind: " + now.Kind);
Console.WriteLine("Query: " + container.GetItemLinqQueryable<MyDocument>() .Where(q => q.StartDate <= now));
public class DateOnlyConverter : IsoDateTimeConverter
{
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
string text = null;
if (value is DateTime dateTime)
{
text = dateTime.ToString("yyyy-MM-dd");
}
writer.WriteValue(text);
}
}
class MyDocument
{
[JsonProperty("id")] public string Id { get; set; }
[JsonConverter(typeof(DateOnlyConverter))] public DateTime StartDate { get; set; }
}
Output:
******* Incorrect *******
Date: 5/26/2022 12:00:00 AM
DateTimeKind: Utc
Query: {"query":"SELECT VALUE root FROM root WHERE (root[\"StartDate\"] <= \"2022-05-25\")"}
******* Correct *******
Date: 5/26/2022 12:00:00 AM
DateTimeKind: Local
Query: {"query":"SELECT VALUE root FROM root WHERE (root[\"StartDate\"] <= \"2022-05-26\")"}
It works as expected for local DateTime
, but fails for UTC.
Expected behavior
The same value passed into the query is passed into the custom JsonConverter
.
Actual behavior
A UTC DateTime
is converted to Local DateTime
before being passed into the custom JsonConverter
.
Environment summary
SDK Version: 3.27.1 (and earlier)
OS Version (e.g. Windows, Linux, MacOSX): All
Additional context
This seems to be a side-effect of ExpressionToSql.VisitConstant
converting the value from it's .NET type to a string representation. Because of that, the string value must be parsed before passing to the JsonConverter
. It seems that it would be better to pass the value directly to the JsonConverter
inside of VisitConstant
, but that seems difficult to do since it would require including additional context (e.g., what property it is being used with to determine whether or not a custom converter is specified).