|
2016-12-14
| ||
| 19:35 | • Closed ticket [69cf6e5dc8]: Round trip of DateTime is sometimes 1 ms off with JulianDay Format plus 3 other changes artifact: 47bc940277 user: mistachkin | |
| 19:31 | When converting a Julian Day value to an integer, round to the nearest millisecond first. Pursuant to [69cf6e5dc8]. check-in: f154173793 user: mistachkin tags: trunk | |
|
2016-12-06
| ||
| 01:51 | • Pending ticket [69cf6e5dc8]: Round trip of DateTime is sometimes 1 ms off with JulianDay Format plus 5 other changes artifact: d2627941e6 user: mistachkin | |
| 01:49 | Candidate fix for ticket [69cf6e5dc8]. closed check-in: 471c16c617 user: mistachkin tags: tkt-69cf6e5dc8 | |
|
2016-12-05
| ||
| 22:47 | Add test case for ticket [69cf6e5dc8]. check-in: e2e6a0c7f1 user: mistachkin tags: tkt-69cf6e5dc8 | |
| 22:42 | • Verified ticket [69cf6e5dc8]: Round trip of DateTime is sometimes 1 ms off with JulianDay Format plus 5 other changes artifact: fc61509492 user: mistachkin | |
|
2016-11-27
| ||
| 19:52 | • Ticket [69cf6e5dc8]: 3 changes artifact: bdd90ec035 user: anonymous | |
| 01:00 | • Ticket [69cf6e5dc8]: 6 changes artifact: 72ef236a0d user: mistachkin | |
|
2016-11-23
| ||
| 22:37 | • New ticket [69cf6e5dc8]. artifact: 9dc6980cc2 user: anonymous | |
| Ticket Hash: | 69cf6e5dc86ba3bcc8b670baf48a9e87c49215f7 | ||
| Title: | Round trip of DateTime is sometimes 1 ms off with JulianDay Format | ||
| Status: | Closed | Type: | Code_Defect |
| Severity: | Minor | Priority: | Medium |
| Subsystem: | Convert | Resolution: | Fixed |
| Last Modified: |
2016-12-14 19:35:40 9.34 years ago |
Created: |
2016-11-23 22:37:45 9.39 years ago |
| Version Found In: | 1.0.103.0 | ||
| User Comments: | ||||
anonymous added on 2016-11-23 22:37:45:
Assume a SQLiteConnection with a connection string of "data source=test.sqlite;datetimeformat=JulianDay;datetimekind=Utc" on Win 10 and .NET FW 4.
Inserting a row to a table with a DateTime-Parameter for a column writes this DateTime as JulianDay-Double into the column.
Reading this column of that row back may result in a DateTime value which is sometimes 1ms less then the original value. An example DateTime is 2016-11-20 00:46:03.000 (or any other date with the same time of day).
The reason:
1) Not every (finite decimal) time of day fraction can be stored on a (binary finite) floating point value. In such cases some of the available (binary finite) neighbours are used to approximate the time of day fraction.
2) DateTime values are stored and *recreated* only with millisecond precision in SQLiteConvert
Here the code-example from a C# console project:
public static void copyPaste()
{
SQLiteConnection.CreateFile("test.sqlite");
using (var lConn = new SQLiteConnection("data source = test.sqlite;datetimeformat = JulianDay;datetimekind = Utc")) {
lConn.Open();
using (var lCreateCmd = lConn.CreateCommand())
{
lCreateCmd.CommandText = @"CREATE TABLE [Test] (dt)";
lCreateCmd.ExecuteNonQuery();
}
using (var lInsertCmd = lConn.CreateCommand())
{
lInsertCmd.CommandText = @"INSERT INTO [Test](dt) VALUES(@dt)";
var dt = lInsertCmd.CreateParameter();
dt.ParameterName = "dt";
dt.Value = new DateTime(2016, 11, 20, 00, 46, 03, 000, DateTimeKind.Utc);
lInsertCmd.Parameters.Add(dt);
lInsertCmd.ExecuteNonQuery();
}
using (var lSelectCmd = lConn.CreateCommand())
{
lSelectCmd.CommandText = @"SELECT dt FROM [Test]";
using (var lReader = lSelectCmd.ExecuteReader())
{
while (lReader.Read())
{
Console.WriteLine("Value: {0:yyyy-MM-dd HH:mm:ss.ffff}", lReader.GetDateTime(0));
}
}
}
}
}
But:
As long as the Julian Day Number is not too large, the JulianDay-Double can store values to a precision of at least 1/10 msec (100usec). Therefore it is possible to restore the original DateTime value with millisecond precision using the sub-ms fraction of the JulianDay-Double.
The proposed solution System.Data.SQLite.SQLiteConvert.cs (row 259):
private static long DoubleToJd(
double julianDay
)
{
return (long)Math.Round(julianDay * 86400000.0);
}
mistachkin added on 2016-11-27 01:00:46:
So, using Math.Round will result in the desired behavior? I'll need to spend some time understanding the corner cases impacted by this change. anonymous added on 2016-11-27 19:52:32:
As long as SQLiteConvert only store a DateTime value with millisecond precision (with the julian day conversion, see SQLiteConvert.computeJD) the conversion back to a DateTime should yield the original value. The following methods in SQLiteConvert are of interest for this case: long DoubleToJd(double julianDay) double JdToDouble(long jd) long computeJD(DateTime dateTime) C# Interactive > // Julian-Day-Number of 2016-11-27 * 86400000 + 00:46:03.000 in msec since 00:00 > long sjd = 212347010763000; > long ljd = (long)((sjd / 86400000.0) * 86400000.0); > Console.WriteLine(ljd) 212347010762999 mistachkin added on 2016-12-05 22:42:23:
I was able to reproduce the issue locally. mistachkin added on 2016-12-06 01:51:00:
Fixed on branch via check-in [471c16c6171c9fee]. mistachkin added on 2016-12-14 19:35:40:
Fix merged to trunk via check-in [f154173793f81c6f]. | ||||