github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/tests/time_test.go (about)

     1  package tests
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/ncruces/go-sqlite3"
    11  	"github.com/ncruces/go-sqlite3/driver"
    12  	_ "github.com/ncruces/go-sqlite3/embed"
    13  	_ "github.com/ncruces/go-sqlite3/tests/testcfg"
    14  )
    15  
    16  func TestTimeFormat_Encode(t *testing.T) {
    17  	t.Parallel()
    18  
    19  	reference := time.Date(2013, 10, 7, 4, 23, 19, 120_000_000, time.FixedZone("", -4*3600))
    20  
    21  	tests := []struct {
    22  		fmt  sqlite3.TimeFormat
    23  		time time.Time
    24  		want any
    25  	}{
    26  		{sqlite3.TimeFormatDefault, reference, "2013-10-07T04:23:19.12-04:00"},
    27  		{sqlite3.TimeFormatJulianDay, reference, 2456572.849526851851852},
    28  		{sqlite3.TimeFormatUnix, reference, int64(1381134199)},
    29  		{sqlite3.TimeFormatUnixFrac, reference, 1381134199.120},
    30  		{sqlite3.TimeFormatUnixMilli, reference, int64(1381134199_120)},
    31  		{sqlite3.TimeFormatUnixMicro, reference, int64(1381134199_120000)},
    32  		{sqlite3.TimeFormatUnixNano, reference, int64(1381134199_120000000)},
    33  		{sqlite3.TimeFormat7, reference, "2013-10-07T08:23:19.120"},
    34  	}
    35  	for _, tt := range tests {
    36  		t.Run("", func(t *testing.T) {
    37  			if got := tt.fmt.Encode(tt.time); !reflect.DeepEqual(got, tt.want) {
    38  				t.Errorf("%q.Encode(%v) = %v, want %v", tt.fmt, tt.time, got, tt.want)
    39  			}
    40  		})
    41  	}
    42  }
    43  
    44  func TestTimeFormat_Decode(t *testing.T) {
    45  	t.Parallel()
    46  
    47  	const offset = -4 * 3600
    48  	zone := time.FixedZone("", offset)
    49  	reference := time.Date(2013, 10, 7, 4, 23, 19, 120_000_000, zone)
    50  	refnodate := time.Date(2000, 01, 1, 4, 23, 19, 120_000_000, zone)
    51  
    52  	tests := []struct {
    53  		fmt       sqlite3.TimeFormat
    54  		val       any
    55  		want      time.Time
    56  		wantDelta time.Duration
    57  		wantOff   int
    58  		wantErr   bool
    59  	}{
    60  		{sqlite3.TimeFormatJulianDay, "2456572.849526851851852", reference, 0, 0, false},
    61  		{sqlite3.TimeFormatJulianDay, 2456572.849526851851852, reference, time.Millisecond, 0, false},
    62  		{sqlite3.TimeFormatJulianDay, int64(2456572), reference, 24 * time.Hour, 0, false},
    63  		{sqlite3.TimeFormatJulianDay, false, time.Time{}, 0, 0, true},
    64  
    65  		{sqlite3.TimeFormatUnix, "1381134199.120", reference, time.Microsecond, 0, false},
    66  		{sqlite3.TimeFormatUnix, 1381134199.120, reference, time.Microsecond, 0, false},
    67  		{sqlite3.TimeFormatUnix, int64(1381134199), reference, time.Second, 0, false},
    68  		{sqlite3.TimeFormatUnix, "abc", time.Time{}, 0, 0, true},
    69  		{sqlite3.TimeFormatUnix, false, time.Time{}, 0, 0, true},
    70  
    71  		{sqlite3.TimeFormatUnixMilli, "1381134199120", reference, 0, 0, false},
    72  		{sqlite3.TimeFormatUnixMilli, 1381134199.120e3, reference, 0, 0, false},
    73  		{sqlite3.TimeFormatUnixMilli, int64(1381134199_120), reference, 0, 0, false},
    74  		{sqlite3.TimeFormatUnixMilli, "abc", time.Time{}, 0, 0, true},
    75  		{sqlite3.TimeFormatUnixMilli, false, time.Time{}, 0, 0, true},
    76  
    77  		{sqlite3.TimeFormatUnixMicro, "1381134199120000", reference, 0, 0, false},
    78  		{sqlite3.TimeFormatUnixMicro, 1381134199.120e6, reference, 0, 0, false},
    79  		{sqlite3.TimeFormatUnixMicro, int64(1381134199_120000), reference, 0, 0, false},
    80  		{sqlite3.TimeFormatUnixMicro, "abc", time.Time{}, 0, 0, true},
    81  		{sqlite3.TimeFormatUnixMicro, false, time.Time{}, 0, 0, true},
    82  
    83  		{sqlite3.TimeFormatUnixNano, "1381134199120000000", reference, 0, 0, false},
    84  		{sqlite3.TimeFormatUnixNano, 1381134199.120e9, reference, 0, 0, false},
    85  		{sqlite3.TimeFormatUnixNano, int64(1381134199_120000000), reference, 0, 0, false},
    86  		{sqlite3.TimeFormatUnixNano, "abc", time.Time{}, 0, 0, true},
    87  		{sqlite3.TimeFormatUnixNano, false, time.Time{}, 0, 0, true},
    88  
    89  		{sqlite3.TimeFormatAuto, "2456572.849526851851852", reference, time.Millisecond, 0, false},
    90  		{sqlite3.TimeFormatAuto, "2456572", reference, 24 * time.Hour, 0, false},
    91  		{sqlite3.TimeFormatAuto, "1381134199.120", reference, time.Microsecond, 0, false},
    92  		{sqlite3.TimeFormatAuto, "1381134199.120e3", reference, time.Microsecond, 0, false},
    93  		{sqlite3.TimeFormatAuto, "1381134199.120e6", reference, time.Microsecond, 0, false},
    94  		{sqlite3.TimeFormatAuto, "1381134199.120e9", reference, time.Microsecond, 0, false},
    95  		{sqlite3.TimeFormatAuto, "1381134199", reference, time.Second, 0, false},
    96  		{sqlite3.TimeFormatAuto, "1381134199120", reference, 0, 0, false},
    97  		{sqlite3.TimeFormatAuto, "1381134199120000", reference, 0, 0, false},
    98  		{sqlite3.TimeFormatAuto, "1381134199120000000", reference, 0, 0, false},
    99  		{sqlite3.TimeFormatAuto, "2013-10-07 04:23:19.12-04:00", reference, 0, offset, false},
   100  		{sqlite3.TimeFormatAuto, "04:23:19.12-04:00", refnodate, 0, offset, false},
   101  		{sqlite3.TimeFormatAuto, "abc", time.Time{}, 0, 0, true},
   102  		{sqlite3.TimeFormatAuto, false, time.Time{}, 0, 0, true},
   103  
   104  		{sqlite3.TimeFormat3, "2013-10-07 04:23:19.12-04:00", reference, 0, offset, false},
   105  		{sqlite3.TimeFormat3, "2013-10-07 08:23:19.12", reference, 0, 0, false},
   106  		{sqlite3.TimeFormat9, "04:23:19.12-04:00", refnodate, 0, offset, false},
   107  		{sqlite3.TimeFormat9, "08:23:19.12", refnodate, 0, 0, false},
   108  		{sqlite3.TimeFormat3, false, time.Time{}, 0, 0, true},
   109  		{sqlite3.TimeFormat9, false, time.Time{}, 0, 0, true},
   110  
   111  		{sqlite3.TimeFormatDefault, "2013-10-07T04:23:19.12-04:00", reference, 0, offset, false},
   112  		{sqlite3.TimeFormatDefault, "2013-10-07T08:23:19.12Z", reference, 0, 0, false},
   113  		{sqlite3.TimeFormatDefault, false, time.Time{}, 0, 0, true},
   114  	}
   115  
   116  	for _, tt := range tests {
   117  		t.Run("", func(t *testing.T) {
   118  			got, err := tt.fmt.Decode(tt.val)
   119  			if (err != nil) != tt.wantErr {
   120  				t.Errorf("%q.Decode(%v) error = %v, wantErr %v", tt.fmt, tt.val, err, tt.wantErr)
   121  				return
   122  			}
   123  			if got.Sub(tt.want).Abs() > tt.wantDelta {
   124  				t.Errorf("%q.Decode(%v) = %v, want %v", tt.fmt, tt.val, got, tt.want)
   125  			}
   126  			if _, off := got.Zone(); off != tt.wantOff {
   127  				t.Errorf("%q.Decode(%v) = %v, want %v", tt.fmt, tt.val, off, tt.wantOff)
   128  			}
   129  		})
   130  	}
   131  }
   132  
   133  func TestTimeFormat_Scanner(t *testing.T) {
   134  	t.Parallel()
   135  
   136  	ctx, cancel := context.WithCancel(context.Background())
   137  	defer cancel()
   138  
   139  	db, err := driver.Open(":memory:", nil)
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  	defer db.Close()
   144  
   145  	conn, err := db.Conn(ctx)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	defer conn.Close()
   150  
   151  	_, err = conn.ExecContext(ctx, `CREATE TABLE test (col)`)
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	reference := time.Date(2013, 10, 7, 4, 23, 19, 120_000_000, time.FixedZone("", -4*3600))
   157  
   158  	_, err = conn.ExecContext(ctx, `INSERT INTO test VALUES (?)`, sqlite3.TimeFormat7TZ.Encode(reference))
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  
   163  	var got time.Time
   164  	err = conn.QueryRowContext(ctx, "SELECT * FROM test").Scan(sqlite3.TimeFormatAuto.Scanner(&got))
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	if !got.Equal(reference) {
   169  		t.Errorf("got %v, want %v", got, reference)
   170  	}
   171  }
   172  
   173  func TestDB_timeCollation(t *testing.T) {
   174  	t.Parallel()
   175  
   176  	db, err := sqlite3.Open(":memory:")
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	defer db.Close()
   181  
   182  	err = db.Exec(`CREATE TABLE times (tstamp COLLATE TIME)`)
   183  	if err != nil {
   184  		t.Fatal(err)
   185  	}
   186  
   187  	stmt, _, err := db.Prepare(`INSERT INTO times VALUES (?), (?), (?)`)
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	defer stmt.Close()
   192  
   193  	stmt.BindTime(1, time.Unix(0, 0).UTC(), sqlite3.TimeFormatDefault)
   194  	stmt.BindTime(2, time.Unix(0, -1).UTC(), sqlite3.TimeFormatDefault)
   195  	stmt.BindTime(3, time.Unix(0, +1).UTC(), sqlite3.TimeFormatDefault)
   196  	stmt.Step()
   197  
   198  	err = stmt.Close()
   199  	if err != nil {
   200  		t.Fatal(err)
   201  	}
   202  
   203  	stmt, _, err = db.Prepare(`SELECT tstamp FROM times ORDER BY tstamp`)
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	var t0 time.Time
   209  	for stmt.Step() {
   210  		t1 := stmt.ColumnTime(0, sqlite3.TimeFormatAuto)
   211  		if t0.After(t1) {
   212  			t.Errorf("got %v after %v", t0, t1)
   213  		}
   214  		t0 = t1
   215  	}
   216  	err = stmt.Close()
   217  	if err != nil {
   218  		t.Fatal(err)
   219  	}
   220  }
   221  
   222  func TestDB_isoWeek(t *testing.T) {
   223  	t.Parallel()
   224  
   225  	db, err := sqlite3.Open(":memory:")
   226  	if err != nil {
   227  		t.Fatal(err)
   228  	}
   229  	defer db.Close()
   230  
   231  	stmt, _, err := db.Prepare(`SELECT strftime('%G-W%V-%u', ?)`)
   232  	if err != nil {
   233  		t.Fatal(err)
   234  	}
   235  	defer stmt.Close()
   236  
   237  	tend := time.Date(2500, 1, 1, 0, 0, 0, 0, time.UTC)
   238  	tstart := time.Date(1500, 1, 1, 12, 0, 0, 0, time.UTC)
   239  	for tm := tstart; tm.Before(tend); tm = tm.AddDate(0, 0, 1) {
   240  		stmt.BindTime(1, tm, sqlite3.TimeFormatDefault)
   241  		if stmt.Step() {
   242  			y, w := tm.ISOWeek()
   243  			d := tm.Weekday()
   244  			if d == 0 {
   245  				d = 7
   246  			}
   247  			want := fmt.Sprintf("%04d-W%02d-%d", y, w, d)
   248  			if got := stmt.ColumnText(0); got != want {
   249  				t.Errorf("got %q, want %q (%v)", got, want, tm)
   250  			}
   251  		}
   252  		stmt.Reset()
   253  	}
   254  }