From d49c7f96cb746ea2c6ce74badd92b7357d86d616 Mon Sep 17 00:00:00 2001 From: Ivan Cadri Date: Sat, 2 Apr 2016 19:57:47 +0200 Subject: [PATCH] added functionality for column type time updated the model_fields to cater for the time field type --- namespace_test.go | 12 ++++----- orm/cmd_utils.go | 4 ++- orm/db.go | 18 ++++++++----- orm/db_utils.go | 18 ++++++++++--- orm/models_fields.go | 60 +++++++++++++++++++++++++++++++++++++++++--- orm/models_info_f.go | 7 ++++-- orm/models_test.go | 2 ++ orm/orm_test.go | 21 +++++++++++++--- 8 files changed, 117 insertions(+), 25 deletions(-) diff --git a/namespace_test.go b/namespace_test.go index a92ae3ef..fc02b5fb 100644 --- a/namespace_test.go +++ b/namespace_test.go @@ -61,8 +61,8 @@ func TestNamespaceNest(t *testing.T) { ns.Namespace( NewNamespace("/admin"). Get("/order", func(ctx *context.Context) { - ctx.Output.Body([]byte("order")) - }), + ctx.Output.Body([]byte("order")) + }), ) AddNamespace(ns) BeeApp.Handlers.ServeHTTP(w, r) @@ -79,8 +79,8 @@ func TestNamespaceNestParam(t *testing.T) { ns.Namespace( NewNamespace("/admin"). Get("/order/:id", func(ctx *context.Context) { - ctx.Output.Body([]byte(ctx.Input.Param(":id"))) - }), + ctx.Output.Body([]byte(ctx.Input.Param(":id"))) + }), ) AddNamespace(ns) BeeApp.Handlers.ServeHTTP(w, r) @@ -124,8 +124,8 @@ func TestNamespaceFilter(t *testing.T) { ctx.Output.Body([]byte("this is Filter")) }). Get("/user/:id", func(ctx *context.Context) { - ctx.Output.Body([]byte(ctx.Input.Param(":id"))) - }) + ctx.Output.Body([]byte(ctx.Input.Param(":id"))) + }) AddNamespace(ns) BeeApp.Handlers.ServeHTTP(w, r) if w.Body.String() != "this is Filter" { diff --git a/orm/cmd_utils.go b/orm/cmd_utils.go index da0ee8ab..13f35722 100644 --- a/orm/cmd_utils.go +++ b/orm/cmd_utils.go @@ -55,6 +55,8 @@ checkColumn: col = fmt.Sprintf(T["string"], fieldSize) case TypeTextField: col = T["string-text"] + case TypeTimeField: + col = T["time.Time-clock"] case TypeDateField: col = T["time.Time-date"] case TypeDateTimeField: @@ -264,7 +266,7 @@ func getColumnDefault(fi *fieldInfo) string { // These defaults will be useful if there no config value orm:"default" and NOT NULL is on switch fi.fieldType { - case TypeDateField, TypeDateTimeField, TypeTextField: + case TypeTimeField, TypeDateField, TypeDateTimeField, TypeTextField: return v case TypeBitField, TypeSmallIntegerField, TypeIntegerField, diff --git a/orm/db.go b/orm/db.go index efc90e6e..04cdc30a 100644 --- a/orm/db.go +++ b/orm/db.go @@ -24,6 +24,7 @@ import ( ) const ( + formatTime = "15:04:05" formatDate = "2006-01-02" formatDateTime = "2006-01-02 15:04:05" ) @@ -175,7 +176,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val value = field.Float() } } - case TypeDateField, TypeDateTimeField: + case TypeTimeField, TypeDateField, TypeDateTimeField: value = field.Interface() if t, ok := value.(time.Time); ok { d.ins.TimeToDB(&t, tz) @@ -229,7 +230,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val } } switch fi.fieldType { - case TypeDateField, TypeDateTimeField: + case TypeTimeField, TypeDateField, TypeDateTimeField: if fi.autoNow || fi.autoNowAdd && insert { if insert { if t, ok := value.(time.Time); ok && !t.IsZero() { @@ -1098,7 +1099,7 @@ setValue: } else { value = str.String() } - case fieldType == TypeDateField || fieldType == TypeDateTimeField: + case fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField: if str == nil { switch t := val.(type) { case time.Time: @@ -1118,15 +1119,20 @@ setValue: if len(s) >= 19 { s = s[:19] t, err = time.ParseInLocation(formatDateTime, s, tz) - } else { + } else if len(s) >= 10 { if len(s) > 10 { s = s[:10] } t, err = time.ParseInLocation(formatDate, s, tz) + } else if len(s) >= 8 { + if len(s) > 8 { + s = s[:8] + } + t, err = time.ParseInLocation(formatTime, s, tz) } t = t.In(DefaultTimeLoc) - if err != nil && s != "0000-00-00" && s != "0000-00-00 00:00:00" { + if err != nil && s != "00:00:00" && s != "0000-00-00" && s != "0000-00-00 00:00:00" { tErr = err goto end } @@ -1255,7 +1261,7 @@ setValue: field.SetString(value.(string)) } } - case fieldType == TypeDateField || fieldType == TypeDateTimeField: + case fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField: if isNative { if value == nil { value = time.Time{} diff --git a/orm/db_utils.go b/orm/db_utils.go index ff36b286..cf465d02 100644 --- a/orm/db_utils.go +++ b/orm/db_utils.go @@ -74,24 +74,32 @@ outFor: case reflect.String: v := val.String() if fi != nil { - if fi.fieldType == TypeDateField || fi.fieldType == TypeDateTimeField { + if fi.fieldType == TypeTimeField || fi.fieldType == TypeDateField || fi.fieldType == TypeDateTimeField { var t time.Time var err error if len(v) >= 19 { s := v[:19] t, err = time.ParseInLocation(formatDateTime, s, DefaultTimeLoc) - } else { + } else if len(v) >= 10 { s := v if len(v) > 10 { s = v[:10] } t, err = time.ParseInLocation(formatDate, s, tz) + } else { + s := v + if len(s) > 8 { + s = v[:8] + } + t, err = time.ParseInLocation(formatTime, s, tz) } if err == nil { if fi.fieldType == TypeDateField { v = t.In(tz).Format(formatDate) - } else { + } else if fi.fieldType == TypeDateTimeField { v = t.In(tz).Format(formatDateTime) + } else { + v = t.In(tz).Format(formatTime) } } } @@ -137,8 +145,10 @@ outFor: if v, ok := arg.(time.Time); ok { if fi != nil && fi.fieldType == TypeDateField { arg = v.In(tz).Format(formatDate) - } else { + } else if fi.fieldType == TypeDateTimeField { arg = v.In(tz).Format(formatDateTime) + } else { + arg = v.In(tz).Format(formatTime) } } else { typ := val.Type() diff --git a/orm/models_fields.go b/orm/models_fields.go index ad1fb6f9..1bfbfb2f 100644 --- a/orm/models_fields.go +++ b/orm/models_fields.go @@ -25,6 +25,7 @@ const ( TypeBooleanField = 1 << iota TypeCharField TypeTextField + TypeTimeField TypeDateField TypeDateTimeField TypeBitField @@ -46,9 +47,9 @@ const ( // Define some logic enum const ( - IsIntegerField = ^-TypePositiveBigIntegerField >> 4 << 5 - IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 8 << 9 - IsRelField = ^-RelReverseMany >> 14 << 15 + IsIntegerField = ^-TypePositiveBigIntegerField >> 5 << 6 + IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 9 << 10 + IsRelField = ^-RelReverseMany >> 15 << 16 IsFieldType = ^-RelReverseMany<<1 + 1 ) @@ -145,6 +146,59 @@ func (e *CharField) RawValue() interface{} { // verify CharField implement Fielder var _ Fielder = new(CharField) +// A time, represented in go by a time.Time instance. +// only time values like 10:00:00 +// Has a few extra, optional attr tag: +// +// auto_now: +// Automatically set the field to now every time the object is saved. Useful for “last-modified” timestamps. +// Note that the current date is always used; it’s not just a default value that you can override. +// +// auto_now_add: +// Automatically set the field to now when the object is first created. Useful for creation of timestamps. +// Note that the current date is always used; it’s not just a default value that you can override. +// +// eg: `orm:"auto_now"` or `orm:"auto_now_add"` +type TimeField time.Time + +func (e TimeField) Value() time.Time { + return time.Time(e) +} + +func (e *TimeField) Set(d time.Time) { + *e = TimeField(d) +} + +func (e *TimeField) String() string { + return e.Value().String() +} + +func (e *TimeField) FieldType() int { + return TypeDateField +} + +func (e *TimeField) SetRaw(value interface{}) error { + switch d := value.(type) { + case time.Time: + e.Set(d) + case string: + v, err := timeParse(d, formatTime) + if err != nil { + e.Set(v) + } + return err + default: + return fmt.Errorf(" unknown value `%s`", value) + } + return nil +} + +func (e *TimeField) RawValue() interface{} { + return e.Value() +} + +var _ Fielder = new(TimeField) + // DateField A date, represented in go by a time.Time instance. // only date values like 2006-01-02 // Has a few extra, optional attr tag: diff --git a/orm/models_info_f.go b/orm/models_info_f.go index 996a2f40..20a7c9d6 100644 --- a/orm/models_info_f.go +++ b/orm/models_info_f.go @@ -248,6 +248,9 @@ checkType: if fieldType == TypeDateTimeField && tags["type"] == "date" { fieldType = TypeDateField } + if fieldType == TypeTimeField && tags["type"] == "time" { + fieldType = TypeTimeField + } } switch fieldType { @@ -353,7 +356,7 @@ checkType: case TypeTextField: fi.index = false fi.unique = false - case TypeDateField, TypeDateTimeField: + case TypeTimeField, TypeDateField, TypeDateTimeField: if attrs["auto_now"] { fi.autoNow = true } else if attrs["auto_now_add"] { @@ -406,7 +409,7 @@ checkType: fi.index = false } - if fi.auto || fi.pk || fi.unique || fieldType == TypeDateField || fieldType == TypeDateTimeField { + if fi.auto || fi.pk || fi.unique || fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField { // can not set default initial.Clear() } diff --git a/orm/models_test.go b/orm/models_test.go index f42d725e..cbc51dcc 100644 --- a/orm/models_test.go +++ b/orm/models_test.go @@ -112,6 +112,7 @@ type Data struct { Boolean bool Char string `orm:"size(50)"` Text string `orm:"type(text)"` + Time time.Time `orm:"type(time)"` Date time.Time `orm:"type(date)"` DateTime time.Time `orm:"column(datetime)"` Byte byte @@ -136,6 +137,7 @@ type DataNull struct { Boolean bool `orm:"null"` Char string `orm:"null;size(50)"` Text string `orm:"null;type(text)"` + Time time.Time `orm:"null;type(time)"` Date time.Time `orm:"null;type(date)"` DateTime time.Time `orm:"null;column(datetime)"` Byte byte `orm:"null"` diff --git a/orm/orm_test.go b/orm/orm_test.go index 3f6e1566..0c615328 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -34,6 +34,7 @@ var _ = os.PathSeparator var ( testDate = formatDate + " -0700" testDateTime = formatDateTime + " -0700" + testTime = formatTime + " -0700" ) type argAny []interface{} @@ -240,6 +241,7 @@ var DataValues = map[string]interface{}{ "Boolean": true, "Char": "char", "Text": "text", + "Time": time.Now(), "Date": time.Now(), "DateTime": time.Now(), "Byte": byte(1<<8 - 1), @@ -267,7 +269,6 @@ func TestDataTypes(t *testing.T) { e := ind.FieldByName(name) e.Set(reflect.ValueOf(value)) } - id, err := dORM.Insert(&d) throwFail(t, err) throwFail(t, AssertIs(id, 1)) @@ -288,6 +289,9 @@ func TestDataTypes(t *testing.T) { case "DateTime": vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDateTime) value = value.(time.Time).In(DefaultTimeLoc).Format(testDateTime) + case "Time": + vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime) + value = value.(time.Time).In(DefaultTimeLoc).Format(testTime) } throwFail(t, AssertIs(vu == value, true), value, vu) } @@ -1521,6 +1525,7 @@ func TestRawQueryRow(t *testing.T) { Boolean bool Char string Text string + Time time.Time Date time.Time DateTime time.Time Byte byte @@ -1549,14 +1554,14 @@ func TestRawQueryRow(t *testing.T) { Q := dDbBaser.TableQuote() cols := []string{ - "id", "boolean", "char", "text", "date", "datetime", "byte", "rune", "int", "int8", "int16", "int32", + "id", "boolean", "char", "text", "time", "date", "datetime", "byte", "rune", "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "decimal", } sep := fmt.Sprintf("%s, %s", Q, Q) query := fmt.Sprintf("SELECT %s%s%s FROM data WHERE id = ?", Q, strings.Join(cols, sep), Q) var id int values := []interface{}{ - &id, &Boolean, &Char, &Text, &Date, &DateTime, &Byte, &Rune, &Int, &Int8, &Int16, &Int32, + &id, &Boolean, &Char, &Text, &Time, &Date, &DateTime, &Byte, &Rune, &Int, &Int8, &Int16, &Int32, &Int64, &Uint, &Uint8, &Uint16, &Uint32, &Uint64, &Float32, &Float64, &Decimal, } err := dORM.Raw(query, 1).QueryRow(values...) @@ -1567,6 +1572,10 @@ func TestRawQueryRow(t *testing.T) { switch col { case "id": throwFail(t, AssertIs(id, 1)) + case "time": + v = v.(time.Time).In(DefaultTimeLoc) + value := dataValues[col].(time.Time).In(DefaultTimeLoc) + throwFail(t, AssertIs(v, value, testTime)) case "date": v = v.(time.Time).In(DefaultTimeLoc) value := dataValues[col].(time.Time).In(DefaultTimeLoc) @@ -1614,6 +1623,9 @@ func TestQueryRows(t *testing.T) { e := ind.FieldByName(name) vu := e.Interface() switch name { + case "Time": + vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime) + value = value.(time.Time).In(DefaultTimeLoc).Format(testTime) case "Date": vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate) value = value.(time.Time).In(DefaultTimeLoc).Format(testDate) @@ -1638,6 +1650,9 @@ func TestQueryRows(t *testing.T) { e := ind.FieldByName(name) vu := e.Interface() switch name { + case "Time": + vu = vu.(time.Time).In(DefaultTimeLoc).Format(testTime) + value = value.(time.Time).In(DefaultTimeLoc).Format(testTime) case "Date": vu = vu.(time.Time).In(DefaultTimeLoc).Format(testDate) value = value.(time.Time).In(DefaultTimeLoc).Format(testDate)