mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-11 21:49:13 +00:00
Fuzz time shift.
This commit is contained in:
@@ -97,13 +97,13 @@ func ParseTimeShift(s string) (years, months, days int, duration time.Duration,
|
||||
if ok = sign && len(s) >= 10 && s[7] == '-'; !ok {
|
||||
return // !ok
|
||||
}
|
||||
if years, ok = parseInt(s[0:4]); !ok {
|
||||
if years, ok = parseInt(s[0:4], 0); !ok {
|
||||
return // !ok
|
||||
}
|
||||
if months, ok = parseInt(s[5:7]); !ok {
|
||||
if months, ok = parseInt(s[5:7], 12); !ok {
|
||||
return // !ok
|
||||
}
|
||||
if days, ok = parseInt(s[8:10]); !ok {
|
||||
if days, ok = parseInt(s[8:10], 31); !ok {
|
||||
return // !ok
|
||||
}
|
||||
if len(s) == 10 {
|
||||
@@ -121,10 +121,10 @@ func ParseTimeShift(s string) (years, months, days int, duration time.Duration,
|
||||
}
|
||||
|
||||
var hours, minutes int
|
||||
if hours, ok = parseInt(s[0:2]); !ok {
|
||||
if hours, ok = parseInt(s[0:2], 24); !ok {
|
||||
return
|
||||
}
|
||||
if minutes, ok = parseInt(s[3:5]); !ok {
|
||||
if minutes, ok = parseInt(s[3:5], 60); !ok {
|
||||
return
|
||||
}
|
||||
duration = time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute
|
||||
@@ -138,7 +138,7 @@ func ParseTimeShift(s string) (years, months, days int, duration time.Duration,
|
||||
|
||||
// Seconds part: HH:MM:SS
|
||||
var seconds int
|
||||
if seconds, ok = parseInt(s[6:8]); !ok {
|
||||
if seconds, ok = parseInt(s[6:8], 60); !ok {
|
||||
return
|
||||
}
|
||||
duration += time.Duration(seconds) * time.Second
|
||||
@@ -153,7 +153,7 @@ func ParseTimeShift(s string) (years, months, days int, duration time.Duration,
|
||||
|
||||
// Nanosecond part: HH:MM:SS.SSS
|
||||
var nanos int
|
||||
if nanos, ok = parseInt(s[0:min(9, len(s))]); !ok {
|
||||
if nanos, ok = parseInt(s[0:min(9, len(s))], 0); !ok {
|
||||
return
|
||||
}
|
||||
for i := len(s); i < 9; i++ {
|
||||
@@ -163,12 +163,12 @@ func ParseTimeShift(s string) (years, months, days int, duration time.Duration,
|
||||
|
||||
// Subnanosecond part.
|
||||
if len(s) > 9 {
|
||||
_, ok = parseInt(s[9:])
|
||||
_, ok = parseInt(s[9:], 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseInt(s string) (i int, _ bool) {
|
||||
func parseInt(s string, max int) (i int, _ bool) {
|
||||
for _, r := range []byte(s) {
|
||||
r -= '0'
|
||||
if r > 9 {
|
||||
@@ -176,5 +176,5 @@ func parseInt(s string) (i int, _ bool) {
|
||||
}
|
||||
i = i*10 + int(r)
|
||||
}
|
||||
return i, true
|
||||
return i, max == 0 || i < max
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
"github.com/ncruces/go-sqlite3/util/sql3util"
|
||||
)
|
||||
|
||||
@@ -63,26 +65,26 @@ func TestParseTimeShift(t *testing.T) {
|
||||
ok bool
|
||||
}{
|
||||
{"", epoch, false},
|
||||
{"0001-12-30", epoch, false},
|
||||
{"+_001-12-30", epoch, false},
|
||||
{"+0001-_2-30", epoch.AddDate(1, 0, 0), false},
|
||||
{"+0001-12-_0", epoch.AddDate(1, 12, 0), false},
|
||||
{"+0001-12-30", epoch.AddDate(1, 12, 30), true},
|
||||
{"-0001-12-30", epoch.AddDate(-1, -12, -30), true},
|
||||
{"+0001-12-30T", epoch.AddDate(1, 12, 30), false},
|
||||
{"+0001-12-30 12", epoch.AddDate(1, 12, 30), false},
|
||||
{"+0001-12-30 _2:30", epoch.AddDate(1, 12, 30), false},
|
||||
{"+0001-12-30 12:_0", epoch.AddDate(1, 12, 30), false},
|
||||
{"+0001-12-30 12:30", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 30*time.Minute), true},
|
||||
{"+0001-12-30 12:30:", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 30*time.Minute), false},
|
||||
{"+0001-12-30 12:30:_0", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 30*time.Minute), false},
|
||||
{"+0001-12-30 12:30:60", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 31*time.Minute), true},
|
||||
{"+0001-12-30 12:30:60.", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 31*time.Minute), false},
|
||||
{"+0001-12-30 12:30:60._", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 31*time.Minute), false},
|
||||
{"+0001-12-30 12:30:60.1", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 31*time.Minute + 100*time.Millisecond), true},
|
||||
{"+0001-12-30 12:30:60.123456789_", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 31*time.Minute + 123456789), false},
|
||||
{"+0001-12-30 12:30:60.1234567890", epoch.AddDate(1, 12, 30).Add(12*time.Hour + 31*time.Minute + 123456789), true},
|
||||
{"-12:30:60.1234567890", epoch.Add(-12*time.Hour - 31*time.Minute - 123456789), true},
|
||||
{"0001-11-30", epoch, false},
|
||||
{"+_001-11-30", epoch, false},
|
||||
{"+0001-_1-30", epoch.AddDate(1, 0, 0), false},
|
||||
{"+0001-11-_0", epoch.AddDate(1, 11, 0), false},
|
||||
{"+0001-11-30", epoch.AddDate(1, 11, 30), true},
|
||||
{"-0001-11-30", epoch.AddDate(-1, -11, -30), true},
|
||||
{"+0001-11-30T", epoch.AddDate(1, 11, 30), false},
|
||||
{"+0001-11-30 12", epoch.AddDate(1, 11, 30), false},
|
||||
{"+0001-11-30 _2:30", epoch.AddDate(1, 11, 30), false},
|
||||
{"+0001-11-30 12:_0", epoch.AddDate(1, 11, 30), false},
|
||||
{"+0001-11-30 12:30", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), true},
|
||||
{"+0001-11-30 12:30:", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), false},
|
||||
{"+0001-11-30 12:30:_0", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), false},
|
||||
{"+0001-11-30 12:30:59", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), true},
|
||||
{"+0001-11-30 12:30:59.", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), false},
|
||||
{"+0001-11-30 12:30:59._", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), false},
|
||||
{"+0001-11-30 12:30:59.1", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 100*time.Millisecond), true},
|
||||
{"+0001-11-30 12:30:59.123456789_", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 123456789), false},
|
||||
{"+0001-11-30 12:30:59.1234567890", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 123456789), true},
|
||||
{"-12:30:59.1234567890", epoch.Add(-12*time.Hour - 30*time.Minute - 59*time.Second - 123456789), true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.str, func(t *testing.T) {
|
||||
@@ -94,3 +96,72 @@ func TestParseTimeShift(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzParseTimeShift(f *testing.F) {
|
||||
f.Add("")
|
||||
f.Add("0001-12-30")
|
||||
f.Add("+_001-12-30")
|
||||
f.Add("+0001-_2-30")
|
||||
f.Add("+0001-12-_0")
|
||||
f.Add("+0001-12-30")
|
||||
f.Add("-0001-12-30")
|
||||
f.Add("+0001-12-30T")
|
||||
f.Add("+0001-12-30 12")
|
||||
f.Add("+0001-12-30 _2:30")
|
||||
f.Add("+0001-12-30 12:_0")
|
||||
f.Add("+0001-12-30 12:30")
|
||||
f.Add("+0001-12-30 12:30:")
|
||||
f.Add("+0001-12-30 12:30:_0")
|
||||
f.Add("+0001-12-30 12:30:60")
|
||||
f.Add("+0001-12-30 12:30:60.")
|
||||
f.Add("+0001-12-30 12:30:60._")
|
||||
f.Add("+0001-12-30 12:30:60.1")
|
||||
f.Add("+0001-12-30 12:30:60.123456789_")
|
||||
f.Add("+0001-12-30 12:30:60.1234567890")
|
||||
f.Add("-12:30:60.1234567890")
|
||||
|
||||
c, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
s, _, err := c.Prepare(`SELECT julianday('00:00', ?)`)
|
||||
if err != nil {
|
||||
f.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
// Default SQLite date.
|
||||
epoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
f.Fuzz(func(t *testing.T, str string) {
|
||||
years, months, days, duration, ok := sql3util.ParseTimeShift(str)
|
||||
|
||||
// Account for a full 400 year cycle.
|
||||
if years < -200 || years > +200 {
|
||||
t.Skip()
|
||||
}
|
||||
// SQLite only tracks milliseconds.
|
||||
if duration != duration.Truncate(time.Millisecond) {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
if ok {
|
||||
s.Reset()
|
||||
s.BindText(1, str)
|
||||
if !s.Step() {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
got := epoch.AddDate(years, months, days).Add(duration)
|
||||
|
||||
// Julian day introduces floating point inaccuracy.
|
||||
want := s.ColumnTime(0, sqlite3.TimeFormatJulianDay)
|
||||
want = want.Round(time.Millisecond)
|
||||
if !got.Equal(want) {
|
||||
t.Fatalf("with %q, got %v, want %v", str, got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user