Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQLite: Translate TimeSpan members #18844

Open
Tracked by #22950
bricelam opened this issue Nov 11, 2019 · 3 comments
Open
Tracked by #22950

SQLite: Translate TimeSpan members #18844

bricelam opened this issue Nov 11, 2019 · 3 comments
Labels
area-query area-sqlite punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-enhancement
Milestone

Comments

@bricelam
Copy link
Contributor

bricelam commented Nov 11, 2019

We can enable these by registering two UDFs on the connection:

CreateFunction("ef_days", (TimeSpan value) => value.TotalDays);
CreateFunction("ef_timespan", (double value) => TimeSpan.FromDays(value));

The following translations are enabled.

.NET SQL
timeSpan1 + timeSpan2 ef_timespan(ef_days($timeSpan1) + ef_days($timeSpan2))
timeSpan1 - timeSpan2 ef_timespan(ef_days($timeSpan1) - ef_days($timeSpan2))
timeSpan1 / timeSpan2 ef_days($timeSpan1) / ef_days($timeSpan2)
timeSpan / d ef_timespan(ef_days($timeSpan) / $d)
timeSpan1 > timeSpan2 ef_days($timeSpan1) > ef_days($timeSpan2)
timeSpan1 >= timeSpan2 ef_days($timeSpan1) >= ef_days($timeSpan2)
timeSpan1 < timeSpan2 ef_days($timeSpan1) < ef_days($timeSpan2)
timeSpan1 <= timeSpan2 ef_days($timeSpan1) <= ef_days($timeSpan2)
d * timeSpan ef_timespan($d * ef_days($timeSpan))
timeSpan * d ef_timespan(ef_days($timeSpan) * $d)
-timeSpan ef_timespan(-ef_days($timeSpan))
dateTime + timeSpan datetime(julianday($dateTime) + ef_days($timeSpan))
dateTime - timeSpan datetime(julianday($dateTime) - ef_days($timeSpan))
dateTime1 - dateTime2 ef_timespan(julianday($dateTime1) - julianday($dateTime2))
timeSpan.Days CAST(ef_days($timeSpan) AS INTEGER)
timeSpan.Hours ef_days($timeSpan) * 24 % 24
timeSpan.Milliseconds ef_days(%timeSpan) * 86400000 % 1000
timeSpan.Minutes ef_days($timeSpan) * 1440 % 60
timeSpan.Seconds ef_days($timeSpan) * 86400 % 60
timeSpan.Ticks CAST(ef_days($timeSpan) * 864000000000 AS INTEGER)
timeSpan.TotalDays ef_days($timeSpan)
timeSpan.TotalHours ef_days($timeSpan) * 24
timeSpan.TotalMilliseconds ef_days(%timeSpan) * 86400000
timeSpan.TotalMinutes ef_days($timeSpan) * 1440
timeSpan.TotalSeconds ef_days($timeSpan) * 86400
timeSpan.Duration() ef_timespan(abs(ef_days($timeSpan)))
timeSpan.Fromef_days(value) ef_timespan($value)
TimeSpan.FromHours(value) ef_timespan($value / 24)
TimeSpan.FromMilliseconds(value) ef_timespan($value / 86400000)
TimeSpan.FromMinutes(value) ef_timespan($value / 1440)
TimeSpan.FromSeconds(value) ef_timespan($value / 86400)
TimeSpan.FromTicks(value) ef_timespan($value / 864000000000)
Max(t => t.TimeSpan) ef_timespan(max(ef_days(t.TimeSpan)))
Min(t => t.TimeSpan) ef_timespan(min(ef_days(t.TimeSpan)))

Notes:

  • Most .NET operators have equivalent methods to translate too
  • datetime() should actually be translated as rtrim(rtrim(strftime('%Y-%m-%d %H:%M:%f'), '0'), '.')
  • julianday(datetime(text, modifiers)) can reduce to julianday(text, modifiers)
  • julianday(datetime(real)) can reduce to real
  • ef_days(ef_timespan(real)) can reduce to real
@ajcvickers ajcvickers added this to the Backlog milestone Nov 11, 2019
@bricelam bricelam added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Jan 22, 2020
@bricelam bricelam self-assigned this Jan 22, 2020
@bricelam
Copy link
Contributor Author

bricelam commented Jul 16, 2020

I made some good progress in bricelam:timespan, but I ran into floating-point precision issues and wanted to re-examine how we handle TimeSpan and DateTime literals and parameters on SQLite before moving forward.

Assert.Equal() Failure
Expected: 00:01:00
Actual:   00:00:59.9999964

In order to get this to round to the correct time we need to re-create the TimeSpan (and possibly DateTime) instances in various places using a constructor overload that doesn't take double.

@bricelam bricelam removed the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Sep 11, 2020
@bricelam
Copy link
Contributor Author

We could also consider adding a collation.

.NET SQL
OrderBy(t => t.TimeSpan) ORDER BY t.TimeSpan COLLATE EF_TIMESPAN
OrderByDescending(t => t.TimeSpan) ORDER BY t.TimeSpan COLLATE EF_TIMESPAN DESC

@ajcvickers ajcvickers added punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. and removed propose-punt labels Apr 23, 2022
@bricelam
Copy link
Contributor Author

bricelam commented May 3, 2023

Note, we can use the mod function from #18843 (which works with non-integer values) to simplify some translations (including the one for DateTime.Millisecond) which should also avoid overflows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-query area-sqlite punted-for-7.0 Originally planned for the EF Core 7.0 (EF7) release, but moved out due to resource constraints. type-enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants