From 54606d5bfd0be1c35b078cfd581d6fd7817278e9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 3 Aug 2021 13:45:41 +0800 Subject: [PATCH] Fix timesatmp --- dialects/time.go | 69 +++++++++++++++++--------------- engine.go | 10 ++--- integrations/time_test.go | 2 +- internal/convert/time.go | 9 +++++ internal/statements/statement.go | 6 ++- internal/statements/update.go | 5 ++- internal/statements/values.go | 4 +- session_delete.go | 5 ++- session_insert.go | 38 +++++++++--------- session_update.go | 10 ++++- 10 files changed, 93 insertions(+), 65 deletions(-) diff --git a/dialects/time.go b/dialects/time.go index 5aee0c10..209023b7 100644 --- a/dialects/time.go +++ b/dialects/time.go @@ -5,50 +5,53 @@ package dialects import ( + "strings" "time" "xorm.io/xorm/schemas" ) -// FormatTime format time as column type -func FormatTime(dialect Dialect, sqlTypeName string, t time.Time) (v interface{}) { - switch sqlTypeName { - case schemas.Time: - s := t.Format("2006-01-02 15:04:05") // time.RFC3339 - v = s[11:19] - case schemas.Date: - v = t.Format("2006-01-02") - case schemas.DateTime, schemas.TimeStamp, schemas.Varchar: // !DarthPestilane! format time when sqlTypeName is schemas.Varchar. - if dialect.URI().DBType == schemas.ORACLE { - v = t - } else { - v = t.Format("2006-01-02 15:04:05") - } - case schemas.TimeStampz: - if dialect.URI().DBType == schemas.MSSQL { - v = t.Format("2006-01-02T15:04:05.9999999Z07:00") - } else { - v = t.Format(time.RFC3339Nano) - } - case schemas.BigInt, schemas.Int: - v = t.Unix() - default: - v = t - } - return -} - // FormatColumnTime format column time -func FormatColumnTime(dialect Dialect, defaultTimeZone *time.Location, col *schemas.Column, t time.Time) (v interface{}) { +func FormatColumnTime(dialect Dialect, dbLocation *time.Location, col *schemas.Column, t time.Time) (interface{}, error) { if t.IsZero() { if col.Nullable { - return nil + return nil, nil + } + + if col.SQLType.IsNumeric() { + return 0, nil } - return "" } + var tmZone = dbLocation if col.TimeZone != nil { - return FormatTime(dialect, col.SQLType.Name, t.In(col.TimeZone)) + tmZone = col.TimeZone + } + + t = t.In(tmZone) + + switch col.SQLType.Name { + case schemas.Time: + s := t.Format("2006-01-02 15:04:05") // time.RFC3339 + return s[11:19], nil + case schemas.Date: + return t.Format("2006-01-02"), nil + case schemas.DateTime, schemas.TimeStamp: + if col.Length > 0 { + return t.Format("2006-01-02 15:04:05." + strings.Repeat("0", col.Length)), nil + } + return t.Format("2006-01-02 15:04:05"), nil + case schemas.Varchar: + return t.Format("2006-01-02 15:04:05"), nil + case schemas.TimeStampz: + if dialect.URI().DBType == schemas.MSSQL { + return t.Format("2006-01-02T15:04:05.9999999Z07:00"), nil + } else { + return t.Format(time.RFC3339Nano), nil + } + case schemas.BigInt, schemas.Int: + return t.Unix(), nil + default: + return t, nil } - return FormatTime(dialect, col.SQLType.Name, t.In(defaultTimeZone)) } diff --git a/engine.go b/engine.go index 20c07e13..133e9553 100644 --- a/engine.go +++ b/engine.go @@ -1226,13 +1226,13 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) { } // nowTime return current time -func (engine *Engine) nowTime(col *schemas.Column) (interface{}, time.Time) { +func (engine *Engine) nowTime(col *schemas.Column) (interface{}, time.Time, error) { t := time.Now() - var tz = engine.DatabaseTZ - if !col.DisableTimeZone && col.TimeZone != nil { - tz = col.TimeZone + result, err := dialects.FormatColumnTime(engine.dialect, engine.DatabaseTZ, col, t) + if err != nil { + return nil, time.Time{}, err } - return dialects.FormatTime(engine.dialect, col.SQLType.Name, t.In(tz)), t.In(engine.TZLocation) + return result, t.In(engine.TZLocation), nil } // GetColumnMapper returns the column name mapper diff --git a/integrations/time_test.go b/integrations/time_test.go index 8700e963..39192dc8 100644 --- a/integrations/time_test.go +++ b/integrations/time_test.go @@ -575,7 +575,7 @@ func TestTimestamp(t *testing.T) { type TimestampStruct struct { Id int64 - InsertTime time.Time `xorm:"timestamp"` + InsertTime time.Time `xorm:"DATETIME(6)"` } assertSync(t, new(TimestampStruct)) diff --git a/internal/convert/time.go b/internal/convert/time.go index ecb30a3f..e53a19cd 100644 --- a/internal/convert/time.go +++ b/internal/convert/time.go @@ -8,6 +8,7 @@ import ( "database/sql" "fmt" "strconv" + "strings" "time" "xorm.io/xorm/internal/utils" @@ -39,6 +40,14 @@ func String2Time(s string, originalLocation *time.Location, convertedLocation *t } dt = dt.In(convertedLocation) return &dt, nil + } else if len(s) >= 21 && s[19] == '.' { + var layout = "2006-01-02 15:04:05." + strings.Repeat("0", len(s)-20) + dt, err := time.ParseInLocation(layout, s, originalLocation) + if err != nil { + return nil, err + } + dt = dt.In(convertedLocation) + return &dt, nil } else { i, err := strconv.ParseInt(s, 10, 64) if err == nil { diff --git a/internal/statements/statement.go b/internal/statements/statement.go index 8e3c083c..adbeb1c2 100644 --- a/internal/statements/statement.go +++ b/internal/statements/statement.go @@ -734,7 +734,11 @@ func (statement *Statement) asDBCond(fieldValue reflect.Value, fieldType reflect if !requiredField && (t.IsZero() || !fieldValue.IsValid()) { return nil, false, nil } - return dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t), true, nil + res, err := dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) + if err != nil { + return nil, false, err + } + return res, true, nil } else if fieldType.ConvertibleTo(schemas.BigFloatType) { t := fieldValue.Convert(schemas.BigFloatType).Interface().(big.Float) v := t.String() diff --git a/internal/statements/update.go b/internal/statements/update.go index 39a7f829..a8a174f9 100644 --- a/internal/statements/update.go +++ b/internal/statements/update.go @@ -208,7 +208,10 @@ func (statement *Statement) BuildUpdates(tableValue reflect.Value, if !requiredField && (t.IsZero() || !fieldValue.IsValid()) { continue } - val = dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) + val, err = dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) + if err != nil { + return nil, nil, err + } } else if nulType, ok := fieldValue.Interface().(driver.Valuer); ok { val, _ = nulType.Value() if val == nil && !requiredField { diff --git a/internal/statements/values.go b/internal/statements/values.go index ada01755..7351fb79 100644 --- a/internal/statements/values.go +++ b/internal/statements/values.go @@ -87,8 +87,8 @@ func (statement *Statement) Value2Interface(col *schemas.Column, fieldValue refl case reflect.Struct: if fieldType.ConvertibleTo(schemas.TimeType) { t := fieldValue.Convert(schemas.TimeType).Interface().(time.Time) - tf := dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) - return tf, nil + tf, err := dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t) + return tf, err } else if fieldType.ConvertibleTo(nullFloatType) { t := fieldValue.Convert(nullFloatType).Interface().(sql.NullFloat64) if !t.Valid { diff --git a/session_delete.go b/session_delete.go index baabb558..37b9c1cd 100644 --- a/session_delete.go +++ b/session_delete.go @@ -212,7 +212,10 @@ func (session *Session) Delete(beans ...interface{}) (int64, error) { paramsLen := len(condArgs) copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1]) - val, t := session.engine.nowTime(deletedColumn) + val, t, err := session.engine.nowTime(deletedColumn) + if err != nil { + return 0, err + } condArgs[0] = val var colName = deletedColumn.Name diff --git a/session_insert.go b/session_insert.go index b116b9ff..78f0e555 100644 --- a/session_insert.go +++ b/session_insert.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "xorm.io/xorm/dialects" "xorm.io/xorm/internal/convert" "xorm.io/xorm/internal/utils" "xorm.io/xorm/schemas" @@ -137,7 +138,10 @@ func (session *Session) insertMultipleStruct(rowsSlicePtr interface{}) (int64, e continue } if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime { - val, t := session.engine.nowTime(col) + val, t, err := session.engine.nowTime(col) + if err != nil { + return 0, err + } args = append(args, val) var colName = col.Name @@ -427,29 +431,12 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac if col.MapType == schemas.ONLYFROMDB { continue } - - if col.IsDeleted { - colNames = append(colNames, col.Name) - if !col.Nullable { - if col.SQLType.IsNumeric() { - args = append(args, 0) - } else { - args = append(args, time.Time{}.Format("2006-01-02 15:04:05")) - } - } else { - args = append(args, nil) - } - continue - } - if session.statement.OmitColumnMap.Contain(col.Name) { continue } - if len(session.statement.ColumnMap) > 0 && !session.statement.ColumnMap.Contain(col.Name) { continue } - if session.statement.IncrColumns.IsColExist(col.Name) { continue } else if session.statement.DecrColumns.IsColExist(col.Name) { @@ -458,6 +445,16 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac continue } + if col.IsDeleted { + arg, err := dialects.FormatColumnTime(session.engine.dialect, session.engine.DatabaseTZ, col, time.Time{}) + if err != nil { + return nil, nil, err + } + args = append(args, arg) + colNames = append(colNames, col.Name) + continue + } + fieldValuePtr, err := col.ValueOf(bean) if err != nil { return nil, nil, err @@ -478,7 +475,10 @@ func (session *Session) genInsertColumns(bean interface{}) ([]string, []interfac if (col.IsCreated || col.IsUpdated) && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { // if time is non-empty, then set to auto time - val, t := session.engine.nowTime(col) + val, t, err := session.engine.nowTime(col) + if err != nil { + return nil, nil, err + } args = append(args, val) var colName = col.Name diff --git a/session_update.go b/session_update.go index 4f8e6961..7d91346e 100644 --- a/session_update.go +++ b/session_update.go @@ -215,7 +215,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 !session.statement.OmitColumnMap.Contain(table.Updated) { colNames = append(colNames, session.engine.Quote(table.Updated)+" = ?") col := table.UpdatedColumn() - val, t := session.engine.nowTime(col) + val, t, err := session.engine.nowTime(col) + if err != nil { + return 0, err + } if session.engine.dialect.URI().DBType == schemas.ORACLE { args = append(args, t) } else { @@ -521,7 +524,10 @@ func (session *Session) genUpdateColumns(bean interface{}) ([]string, []interfac if col.IsUpdated && session.statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { // if time is non-empty, then set to auto time - val, t := session.engine.nowTime(col) + val, t, err := session.engine.nowTime(col) + if err != nil { + return nil, nil, err + } args = append(args, val) var colName = col.Name