forked from apache/cassandra-gocql-driver
-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix
date
marshal, unmarshal functions
Changes: 1. Marshallig are `string` with max `5881580-07-11` or min values `-5877641-06-23` return an error before, now returns value. 2. Marshallig into `custom int64` and `custom string`, was unsupported before, now is supported. 3. Marshallig and Unmarshalling are `int32`, `uint32`, `custom int32` and , `custom uint32`, was unsupported before, now is supported. These are the basic representations of `date`. 4. Unmarshalling `zero data` into `string` and `time.Time` led to non-zero values before, now is led to zero values. 5. Unmarshalling into `int64`, `custom int64` and `custom string`, was unsupported before, now is supported. 6. Marshallig `int64`, `time.Time` values which out of the `cql type` range, does not return an error before, now returns an error. 7. Unmarshallig `corrupted data` into `string`, `time.Time`, does not return an error before, now returns an error. 8. Marshalling the `0001-1-1` value of the `time.Time` return `zero data` before, now returns a data that is equivalent to the difference between `0001-1-1` and `1970-1-1`
- Loading branch information
Showing
5 changed files
with
728 additions
and
71 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package date | ||
|
||
import ( | ||
"reflect" | ||
"time" | ||
) | ||
|
||
func Marshal(value interface{}) ([]byte, error) { | ||
switch v := value.(type) { | ||
case nil: | ||
return nil, nil | ||
case int32: | ||
return EncInt32(v) | ||
case int64: | ||
return EncInt64(v) | ||
case uint32: | ||
return EncUint32(v) | ||
case string: | ||
return EncString(v) | ||
case time.Time: | ||
return EncTime(v) | ||
|
||
case *int32: | ||
return EncInt32R(v) | ||
case *int64: | ||
return EncInt64R(v) | ||
case *uint32: | ||
return EncUint32R(v) | ||
case *string: | ||
return EncStringR(v) | ||
case *time.Time: | ||
return EncTimeR(v) | ||
default: | ||
// Custom types (type MyDate uint32) can be serialized only via `reflect` package. | ||
// Later, when generic-based serialization is introduced we can do that via generics. | ||
rv := reflect.TypeOf(value) | ||
if rv.Kind() != reflect.Ptr { | ||
return EncReflect(reflect.ValueOf(v)) | ||
} | ||
return EncReflectR(reflect.ValueOf(v)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
package date | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
const ( | ||
millisecondsInADay int64 = 24 * 60 * 60 * 1000 | ||
centerEpoch int64 = 1 << 31 | ||
maxYear int = 5881580 | ||
minYear int = -5877641 | ||
maxMilliseconds int64 = 185542587100800000 | ||
minMilliseconds int64 = -185542587187200000 | ||
) | ||
|
||
var ( | ||
maxDate = time.Date(5881580, 07, 11, 0, 0, 0, 0, time.UTC) | ||
minDate = time.Date(-5877641, 06, 23, 0, 0, 0, 0, time.UTC) | ||
) | ||
|
||
func errWrongStringFormat(v interface{}) error { | ||
return fmt.Errorf(`failed to marshal date: the (%T)(%[1]v) should have fromat "2006-01-02"`, v) | ||
} | ||
|
||
func EncInt32(v int32) ([]byte, error) { | ||
return encInt32(v), nil | ||
} | ||
|
||
func EncInt32R(v *int32) ([]byte, error) { | ||
if v == nil { | ||
return nil, nil | ||
} | ||
return encInt32(*v), nil | ||
} | ||
|
||
func EncInt64(v int64) ([]byte, error) { | ||
if v > maxMilliseconds || v < minMilliseconds { | ||
return nil, fmt.Errorf("failed to marshal date: the (int64)(%v) value out of range", v) | ||
} | ||
return encInt64(days(v)), nil | ||
} | ||
|
||
func EncInt64R(v *int64) ([]byte, error) { | ||
if v == nil { | ||
return nil, nil | ||
} | ||
return EncInt64(*v) | ||
} | ||
|
||
func EncUint32(v uint32) ([]byte, error) { | ||
return encUint32(v), nil | ||
} | ||
|
||
func EncUint32R(v *uint32) ([]byte, error) { | ||
if v == nil { | ||
return nil, nil | ||
} | ||
return encUint32(*v), nil | ||
} | ||
|
||
func EncTime(v time.Time) ([]byte, error) { | ||
if v.After(maxDate) || v.Before(minDate) { | ||
return nil, fmt.Errorf("failed to marshal date: the (%T)(%s) value should be in the range from -5877641-06-23 to 5881580-07-11", v, v.Format("2006-01-02")) | ||
} | ||
return encTime(v), nil | ||
} | ||
|
||
func EncTimeR(v *time.Time) ([]byte, error) { | ||
if v == nil { | ||
return nil, nil | ||
} | ||
return EncTime(*v) | ||
} | ||
|
||
func EncString(v string) ([]byte, error) { | ||
if v == "" { | ||
return nil, nil | ||
} | ||
var err error | ||
var y, m, d int | ||
var t time.Time | ||
switch ps := strings.Split(v, "-"); len(ps) { | ||
case 3: | ||
if y, err = strconv.Atoi(ps[0]); err != nil { | ||
return nil, errWrongStringFormat(v) | ||
} | ||
if m, err = strconv.Atoi(ps[1]); err != nil { | ||
return nil, errWrongStringFormat(v) | ||
} | ||
if d, err = strconv.Atoi(ps[2]); err != nil { | ||
return nil, errWrongStringFormat(v) | ||
} | ||
case 4: | ||
if y, err = strconv.Atoi(ps[1]); err != nil || ps[0] != "" { | ||
return nil, errWrongStringFormat(v) | ||
} | ||
y = -y | ||
if m, err = strconv.Atoi(ps[2]); err != nil { | ||
return nil, errWrongStringFormat(v) | ||
} | ||
if d, err = strconv.Atoi(ps[3]); err != nil { | ||
return nil, errWrongStringFormat(v) | ||
} | ||
default: | ||
return nil, errWrongStringFormat(v) | ||
} | ||
if y > maxYear || y < minYear { | ||
return nil, fmt.Errorf("failed to marshal date: the (%T)(%[1]v) value should be in the range from -5877641-06-23 to 5881580-07-11", v) | ||
} | ||
t = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC) | ||
if t.After(maxDate) || t.Before(minDate) { | ||
return nil, fmt.Errorf("failed to marshal date: the (%T)(%[1]v) value should be in the range from -5877641-06-23 to 5881580-07-11", v) | ||
} | ||
return encTime(t), nil | ||
} | ||
|
||
func EncStringR(v *string) ([]byte, error) { | ||
if v == nil { | ||
return nil, nil | ||
} | ||
return EncString(*v) | ||
} | ||
|
||
func EncReflect(v reflect.Value) ([]byte, error) { | ||
switch v.Kind() { | ||
case reflect.Int32: | ||
return encInt64(v.Int()), nil | ||
case reflect.Int64: | ||
val := v.Int() | ||
if val > maxMilliseconds || val < minMilliseconds { | ||
return nil, fmt.Errorf("failed to marshal date: the value (%T)(%[1]v) out of range", v.Interface()) | ||
} | ||
return encInt64(days(val)), nil | ||
case reflect.Uint32: | ||
val := v.Uint() | ||
return []byte{byte(val >> 24), byte(val >> 16), byte(val >> 8), byte(val)}, nil | ||
case reflect.String: | ||
return encReflectString(v) | ||
case reflect.Struct: | ||
if v.Type().String() == "gocql.unsetColumn" { | ||
return nil, nil | ||
} | ||
return nil, fmt.Errorf("failed to marshal date: unsupported value type (%T)(%[1]v)", v.Interface()) | ||
default: | ||
return nil, fmt.Errorf("failed to marshal date: unsupported value type (%T)(%[1]v)", v.Interface()) | ||
} | ||
} | ||
|
||
func EncReflectR(v reflect.Value) ([]byte, error) { | ||
if v.IsNil() { | ||
return nil, nil | ||
} | ||
return EncReflect(v.Elem()) | ||
} | ||
|
||
func encReflectString(v reflect.Value) ([]byte, error) { | ||
val := v.String() | ||
if val == "" { | ||
return nil, nil | ||
} | ||
var err error | ||
var y, m, d int | ||
var t time.Time | ||
ps := strings.Split(val, "-") | ||
switch len(ps) { | ||
case 3: | ||
if y, err = strconv.Atoi(ps[0]); err != nil { | ||
return nil, errWrongStringFormat(v.Interface()) | ||
} | ||
if m, err = strconv.Atoi(ps[1]); err != nil { | ||
return nil, errWrongStringFormat(v.Interface()) | ||
} | ||
if d, err = strconv.Atoi(ps[2]); err != nil { | ||
return nil, errWrongStringFormat(v.Interface()) | ||
} | ||
case 4: | ||
if y, err = strconv.Atoi(ps[1]); err != nil { | ||
return nil, errWrongStringFormat(v.Interface()) | ||
} | ||
y = -y | ||
if m, err = strconv.Atoi(ps[2]); err != nil { | ||
return nil, errWrongStringFormat(v.Interface()) | ||
} | ||
if d, err = strconv.Atoi(ps[3]); err != nil { | ||
return nil, errWrongStringFormat(v.Interface()) | ||
} | ||
default: | ||
return nil, errWrongStringFormat(v.Interface()) | ||
} | ||
if y > maxYear || y < minYear { | ||
return nil, fmt.Errorf("failed to marshal date: the (%T)(%[1]v) value should be in the range from -5877641-06-23 to 5881580-07-11", v.Interface()) | ||
} | ||
t = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC) | ||
if t.After(maxDate) || t.Before(minDate) { | ||
return nil, fmt.Errorf("failed to marshal date: the (%T)(%[1]v) value should be in the range from -5877641-06-23 to 5881580-07-11", v.Interface()) | ||
} | ||
return encTime(t), nil | ||
} | ||
|
||
func encInt64(v int64) []byte { | ||
return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} | ||
} | ||
|
||
func encInt32(v int32) []byte { | ||
return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} | ||
} | ||
|
||
func encUint32(v uint32) []byte { | ||
return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} | ||
} | ||
|
||
func encTime(v time.Time) []byte { | ||
d := days(v.UnixMilli()) | ||
return []byte{byte(d >> 24), byte(d >> 16), byte(d >> 8), byte(d)} | ||
} | ||
|
||
func days(v int64) int64 { | ||
return v/millisecondsInADay + centerEpoch | ||
} |
Oops, something went wrong.