mirror of https://github.com/jackc/pgx.git
Add date to conversion system
parent
4a9343e45d
commit
325f700b6e
1
conn.go
1
conn.go
|
@ -286,6 +286,7 @@ func (c *Conn) connect(config ConnConfig, network, address string, tlsConfig *tl
|
||||||
|
|
||||||
c.oidPgtypeValues = map[OID]pgtype.Value{
|
c.oidPgtypeValues = map[OID]pgtype.Value{
|
||||||
BoolOID: &b,
|
BoolOID: &b,
|
||||||
|
DateOID: &pgtype.Date{},
|
||||||
Int2OID: &i2,
|
Int2OID: &i2,
|
||||||
Int4OID: &i4,
|
Int4OID: &i4,
|
||||||
Int8OID: &i8,
|
Int8OID: &i8,
|
||||||
|
|
|
@ -2,6 +2,7 @@ package pgtype
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// underlyingIntType gets the underlying type that can be converted to Int2, Int4, or Int8
|
// underlyingIntType gets the underlying type that can be converted to Int2, Int4, or Int8
|
||||||
|
@ -71,3 +72,24 @@ func underlyingBoolType(val interface{}) (interface{}, bool) {
|
||||||
|
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// underlyingTimeType gets the underlying type that can be converted to time.Time
|
||||||
|
func underlyingTimeType(val interface{}) (interface{}, bool) {
|
||||||
|
refVal := reflect.ValueOf(val)
|
||||||
|
|
||||||
|
switch refVal.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if refVal.IsNil() {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
convVal := refVal.Elem().Interface()
|
||||||
|
return convVal, true
|
||||||
|
}
|
||||||
|
|
||||||
|
timeType := reflect.TypeOf(time.Time{})
|
||||||
|
if refVal.Type().ConvertibleTo(timeType) {
|
||||||
|
return refVal.Convert(timeType).Interface(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,30 @@ type Date struct {
|
||||||
t time.Time
|
t time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewDate(year int, month time.Month, day int) *Date {
|
||||||
|
return &Date{t: time.Date(year, month, day, 0, 0, 0, 0, time.Local)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Date) ConvertFrom(src interface{}) error {
|
||||||
|
switch value := src.(type) {
|
||||||
|
case Date:
|
||||||
|
*d = value
|
||||||
|
case time.Time:
|
||||||
|
*d = Date{t: value}
|
||||||
|
default:
|
||||||
|
if originalSrc, ok := underlyingTimeType(src); ok {
|
||||||
|
return d.ConvertFrom(originalSrc)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("cannot convert %v to Date", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Date) AssignTo(dst interface{}) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Date) DecodeText(r io.Reader) error {
|
func (d *Date) DecodeText(r io.Reader) error {
|
||||||
size, err := pgio.ReadInt32(r)
|
size, err := pgio.ReadInt32(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -31,7 +55,7 @@ func (d *Date) DecodeText(r io.Reader) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
d.t, err = time.Parse("2006-01-02", string(buf))
|
d.t, err = time.ParseInLocation("2006-01-02", string(buf), time.Local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -88,7 +112,3 @@ func (d Date) EncodeBinary(w io.Writer) error {
|
||||||
func (d Date) Time() time.Time {
|
func (d Date) Time() time.Time {
|
||||||
return d.t
|
return d.t
|
||||||
}
|
}
|
||||||
|
|
||||||
func DateFromTime(t time.Time) Date {
|
|
||||||
return Date{t: t}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
package pgtype_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx"
|
||||||
|
"github.com/jackc/pgx/pgio"
|
||||||
|
"github.com/jackc/pgx/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDateTranscode(t *testing.T) {
|
||||||
|
conn := mustConnectPgx(t)
|
||||||
|
defer mustClose(t, conn)
|
||||||
|
|
||||||
|
ps, err := conn.Prepare("test", "select $1::date")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
result *pgtype.Date
|
||||||
|
}{
|
||||||
|
{result: pgtype.NewDate(1900, 1, 1)},
|
||||||
|
{result: pgtype.NewDate(1970, 1, 1)},
|
||||||
|
{result: pgtype.NewDate(1999, 12, 31)},
|
||||||
|
{result: pgtype.NewDate(2000, 1, 1)},
|
||||||
|
{result: pgtype.NewDate(2000, 1, 2)},
|
||||||
|
{result: pgtype.NewDate(2200, 1, 1)},
|
||||||
|
}
|
||||||
|
|
||||||
|
ps.FieldDescriptions[0].FormatCode = pgx.TextFormatCode
|
||||||
|
for i, tt := range tests {
|
||||||
|
inputBuf := &bytes.Buffer{}
|
||||||
|
err = tt.result.EncodeText(inputBuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("TextFormat %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s string
|
||||||
|
err := conn.QueryRow("test", string(inputBuf.Bytes()[4:])).Scan(&s)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("TextFormat %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputBuf := &bytes.Buffer{}
|
||||||
|
pgio.WriteInt32(outputBuf, int32(len(s)))
|
||||||
|
outputBuf.WriteString(s)
|
||||||
|
var r pgtype.Date
|
||||||
|
err = r.DecodeText(outputBuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("TextFormat %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r != *tt.result {
|
||||||
|
t.Errorf("TextFormat %d: expected %v, got %v", i, *tt.result, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ps.FieldDescriptions[0].FormatCode = pgx.BinaryFormatCode
|
||||||
|
for i, tt := range tests {
|
||||||
|
inputBuf := &bytes.Buffer{}
|
||||||
|
err = tt.result.EncodeBinary(inputBuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("BinaryFormat %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf []byte
|
||||||
|
err := conn.QueryRow("test", inputBuf.Bytes()[4:]).Scan(&buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("BinaryFormat %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputBuf := &bytes.Buffer{}
|
||||||
|
pgio.WriteInt32(outputBuf, int32(len(buf)))
|
||||||
|
outputBuf.Write(buf)
|
||||||
|
var r pgtype.Date
|
||||||
|
err = r.DecodeBinary(outputBuf)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("BinaryFormat %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if r != *tt.result {
|
||||||
|
t.Errorf("BinaryFormat %d: expected %v, got %v", i, tt.result, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDateConvertFrom(t *testing.T) {
|
||||||
|
type _time time.Time
|
||||||
|
|
||||||
|
successfulTests := []struct {
|
||||||
|
source interface{}
|
||||||
|
result *pgtype.Date
|
||||||
|
}{
|
||||||
|
{source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(1900, 1, 1)},
|
||||||
|
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(1970, 1, 1)},
|
||||||
|
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(1999, 12, 31)},
|
||||||
|
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(2000, 1, 1)},
|
||||||
|
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(2000, 1, 2)},
|
||||||
|
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.NewDate(2200, 1, 1)},
|
||||||
|
{source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local)), result: pgtype.NewDate(1970, 1, 1)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range successfulTests {
|
||||||
|
var d pgtype.Date
|
||||||
|
err := d.ConvertFrom(tt.source)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1071,8 +1071,6 @@ func Encode(wbuf *WriteBuf, oid OID, arg interface{}) error {
|
||||||
switch arg := arg.(type) {
|
switch arg := arg.(type) {
|
||||||
case []string:
|
case []string:
|
||||||
return encodeStringSlice(wbuf, oid, arg)
|
return encodeStringSlice(wbuf, oid, arg)
|
||||||
case bool:
|
|
||||||
return encodeBool(wbuf, oid, arg)
|
|
||||||
case []bool:
|
case []bool:
|
||||||
return encodeBoolSlice(wbuf, oid, arg)
|
return encodeBoolSlice(wbuf, oid, arg)
|
||||||
case Char:
|
case Char:
|
||||||
|
@ -2023,7 +2021,12 @@ func decodeDate(vr *ValueReader) time.Time {
|
||||||
func encodeTime(w *WriteBuf, oid OID, value time.Time) error {
|
func encodeTime(w *WriteBuf, oid OID, value time.Time) error {
|
||||||
switch oid {
|
switch oid {
|
||||||
case DateOID:
|
case DateOID:
|
||||||
return pgtype.DateFromTime(value).EncodeBinary(w)
|
var d pgtype.Date
|
||||||
|
err := d.ConvertFrom(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.EncodeBinary(w)
|
||||||
case TimestampTzOID, TimestampOID:
|
case TimestampTzOID, TimestampOID:
|
||||||
return pgtype.TimestamptzFromTime(value).EncodeBinary(w)
|
return pgtype.TimestamptzFromTime(value).EncodeBinary(w)
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in New Issue