diff --git a/README.md b/README.md index 9f6ebdf..39b2e69 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,8 @@ To work around this limitation, SQLite is [patched](sqlite3/locking_mode.patch) to always use `EXCLUSIVE` locking mode for WAL databases. Because connection pooling is incompatible with `EXCLUSIVE` locking mode, -to use the [`database/sql`](https://pkg.go.dev/database/sql) -driver with WAL mode databases you should disable connection pooling by calling +to use the [`database/sql`](https://pkg.go.dev/database/sql) driver +with WAL mode databases you should disable connection pooling by calling [`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns). #### File Locking @@ -61,11 +61,15 @@ On BSD Unixes, this module uses [BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2). On BSD Unixes, BSD locks are fully compatible with POSIX advisory locks. -On Windows, this module uses `LockFile`, `LockFileEx`, and `UnlockFile`, like SQLite. +On Windows, this module uses `LockFile`, `LockFileEx`, and `UnlockFile`, +like SQLite. On all other platforms, file locking is not supported, and you must use [`nolock=1`](https://www.sqlite.org/uri.html#urinolock) to open database files. +To use the [`database/sql`](https://pkg.go.dev/database/sql) driver +with `nolock=1` you must disable connection pooling by calling +[`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns). #### Testing diff --git a/tests/time_test.go b/tests/time_test.go index eb28113..0a8dbda 100644 --- a/tests/time_test.go +++ b/tests/time_test.go @@ -40,70 +40,72 @@ func TestTimeFormat_Encode(t *testing.T) { func TestTimeFormat_Decode(t *testing.T) { t.Parallel() - reference := time.Date(2013, 10, 7, 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)) + zone := time.FixedZone("", -4*3600) + reference := time.Date(2013, 10, 7, 4, 23, 19, 120_000_000, zone) + refnodate := time.Date(2000, 01, 1, 4, 23, 19, 120_000_000, zone) tests := []struct { fmt sqlite3.TimeFormat val any want time.Time wantDelta time.Duration + wantLoc *time.Location wantErr bool }{ - {sqlite3.TimeFormatJulianDay, "2456572.849526851851852", reference, 0, false}, - {sqlite3.TimeFormatJulianDay, 2456572.849526851851852, reference, time.Millisecond, false}, - {sqlite3.TimeFormatJulianDay, int64(2456572), reference, 24 * time.Hour, false}, - {sqlite3.TimeFormatJulianDay, false, time.Time{}, 0, true}, + {sqlite3.TimeFormatJulianDay, "2456572.849526851851852", reference, 0, time.UTC, false}, + {sqlite3.TimeFormatJulianDay, 2456572.849526851851852, reference, time.Millisecond, time.UTC, false}, + {sqlite3.TimeFormatJulianDay, int64(2456572), reference, 24 * time.Hour, time.UTC, false}, + {sqlite3.TimeFormatJulianDay, false, time.Time{}, 0, nil, true}, - {sqlite3.TimeFormatUnix, "1381134199.120", reference, time.Microsecond, false}, - {sqlite3.TimeFormatUnix, 1381134199.120, reference, time.Microsecond, false}, - {sqlite3.TimeFormatUnix, int64(1381134199), reference, time.Second, false}, - {sqlite3.TimeFormatUnix, "abc", time.Time{}, 0, true}, - {sqlite3.TimeFormatUnix, false, time.Time{}, 0, true}, + {sqlite3.TimeFormatUnix, "1381134199.120", reference, time.Microsecond, time.UTC, false}, + {sqlite3.TimeFormatUnix, 1381134199.120, reference, time.Microsecond, time.UTC, false}, + {sqlite3.TimeFormatUnix, int64(1381134199), reference, time.Second, time.UTC, false}, + {sqlite3.TimeFormatUnix, "abc", time.Time{}, 0, nil, true}, + {sqlite3.TimeFormatUnix, false, time.Time{}, 0, nil, true}, - {sqlite3.TimeFormatUnixMilli, "1381134199120", reference, 0, false}, - {sqlite3.TimeFormatUnixMilli, 1381134199.120e3, reference, 0, false}, - {sqlite3.TimeFormatUnixMilli, int64(1381134199_120), reference, 0, false}, - {sqlite3.TimeFormatUnixMilli, "abc", time.Time{}, 0, true}, - {sqlite3.TimeFormatUnixMilli, false, time.Time{}, 0, true}, + {sqlite3.TimeFormatUnixMilli, "1381134199120", reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixMilli, 1381134199.120e3, reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixMilli, int64(1381134199_120), reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixMilli, "abc", time.Time{}, 0, nil, true}, + {sqlite3.TimeFormatUnixMilli, false, time.Time{}, 0, nil, true}, - {sqlite3.TimeFormatUnixMicro, "1381134199120000", reference, 0, false}, - {sqlite3.TimeFormatUnixMicro, 1381134199.120e6, reference, 0, false}, - {sqlite3.TimeFormatUnixMicro, int64(1381134199_120000), reference, 0, false}, - {sqlite3.TimeFormatUnixMicro, "abc", time.Time{}, 0, true}, - {sqlite3.TimeFormatUnixMicro, false, time.Time{}, 0, true}, + {sqlite3.TimeFormatUnixMicro, "1381134199120000", reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixMicro, 1381134199.120e6, reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixMicro, int64(1381134199_120000), reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixMicro, "abc", time.Time{}, 0, nil, true}, + {sqlite3.TimeFormatUnixMicro, false, time.Time{}, 0, nil, true}, - {sqlite3.TimeFormatUnixNano, "1381134199120000000", reference, 0, false}, - {sqlite3.TimeFormatUnixNano, 1381134199.120e9, reference, 0, false}, - {sqlite3.TimeFormatUnixNano, int64(1381134199_120000000), reference, 0, false}, - {sqlite3.TimeFormatUnixNano, "abc", time.Time{}, 0, true}, - {sqlite3.TimeFormatUnixNano, false, time.Time{}, 0, true}, + {sqlite3.TimeFormatUnixNano, "1381134199120000000", reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixNano, 1381134199.120e9, reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixNano, int64(1381134199_120000000), reference, 0, time.UTC, false}, + {sqlite3.TimeFormatUnixNano, "abc", time.Time{}, 0, nil, true}, + {sqlite3.TimeFormatUnixNano, false, time.Time{}, 0, nil, true}, - {sqlite3.TimeFormatAuto, "2456572.849526851851852", reference, time.Millisecond, false}, - {sqlite3.TimeFormatAuto, "2456572", reference, 24 * time.Hour, false}, - {sqlite3.TimeFormatAuto, "1381134199.120", reference, time.Microsecond, false}, - {sqlite3.TimeFormatAuto, "1381134199.120e3", reference, time.Microsecond, false}, - {sqlite3.TimeFormatAuto, "1381134199.120e6", reference, time.Microsecond, false}, - {sqlite3.TimeFormatAuto, "1381134199.120e9", reference, time.Microsecond, false}, - {sqlite3.TimeFormatAuto, "1381134199", reference, time.Second, false}, - {sqlite3.TimeFormatAuto, "1381134199120", reference, 0, false}, - {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", refnodate, 0, false}, - {sqlite3.TimeFormatAuto, "abc", time.Time{}, 0, true}, - {sqlite3.TimeFormatAuto, false, time.Time{}, 0, true}, + {sqlite3.TimeFormatAuto, "2456572.849526851851852", reference, time.Millisecond, time.UTC, false}, + {sqlite3.TimeFormatAuto, "2456572", reference, 24 * time.Hour, time.UTC, false}, + {sqlite3.TimeFormatAuto, "1381134199.120", reference, time.Microsecond, time.UTC, false}, + {sqlite3.TimeFormatAuto, "1381134199.120e3", reference, time.Microsecond, time.UTC, false}, + {sqlite3.TimeFormatAuto, "1381134199.120e6", reference, time.Microsecond, time.UTC, false}, + {sqlite3.TimeFormatAuto, "1381134199.120e9", reference, time.Microsecond, time.UTC, false}, + {sqlite3.TimeFormatAuto, "1381134199", reference, time.Second, time.UTC, false}, + {sqlite3.TimeFormatAuto, "1381134199120", reference, 0, time.UTC, false}, + {sqlite3.TimeFormatAuto, "1381134199120000", reference, 0, time.UTC, false}, + {sqlite3.TimeFormatAuto, "1381134199120000000", reference, 0, time.UTC, false}, + {sqlite3.TimeFormatAuto, "2013-10-07 04:23:19.12-04:00", reference, 0, zone, false}, + {sqlite3.TimeFormatAuto, "04:23:19.12-04:00", refnodate, 0, zone, false}, + {sqlite3.TimeFormatAuto, "abc", time.Time{}, 0, nil, true}, + {sqlite3.TimeFormatAuto, false, time.Time{}, 0, nil, 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", 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}, + {sqlite3.TimeFormat3, "2013-10-07 04:23:19.12-04:00", reference, 0, zone, false}, + {sqlite3.TimeFormat3, "2013-10-07 08:23:19.12", reference, 0, time.UTC, false}, + {sqlite3.TimeFormat9, "04:23:19.12-04:00", refnodate, 0, zone, false}, + {sqlite3.TimeFormat9, "08:23:19.12", refnodate, 0, time.UTC, false}, + {sqlite3.TimeFormat3, false, time.Time{}, 0, nil, true}, + {sqlite3.TimeFormat9, false, time.Time{}, 0, nil, true}, - {sqlite3.TimeFormatDefault, "2013-10-07T04:23:19.12-04:00", reference, 0, false}, - {sqlite3.TimeFormatDefault, "2013-10-07T08:23:19.12Z", reference, 0, false}, - {sqlite3.TimeFormatDefault, false, time.Time{}, 0, true}, + {sqlite3.TimeFormatDefault, "2013-10-07T04:23:19.12-04:00", reference, 0, zone, false}, + {sqlite3.TimeFormatDefault, "2013-10-07T08:23:19.12Z", reference, 0, time.UTC, false}, + {sqlite3.TimeFormatDefault, false, time.Time{}, 0, nil, true}, } for _, tt := range tests { @@ -113,9 +115,12 @@ func TestTimeFormat_Decode(t *testing.T) { t.Errorf("%q.Decode(%v) error = %v, wantErr %v", tt.fmt, tt.val, err, tt.wantErr) return } - if tt.want.Sub(got).Abs() > tt.wantDelta { + if got.Sub(tt.want).Abs() > tt.wantDelta { t.Errorf("%q.Decode(%v) = %v, want %v", tt.fmt, tt.val, got, tt.want) } + if got.Location().String() != tt.wantLoc.String() { + t.Errorf("%q.Decode(%v) = %v, want %v", tt.fmt, tt.val, got.Location(), tt.wantLoc) + } }) } } diff --git a/time.go b/time.go index 8475439..257ac1d 100644 --- a/time.go +++ b/time.go @@ -164,9 +164,9 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { case float64: sec, frac := math.Modf(v) nsec := math.Floor(frac * 1e9) - return time.Unix(int64(sec), int64(nsec)), nil + return time.Unix(int64(sec), int64(nsec)).UTC(), nil case int64: - return time.Unix(v, 0), nil + return time.Unix(v, 0).UTC(), nil default: return time.Time{}, util.TimeErr } @@ -181,9 +181,9 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { } switch v := v.(type) { case float64: - return time.UnixMilli(int64(math.Floor(v))), nil + return time.UnixMilli(int64(math.Floor(v))).UTC(), nil case int64: - return time.UnixMilli(int64(v)), nil + return time.UnixMilli(int64(v)).UTC(), nil default: return time.Time{}, util.TimeErr } @@ -198,9 +198,9 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { } switch v := v.(type) { case float64: - return time.UnixMicro(int64(math.Floor(v))), nil + return time.UnixMicro(int64(math.Floor(v))).UTC(), nil case int64: - return time.UnixMicro(int64(v)), nil + return time.UnixMicro(int64(v)).UTC(), nil default: return time.Time{}, util.TimeErr } @@ -215,9 +215,9 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { } switch v := v.(type) { case float64: - return time.Unix(0, int64(math.Floor(v))), nil + return time.Unix(0, int64(math.Floor(v))).UTC(), nil case int64: - return time.Unix(0, int64(v)), nil + return time.Unix(0, int64(v)).UTC(), nil default: return time.Time{}, util.TimeErr } @@ -238,26 +238,16 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { } dates := []TimeFormat{ - TimeFormat6TZ, TimeFormat6, TimeFormat3TZ, TimeFormat3, - TimeFormat5TZ, TimeFormat5, TimeFormat2TZ, TimeFormat2, - TimeFormat1, + TimeFormat9, TimeFormat8, + TimeFormat6, TimeFormat5, + TimeFormat3, TimeFormat2, TimeFormat1, } for _, f := range dates { - t, err := time.Parse(string(f), s) + t, err := f.Decode(s) if err == nil { return t, nil } } - - times := []TimeFormat{ - TimeFormat9TZ, TimeFormat9, TimeFormat8TZ, TimeFormat8, - } - for _, f := range times { - t, err := time.Parse(string(f), s) - if err == nil { - return t.AddDate(2000, 0, 0), nil - } - } } switch v := v.(type) { case float64: @@ -314,7 +304,10 @@ func (f TimeFormat) Decode(v any) (time.Time, error) { return time.Time{}, util.TimeErr } t, err := f.parseRelaxed(s) - return t.AddDate(2000, 0, 0), err + if err != nil { + return time.Time{}, err + } + return t.AddDate(2000, 0, 0), nil default: s, ok := v.(string)