diff --git a/driver/driver_test.go b/driver/driver_test.go index b1fed7b..1995578 100644 --- a/driver/driver_test.go +++ b/driver/driver_test.go @@ -1,4 +1,3 @@ -// Package driver provides a database/sql driver for SQLite. package driver import ( diff --git a/sqlite3/amalg.c b/sqlite3/amalg.c index 96f1630..d942bf3 100644 --- a/sqlite3/amalg.c +++ b/sqlite3/amalg.c @@ -3,6 +3,8 @@ #include "main.c" #include "os.c" #include "qsort.c" +#include "time.c" + #include "sqlite3.c" sqlite3_destructor_type malloc_destructor = &free; diff --git a/sqlite3/time.c b/sqlite3/time.c new file mode 100644 index 0000000..a587963 --- /dev/null +++ b/sqlite3/time.c @@ -0,0 +1,29 @@ +#include + +#include "sqlite3.h" + +static int time_collation(void *pArg, int nKey1, const void *pKey1, int nKey2, + const void *pKey2) { + // If keys are of different length, and both terminated by a Z, + // ignore the Z for collation purposes. + if (nKey1 && nKey2 && nKey1 != nKey2) { + const char *pK1 = (const char *)pKey1; + const char *pK2 = (const char *)pKey2; + if (pK1[nKey1 - 1] == 'Z' && pK2[nKey2 - 1] == 'Z') { + nKey1--; + nKey2--; + } + } + + int n = nKey1 < nKey2 ? nKey1 : nKey2; + int rc = memcmp(pKey1, pKey2, n); + if (rc == 0) { + rc = nKey1 - nKey2; + } + return rc; +} + +int sqlite3_time_collation(sqlite3 *db) { + return sqlite3_create_collation_v2(db, "TIME", SQLITE_UTF8, 0, time_collation, + 0); +} \ No newline at end of file diff --git a/tests/time_test.go b/tests/time_test.go index 6b7eabb..9b8dff9 100644 --- a/tests/time_test.go +++ b/tests/time_test.go @@ -40,7 +40,7 @@ func TestTimeFormat_Decode(t *testing.T) { t.Parallel() reference := time.Date(2013, 10, 7, 4, 23, 19, 120_000_000, time.FixedZone("", -4*3600)) - reftime := time.Date(2000, 1, 1, 4, 23, 19, 120_000_000, time.FixedZone("", -4*3600)) + refnodate := time.Date(2000, 01, 1, 4, 23, 19, 120_000_000, time.FixedZone("", -4*3600)) tests := []struct { fmt sqlite3.TimeFormat @@ -89,14 +89,14 @@ func TestTimeFormat_Decode(t *testing.T) { {sqlite3.TimeFormatAuto, "1381134199120000", reference, 0, false}, {sqlite3.TimeFormatAuto, "1381134199120000000", reference, 0, false}, {sqlite3.TimeFormatAuto, "2013-10-07 04:23:19.12-04:00", reference, 0, false}, - {sqlite3.TimeFormatAuto, "04:23:19.12-04:00", reftime, 0, false}, + {sqlite3.TimeFormatAuto, "04:23:19.12-04:00", refnodate, 0, false}, {sqlite3.TimeFormatAuto, "abc", time.Time{}, 0, true}, {sqlite3.TimeFormatAuto, false, time.Time{}, 0, true}, {sqlite3.TimeFormat3, "2013-10-07 04:23:19.12-04:00", reference, 0, false}, {sqlite3.TimeFormat3, "2013-10-07 08:23:19.12", reference, 0, false}, - {sqlite3.TimeFormat9, "04:23:19.12-04:00", reftime, 0, false}, - {sqlite3.TimeFormat9, "08:23:19.12", reftime, 0, false}, + {sqlite3.TimeFormat9, "04:23:19.12-04:00", refnodate, 0, false}, + {sqlite3.TimeFormat9, "08:23:19.12", refnodate, 0, false}, {sqlite3.TimeFormat3, false, time.Time{}, 0, true}, {sqlite3.TimeFormat9, false, time.Time{}, 0, true}, diff --git a/time.go b/time.go index a1308cf..00a9ed9 100644 --- a/time.go +++ b/time.go @@ -62,13 +62,18 @@ const ( // [TimeFormatDefault] and [TimeFormatAuto] encode using [time.RFC3339Nano], // with nanosecond accuracy, and preserving any timezone offset. // -// This is the format used by the database/sql driver: -// [database/sql.Row.Scan] is able to decode as [time.Time] +// This is the format used by the [database/sql] driver: +// [database/sql.Row.Scan] will decode as [time.Time] // values encoded with [time.RFC3339Nano]. // // Time values encoded with [time.RFC3339Nano] cannot be sorted as strings // to produce a time-ordered sequence. -// Use [TimeFormat7] for time-ordered encoding. +// +// Assuming that the time zones of the time values are the same (e.g., all in UTC), +// and expressed using the same string (e.g., all "Z" or all "+00:00"), +// use the TIME [collating sequence] to produce a time-ordered sequence. +// +// Otherwise, use [TimeFormat7] for time-ordered encoding. // // Formats [TimeFormat1] through [TimeFormat10] // convert time values to UTC before encoding. @@ -78,6 +83,8 @@ const ( // or an int64 for the other numeric formats. // // https://www.sqlite.org/lang_datefunc.html +// +// [collating sequence]: https://www.sqlite.org/datatype3.html#collating_sequences func (f TimeFormat) Encode(t time.Time) any { switch f { // Numeric formats @@ -123,9 +130,9 @@ func (f TimeFormat) Encode(t time.Time) any { // [TimeFormatAuto] implements (and extends) the SQLite auto modifier. // Julian day numbers are safe to use for historical dates, // from 4712BC through 9999AD. -// Unix timestamps (expressed in seconds, milliseconds, microseconds, or nanoseconds), -// are safe to use for current events, from 1980 through at least 2260. -// Unix timestamps before 1980 may be misinterpreted as julian day numbers, +// Unix timestamps (expressed in seconds, milliseconds, microseconds, or nanoseconds) +// are safe to use for current events, from at least 1980 through at least 2260. +// Unix timestamps before 1980 and after 9999 may be misinterpreted as julian day numbers, // or have the wrong time unit. // // https://www.sqlite.org/lang_datefunc.html