diff --git a/all_test.go b/all_test.go index 0c5f3e1..b14e72e 100644 --- a/all_test.go +++ b/all_test.go @@ -923,3 +923,51 @@ Col 3: DatabaseTypeName "DATE", DecimalSize 0 0 false, Length 922337203685477580 } t.Log(b.String()) } + +// https://gitlab.com/cznic/sqlite/-/issues/35 +func TestTime(t *testing.T) { + types := []string{ + "DATE", + "DATETIME", + "Date", + "DateTime", + "TIMESTAMP", + "TimeStamp", + "date", + "datetime", + "timestamp", + } + db, err := sql.Open(driverName, "file::memory:") + if err != nil { + t.Fatal(err) + } + + defer func() { + db.Close() + }() + + for _, typ := range types { + if _, err := db.Exec(fmt.Sprintf(` + drop table if exists mg; + create table mg (applied_at %s); + `, typ)); err != nil { + t.Fatal(err) + } + + now := time.Now() + _, err = db.Exec(`INSERT INTO mg (applied_at) VALUES (?)`, &now) + if err != nil { + t.Fatal(err) + } + + var appliedAt time.Time + err = db.QueryRow("SELECT applied_at FROM mg").Scan(&appliedAt) + if err != nil { + t.Fatal(err) + } + + if g, e := appliedAt, now; !g.Equal(e) { + t.Fatal(g, e) + } + } +} diff --git a/sqlite.go b/sqlite.go index 4bad6d7..7a5e9ac 100644 --- a/sqlite.go +++ b/sqlite.go @@ -277,7 +277,12 @@ func (r *rows) Next(dest []driver.Value) (err error) { return err } - dest[i] = v + switch r.ColumnTypeDatabaseTypeName(i) { + case "DATE", "DATETIME", "TIMESTAMP": + dest[i], _ = r.c.parseTime(v) + default: + dest[i] = v + } case sqlite3.SQLITE_BLOB: v, err := r.c.columnBlob(r.pstmt, i) if err != nil { @@ -299,6 +304,33 @@ func (r *rows) Next(dest []driver.Value) (err error) { } } +// Attempt to parse s as a time. Return (s, false) if s is not +// recognized as a valid time encoding. +func (c *conn) parseTime(s string) (interface{}, bool) { + if v, ok := c.parseTimeString(s, strings.Index(s, "m=")); ok { + return v, true + } + + // TODO Add URI select time storage format and handle more formats + return s, false +} + +// Attempt to parse s as a time string produced by t.String(). If x > 0 it's +// the index of substring "m=" within s. Return (s, false) if s is +// not recognized as a valid time encoding. +func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) { + s := s0 + if x > 0 { + s = s[:x] // "2006-01-02 15:04:05.999999999 -0700 MST m=+9999" -> "2006-01-02 15:04:05.999999999 -0700 MST " + } + s = strings.TrimSpace(s) + if t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", s); err == nil { + return t, true + } + + return s0, false +} + // RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return // the database system type name without the length. Type names should be // uppercase. Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2",