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-Leaf 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 | ||
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]. |