Time docs, tweaks, tests
This commit is contained in:
parent
e6fc057718
commit
e001fb0f0c
5 changed files with 130 additions and 44 deletions
20
README.md
20
README.md
|
@ -23,17 +23,21 @@ Marshals to JSON null if SQL source data is null. Zero (blank) input will not pr
|
||||||
#### null.Int
|
#### null.Int
|
||||||
Nullable int64.
|
Nullable int64.
|
||||||
|
|
||||||
Marshals to JSON null if SQL null. Zero input will not produce a null Int. Can unmarshal from `sql.NullInt64` JSON input.
|
Marshals to JSON null if SQL source data is null. Zero input will not produce a null Int. Can unmarshal from `sql.NullInt64` JSON input.
|
||||||
|
|
||||||
#### null.Float
|
#### null.Float
|
||||||
Nullable float64.
|
Nullable float64.
|
||||||
|
|
||||||
Unlike `zero.Float`, `null.Float` will marshal to null if null. Zero input will not produce a null Float. Can unmarshal from `sql.NullFloat64` JSON input.
|
Marshals to JSON null if SQL source data is null. Zero input will not produce a null Float. Can unmarshal from `sql.NullFloat64` JSON input.
|
||||||
|
|
||||||
#### null.Bool
|
#### null.Bool
|
||||||
Nullable bool.
|
Nullable bool.
|
||||||
|
|
||||||
Unlike `zero.Bool`, `null.Bool` will marshal to null if null. False input will not produce a null Bool. Can unmarshal from `sql.NullBool` JSON input.
|
Marshals to JSON null if SQL source data is null. False input will not produce a null Bool. Can unmarshal from `sql.NullBool` JSON input.
|
||||||
|
|
||||||
|
#### null.Time
|
||||||
|
|
||||||
|
Marshals to JSON null if SQL source data is null. Uses `time.Time`'s marshaler. Can unmarshal from `pq.NullTime` and similar JSON input.
|
||||||
|
|
||||||
### zero package
|
### zero package
|
||||||
|
|
||||||
|
@ -47,17 +51,21 @@ Will marshal to a blank string if null. Blank string input produces a null Strin
|
||||||
#### zero.Int
|
#### zero.Int
|
||||||
Nullable int64.
|
Nullable int64.
|
||||||
|
|
||||||
Will marshal to 0 if null. Blank string or 0 input produces a null Int. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullInt64` JSON input.
|
Will marshal to 0 if null. 0 produces a null Int. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullInt64` JSON input.
|
||||||
|
|
||||||
#### zero.Float
|
#### zero.Float
|
||||||
Nullable float64.
|
Nullable float64.
|
||||||
|
|
||||||
Will marshal to 0 if null. Blank string or 0 input produces a null Float. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullFloat64` JSON input.
|
Will marshal to 0 if null. 0.0 produces a null Float. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullFloat64` JSON input.
|
||||||
|
|
||||||
#### zero.Bool
|
#### zero.Bool
|
||||||
Nullable bool.
|
Nullable bool.
|
||||||
|
|
||||||
Will marshal to false if null. Blank string, false input produces a null Float. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullBool` JSON input.
|
Will marshal to false if null. `false` produces a null Float. Null values and zero values are considered equivalent. Can unmarshal from `sql.NullBool` JSON input.
|
||||||
|
|
||||||
|
#### null.Time
|
||||||
|
|
||||||
|
Will marshal to the zero time if null. Uses `time.Time`'s marshaler. Can unmarshal from `pq.NullTime` and similar JSON input.
|
||||||
|
|
||||||
|
|
||||||
### Bugs
|
### Bugs
|
||||||
|
|
9
time.go
9
time.go
|
@ -55,8 +55,7 @@ func TimeFrom(t time.Time) Time {
|
||||||
// TimeFromPtr creates a new Time that will be null if t is nil.
|
// TimeFromPtr creates a new Time that will be null if t is nil.
|
||||||
func TimeFromPtr(t *time.Time) Time {
|
func TimeFromPtr(t *time.Time) Time {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
var ti time.Time
|
return NewTime(time.Time{}, false)
|
||||||
return NewTime(ti, false)
|
|
||||||
}
|
}
|
||||||
return NewTime(*t, true)
|
return NewTime(*t, true)
|
||||||
}
|
}
|
||||||
|
@ -71,7 +70,7 @@ func (t Time) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
// It supports string, map[string]interface{},
|
// It supports string, object (e.g. pq.NullTime and friends)
|
||||||
// and null input.
|
// and null input.
|
||||||
func (t *Time) UnmarshalJSON(data []byte) error {
|
func (t *Time) UnmarshalJSON(data []byte) error {
|
||||||
var err error
|
var err error
|
||||||
|
@ -86,9 +85,9 @@ func (t *Time) UnmarshalJSON(data []byte) error {
|
||||||
ti, tiOK := x["Time"].(string)
|
ti, tiOK := x["Time"].(string)
|
||||||
valid, validOK := x["Valid"].(bool)
|
valid, validOK := x["Valid"].(bool)
|
||||||
if !tiOK || !validOK {
|
if !tiOK || !validOK {
|
||||||
return fmt.Errorf("json: unmarshalling object into Go value of type null.Time requires key \"Time\" to be of type string and key \"Valid\" to be of type bool; found %T and %T, respectively", x["Time"], x["Valid"])
|
return fmt.Errorf(`json: unmarshalling object into Go value of type null.Time requires key "Time" to be of type string and key "Valid" to be of type bool; found %T and %T, respectively`, x["Time"], x["Valid"])
|
||||||
}
|
}
|
||||||
err = t.Time.UnmarshalJSON([]byte(`"` + ti + `"`))
|
err = t.Time.UnmarshalText([]byte(ti))
|
||||||
t.Valid = valid
|
t.Valid = valid
|
||||||
return err
|
return err
|
||||||
case nil:
|
case nil:
|
||||||
|
|
66
time_test.go
66
time_test.go
|
@ -9,10 +9,11 @@ import (
|
||||||
var (
|
var (
|
||||||
timeString = "2012-12-21T21:21:21Z"
|
timeString = "2012-12-21T21:21:21Z"
|
||||||
timeJSON = []byte(`"` + timeString + `"`)
|
timeJSON = []byte(`"` + timeString + `"`)
|
||||||
blankTimeJSON = []byte(`null`)
|
nullTimeJSON = []byte(`null`)
|
||||||
timeValue, _ = time.Parse(time.RFC3339, timeString)
|
timeValue, _ = time.Parse(time.RFC3339, timeString)
|
||||||
timeMap = []byte(`{"Time":"2012-12-21T21:21:21Z","Valid":true}`)
|
timeObject = []byte(`{"Time":"2012-12-21T21:21:21Z","Valid":true}`)
|
||||||
invalidMap = []byte(`{"Time":"0001-01-01T00:00:00Z","Valid":false}`)
|
nullObject = []byte(`{"Time":"0001-01-01T00:00:00Z","Valid":false}`)
|
||||||
|
badObject = []byte(`{"hello": "world"}`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUnmarshalTimeString(t *testing.T) {
|
func TestUnmarshalTimeString(t *testing.T) {
|
||||||
|
@ -21,20 +22,41 @@ func TestUnmarshalTimeString(t *testing.T) {
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertTime(t, ti, "UnmarshalJSON() json")
|
assertTime(t, ti, "UnmarshalJSON() json")
|
||||||
|
|
||||||
var blank Time
|
var null Time
|
||||||
err = json.Unmarshal(blankTimeJSON, &blank)
|
err = json.Unmarshal(nullTimeJSON, &null)
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertNullTime(t, blank, "blank time json")
|
assertNullTime(t, null, "null time json")
|
||||||
|
|
||||||
var fromMap Time
|
var fromObject Time
|
||||||
err = json.Unmarshal(timeMap, &fromMap)
|
err = json.Unmarshal(timeObject, &fromObject)
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertTime(t, fromMap, "map time json")
|
assertTime(t, fromObject, "time from object json")
|
||||||
|
|
||||||
|
var nullFromObj Time
|
||||||
|
err = json.Unmarshal(nullObject, &nullFromObj)
|
||||||
|
maybePanic(err)
|
||||||
|
assertNullTime(t, nullFromObj, "null from object json")
|
||||||
|
|
||||||
var invalid Time
|
var invalid Time
|
||||||
err = json.Unmarshal(invalidMap, &invalid)
|
err = invalid.UnmarshalJSON(invalidJSON)
|
||||||
maybePanic(err)
|
if _, ok := err.(*json.SyntaxError); !ok {
|
||||||
assertNullTime(t, invalid, "map invalid time json")
|
t.Errorf("expected json.SyntaxError, not %T", err)
|
||||||
|
}
|
||||||
|
assertNullTime(t, invalid, "invalid from object json")
|
||||||
|
|
||||||
|
var bad Time
|
||||||
|
err = json.Unmarshal(badObject, &bad)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error: bad object")
|
||||||
|
}
|
||||||
|
assertNullTime(t, bad, "bad from object json")
|
||||||
|
|
||||||
|
var wrongType Time
|
||||||
|
err = json.Unmarshal(intJSON, &wrongType)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error: wrong type JSON")
|
||||||
|
}
|
||||||
|
assertNullTime(t, wrongType, "wrong type object json")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalTime(t *testing.T) {
|
func TestMarshalTime(t *testing.T) {
|
||||||
|
@ -42,6 +64,11 @@ func TestMarshalTime(t *testing.T) {
|
||||||
data, err := json.Marshal(ti)
|
data, err := json.Marshal(ti)
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertJSONEquals(t, data, string(timeJSON), "non-empty json marshal")
|
assertJSONEquals(t, data, string(timeJSON), "non-empty json marshal")
|
||||||
|
|
||||||
|
ti.Valid = false
|
||||||
|
data, err = json.Marshal(ti)
|
||||||
|
maybePanic(err)
|
||||||
|
assertJSONEquals(t, data, string(nullJSON), "null json marshal")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeFrom(t *testing.T) {
|
func TestTimeFrom(t *testing.T) {
|
||||||
|
@ -80,16 +107,29 @@ func TestTimePointer(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeScan(t *testing.T) {
|
func TestTimeScanValue(t *testing.T) {
|
||||||
var ti Time
|
var ti Time
|
||||||
err := ti.Scan(timeValue)
|
err := ti.Scan(timeValue)
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertTime(t, ti, "scanned time")
|
assertTime(t, ti, "scanned time")
|
||||||
|
if v, err := ti.Value(); v != timeValue || err != nil {
|
||||||
|
t.Error("bad value or err:", v, err)
|
||||||
|
}
|
||||||
|
|
||||||
var null Time
|
var null Time
|
||||||
err = null.Scan(nil)
|
err = null.Scan(nil)
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertNullTime(t, null, "scanned null")
|
assertNullTime(t, null, "scanned null")
|
||||||
|
if v, err := null.Value(); v != nil || err != nil {
|
||||||
|
t.Error("bad value or err:", v, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrong Time
|
||||||
|
err = wrong.Scan(int64(42))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error")
|
||||||
|
}
|
||||||
|
assertNullTime(t, wrong, "scanned wrong")
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertTime(t *testing.T, ti Time, from string) {
|
func assertTime(t *testing.T, ti Time, from string) {
|
||||||
|
|
14
zero/time.go
14
zero/time.go
|
@ -58,8 +58,7 @@ func TimeFrom(t time.Time) Time {
|
||||||
// be null if t is nil or *t is the zero value.
|
// be null if t is nil or *t is the zero value.
|
||||||
func TimeFromPtr(t *time.Time) Time {
|
func TimeFromPtr(t *time.Time) Time {
|
||||||
if t == nil {
|
if t == nil {
|
||||||
var ti time.Time
|
return NewTime(time.Time{}, false)
|
||||||
return NewTime(ti, false)
|
|
||||||
}
|
}
|
||||||
return TimeFrom(*t)
|
return TimeFrom(*t)
|
||||||
}
|
}
|
||||||
|
@ -69,14 +68,13 @@ func TimeFromPtr(t *time.Time) Time {
|
||||||
// if this time is invalid.
|
// if this time is invalid.
|
||||||
func (t Time) MarshalJSON() ([]byte, error) {
|
func (t Time) MarshalJSON() ([]byte, error) {
|
||||||
if !t.Valid {
|
if !t.Valid {
|
||||||
var ti time.Time
|
return (time.Time{}).MarshalJSON()
|
||||||
return json.Marshal(ti)
|
|
||||||
}
|
}
|
||||||
return json.Marshal(t.Time)
|
return t.Time.MarshalJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
// It supports string, map[string]interface{},
|
// It supports string, object (e.g. pq.NullTime and friends)
|
||||||
// and null input.
|
// and null input.
|
||||||
func (t *Time) UnmarshalJSON(data []byte) error {
|
func (t *Time) UnmarshalJSON(data []byte) error {
|
||||||
var err error
|
var err error
|
||||||
|
@ -96,9 +94,9 @@ func (t *Time) UnmarshalJSON(data []byte) error {
|
||||||
ti, tiOK := x["Time"].(string)
|
ti, tiOK := x["Time"].(string)
|
||||||
valid, validOK := x["Valid"].(bool)
|
valid, validOK := x["Valid"].(bool)
|
||||||
if !tiOK || !validOK {
|
if !tiOK || !validOK {
|
||||||
return fmt.Errorf("json: unmarshalling object into Go value of type null.Time requires key \"Time\" to be of type string and key \"Valid\" to be of type bool; found %T and %T, respectively", x["Time"], x["Valid"])
|
return fmt.Errorf(`json: unmarshalling object into Go value of type null.Time requires key "Time" to be of type string and key "Valid" to be of type bool; found %T and %T, respectively`, x["Time"], x["Valid"])
|
||||||
}
|
}
|
||||||
err = t.Time.UnmarshalJSON([]byte(`"` + ti + `"`))
|
err = t.Time.UnmarshalText([]byte(ti))
|
||||||
t.Valid = valid
|
t.Valid = valid
|
||||||
return err
|
return err
|
||||||
case nil:
|
case nil:
|
||||||
|
|
|
@ -12,13 +12,14 @@ var (
|
||||||
zeroTimeJSON = []byte(`"0001-01-01T00:00:00Z"`)
|
zeroTimeJSON = []byte(`"0001-01-01T00:00:00Z"`)
|
||||||
blankTimeJSON = []byte(`null`)
|
blankTimeJSON = []byte(`null`)
|
||||||
timeValue, _ = time.Parse(time.RFC3339, timeString)
|
timeValue, _ = time.Parse(time.RFC3339, timeString)
|
||||||
timeMap = []byte(`{"Time":"2012-12-21T21:21:21Z","Valid":true}`)
|
timeObject = []byte(`{"Time":"2012-12-21T21:21:21Z","Valid":true}`)
|
||||||
invalidMap = []byte(`{"Time":"0001-01-01T00:00:00Z","Valid":false}`)
|
nullObject = []byte(`{"Time":"0001-01-01T00:00:00Z","Valid":false}`)
|
||||||
|
badObject = []byte(`{"hello": "world"}`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUnmarshalTimeString(t *testing.T) {
|
func TestUnmarshalTimeString(t *testing.T) {
|
||||||
var ti Time
|
var ti Time
|
||||||
err := json.Unmarshal(timeMap, &ti)
|
err := json.Unmarshal(timeObject, &ti)
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertTime(t, ti, "UnmarshalJSON() json")
|
assertTime(t, ti, "UnmarshalJSON() json")
|
||||||
|
|
||||||
|
@ -32,15 +33,48 @@ func TestUnmarshalTimeString(t *testing.T) {
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertNullTime(t, zero, "zero time json")
|
assertNullTime(t, zero, "zero time json")
|
||||||
|
|
||||||
var fromMap Time
|
var fromObject Time
|
||||||
err = json.Unmarshal(timeMap, &fromMap)
|
err = json.Unmarshal(timeObject, &fromObject)
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertTime(t, fromMap, "map time json")
|
assertTime(t, fromObject, "map time json")
|
||||||
|
|
||||||
|
var null Time
|
||||||
|
err = json.Unmarshal(nullObject, &null)
|
||||||
|
maybePanic(err)
|
||||||
|
assertNullTime(t, null, "map null time json")
|
||||||
|
|
||||||
|
var nullFromObj Time
|
||||||
|
err = json.Unmarshal(nullObject, &nullFromObj)
|
||||||
|
maybePanic(err)
|
||||||
|
assertNullTime(t, nullFromObj, "null from object json")
|
||||||
|
|
||||||
var invalid Time
|
var invalid Time
|
||||||
err = json.Unmarshal(invalidMap, &invalid)
|
err = invalid.UnmarshalJSON(invalidJSON)
|
||||||
maybePanic(err)
|
if _, ok := err.(*json.SyntaxError); !ok {
|
||||||
assertNullTime(t, invalid, "map invalid time json")
|
t.Errorf("expected json.SyntaxError, not %T", err)
|
||||||
|
}
|
||||||
|
assertNullTime(t, invalid, "invalid from object json")
|
||||||
|
|
||||||
|
var bad Time
|
||||||
|
err = json.Unmarshal(badObject, &bad)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error: bad object")
|
||||||
|
}
|
||||||
|
assertNullTime(t, bad, "bad from object json")
|
||||||
|
|
||||||
|
var wrongType Time
|
||||||
|
err = json.Unmarshal(intJSON, &wrongType)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error: wrong type JSON")
|
||||||
|
}
|
||||||
|
assertNullTime(t, wrongType, "wrong type object json")
|
||||||
|
|
||||||
|
var wrongString Time
|
||||||
|
err = json.Unmarshal(stringJSON, &wrongString)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error: wrong string JSON")
|
||||||
|
}
|
||||||
|
assertNullTime(t, wrongString, "wrong string object json")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalTime(t *testing.T) {
|
func TestMarshalTime(t *testing.T) {
|
||||||
|
@ -105,6 +139,13 @@ func TestTimeScan(t *testing.T) {
|
||||||
err = null.Scan(nil)
|
err = null.Scan(nil)
|
||||||
maybePanic(err)
|
maybePanic(err)
|
||||||
assertNullTime(t, null, "scanned null")
|
assertNullTime(t, null, "scanned null")
|
||||||
|
|
||||||
|
var wrong Time
|
||||||
|
err = wrong.Scan(int64(42))
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error")
|
||||||
|
}
|
||||||
|
assertNullTime(t, wrong, "scanned wrong")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimeValue(t *testing.T) {
|
func TestTimeValue(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue