Efcore: SQLite : Datetime equality comparison not working (in memory)

Created on 5 Jul 2017  路  21Comments  路  Source: dotnet/efcore

When comparing exact dates in memory with the sqlite provider, it returns no result:
```C#
var targets = (from i in context.Whatever
where i.Date == myDate
select new { i.Id }).ToList();

targets.Count is zero. 
But this works and returns one item:
```C#
 var targets =  (from i in context.Whatever
                        where i.Date.CompareTo(myDate)==0
                        select new { i.Id  }).ToList();

I use Microsoft.EntityFrameworkCore.Sqlite 1.1.2.

closed-question

All 21 comments

Probably the second query is being evaluated on client. There seem to be some mismatch between the format of how values being stored and how parameters are being created.

This may be fixed in latest dev due to self-contained type mapping.

Was the data inserted using EF Core? If not, it may be in an incompatible format.

No, the data was inserted by reading a sql script before like this:
await context.Database.ExecuteSqlCommandAsync(strCommand);

The date is inserted this way : '2017-08-19 00:00' . When I query the item without the equal clause on the date, and then I looked at my item specifically, the date is correctly resolved by EF in memory, the day is 19, month 8, year 2017 and the kind is unspecified.
When I compare the ticks it is working fine, like the CompareTo method, but as Smitpatel said, it might be because it is resolved in memory too..

DateTime maps to TEXT in SQLite.
EF uses following format to convert date into text @"{0:yyyy\-MM\-dd HH\:mm\:ss.FFFFFFF}";
It is likely that if EF is applying any operation on datetime on server, then it would generate literal/parameter in above format. But if the stored values are in different format it will not match (text matching). Anytime the operation is done on client, it will work because we would convert text value to datetime using parse function. Since compareTo or ticks are not translated to server they work.

Ok, I tried setting in the sql script '2017-08-19 00:00:00.000000' but it's still not finding it when comparing through ==.. Am I doing it wrong ?

Yep, this is by design. You need to normalize coerced values (e.g. Guid, DateTime, TimeSpan, etc.) for equality comparisons to work with EF.

Hmm, manually updating the value should have worked...

No this is not working, I thought I missed a zero at first, but here's the new test I made to be sure:

 using (var context = new Context(options))
 {
     var date = new DateTime(2017, 08, 21, 0, 0, 0);//script.sql format : '2017-08-21 00:00:00.0000000'
     var result= context.Whatever.FirstOrDefault(w=> w.Date == date); //result null                
     var result2= context.Whatever.ToList().FirstOrDefault(w=> w.Date == date); //result2 ok
  }

Maybe I'm missing something...

Ah, .FFFFFFF won't pad with 0s. You need to specify just 2017-08-21 00:00:00 in the SQL script.

Now the question is: Do we want to switch to padding in 2.0?

The built-in SQLite function strftime('%Y-%m-%d %H:%M:%f', ...) does pad with up to three 0s. (e.g. 2017-08-21 00:00:00.000)

On the other hand, the built-in function datetime(...) doesn't use fractional seconds, so keeping it like it is probably makes the values easier to work with overall.

Thank you, it's working with the 2017-08-21 00:00:00 format, the == comparison retrieves the item now.

@bricelam - Is there anything actionable on our side here? or should we close it as question?

I've thought about it, and I think the format we have is probably ideal. If you don't have fractional seconds, the format is compatible with datetime(). If you do, the format is lossless.

Unless someone disagrees, we can close it.

It's fine for me as long as I know the right format for my scripts :) many thanks to you two !!

Why was text chosen instead of long for sqlite dates?

@jjxtra Precision and readability

@jjxtra Precision and readability

Precision I don't get, it still takes 8 bytes for a DateTime object in memory. Converting to text converts those 8 bytes to text and does not add any precision, just wasted chars, separators, etc.

My testing in #15019 showed that milliseconds would be lost (and that was just TimeSpan, not DateTime). You are free to add a value conversion if you want:

modelBuilder.Entity<MyEntity>().Property(e => e.DateTimeProperty)
    .HasConversion(ToJulianDay, FromJulianDay);

Ah, just noticed you asked about long, not double. Answer for that: SQLite only has helper functions for working with datetime values in text and Julian day floating-point values, not long/ticks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vijayantkatyal picture vijayantkatyal  路  321Comments

vsfeedback picture vsfeedback  路  98Comments

rowanmiller picture rowanmiller  路  101Comments

rowanmiller picture rowanmiller  路  112Comments

anpete picture anpete  路  100Comments