From 71ae26e5c964517fe52aa0367f18b468e52a63f1 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Wed, 22 Feb 2023 17:51:30 +0000 Subject: [PATCH] Documentation. --- README.md | 9 ++++---- blob.go | 2 +- driver/time.go | 2 +- time.go | 62 +++++++++++++++++++++++++++++++------------------- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 035aaa6..31a4e93 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ ⚠️ CAUTION ⚠️ -This is a WIP.\ -DO NOT USE with data you care about. +This is a WIP. Roadmap: - [x] build SQLite using `zig cc --target=wasm32-wasi` @@ -19,8 +18,10 @@ Roadmap: - [x] file locking, compatible with SQLite on Windows/Unix - [ ] shared memory, compatible with SQLite on Windows/Unix - needed for improved WAL mode -- [ ] advanced features +- [ ] advanced SQLite features + - [ ] nested transactions - [ ] incremental BLOB I/O - [ ] online backup - [ ] session extension - - [ ] snapshot \ No newline at end of file + - [ ] snapshots + - [ ] SQL functions \ No newline at end of file diff --git a/blob.go b/blob.go index 850be5f..739b25f 100644 --- a/blob.go +++ b/blob.go @@ -2,5 +2,5 @@ package sqlite3 // ZeroBlob represents a zero-filled, length n BLOB // that can be used as an argument to -// [database.sql.DB.Exec] and similar methods. +// [database/sql.DB.Exec] and similar methods. type ZeroBlob int64 diff --git a/driver/time.go b/driver/time.go index efac7dd..12958ca 100644 --- a/driver/time.go +++ b/driver/time.go @@ -8,7 +8,7 @@ import ( // Convert a string in [time.RFC3339Nano] format into a [time.Time] // if it roundtrips back to the same string. // This way times can be persisted to, and recovered from, the database, -// but if a string is needed, [database.sql] will recover the same string. +// but if a string is needed, [database/sql] will recover the same string. func maybeDate(text string) driver.Value { // Weed out (some) values that can't possibly be // [time.RFC3339Nano] timestamps. diff --git a/time.go b/time.go index 4e88d93..da5b205 100644 --- a/time.go +++ b/time.go @@ -59,9 +59,13 @@ const ( // [TimeFormatDefault] and [TimeFormatAuto] encode using [time.RFC3339Nano], // with nanosecond accuracy, and preserving timezone. // -// Formats that don't record the timezone +// Formats [TimeFormat1] through [TimeFormat10] // convert time values to UTC before encoding. // +// Returns a string for the text formats, +// a float64 for [TimeFormatJulianDay] and [TimeFormatUnixFrac], +// or an int64 for the other numeric formats. +// // https://www.sqlite.org/lang_datefunc.html func (f TimeFormat) Encode(t time.Time) any { switch f { @@ -81,11 +85,13 @@ func (f TimeFormat) Encode(t time.Time) any { // Special formats case TimeFormatDefault, TimeFormatAuto: f = time.RFC3339Nano - } // SQLite assumes UTC if unspecified. - if !strings.Contains(string(f), "MST") && - !strings.Contains(string(f), "Z07") && - !strings.Contains(string(f), "-07") { + case + TimeFormat1, TimeFormat2, + TimeFormat3, TimeFormat4, + TimeFormat5, TimeFormat6, + TimeFormat7, TimeFormat8, + TimeFormat9, TimeFormat10: t = t.UTC() } return t.Format(string(f)) @@ -93,8 +99,19 @@ func (f TimeFormat) Encode(t time.Time) any { // Decode decodes a time value using this format. // -// Decoding of SQLite recognized formats is lenient: -// timezones and fractional seconds are always optional. +// The time value can be a string, an int64, or a float64. +// +// Formats [TimeFormat8] through [TimeFormat10] +// assume a date of 2000-01-01. +// +// The timezone indicator and fractional seconds are always optional +// for formats [TimeFormat2] through [TimeFormat10]. +// +// [TimeFormatAuto] implements (and extends) the SQLite auto modifier. +// The julian day number is 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. // // https://www.sqlite.org/lang_datefunc.html func (f TimeFormat) Decode(v any) (time.Time, error) { @@ -263,14 +280,7 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { if !ok { return time.Time{}, timeErr } - f := string(f) - f = strings.TrimSuffix(f, "Z07:00") - f = strings.TrimSuffix(f, ".000") - t, err := time.Parse(f+"Z07:00", s) - if err != nil { - t, err = time.Parse(f, s) - } - return t, err + return f.parseRelaxed(s) case TimeFormat8, TimeFormat8TZ, @@ -280,13 +290,7 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { if !ok { return time.Time{}, timeErr } - f := string(f) - f = strings.TrimSuffix(f, "Z07:00") - f = strings.TrimSuffix(f, ".000") - t, err := time.Parse(f+"Z07:00", s) - if err != nil { - t, err = time.Parse(f, s) - } + t, err := f.parseRelaxed(s) return t.AddDate(2000, 0, 0), err default: @@ -294,10 +298,20 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { if !ok { return time.Time{}, timeErr } - f := string(f) if f == "" { f = time.RFC3339Nano } - return time.Parse(f, s) + return time.Parse(string(f), s) } } + +func (f TimeFormat) parseRelaxed(s string) (time.Time, error) { + fs := string(f) + fs = strings.TrimSuffix(fs, "Z07:00") + fs = strings.TrimSuffix(fs, ".000") + t, err := time.Parse(fs+"Z07:00", s) + if err != nil { + return time.Parse(fs, s) + } + return t, nil +}