Fix timesatmp #2021
|
@ -313,8 +313,12 @@ func (db *mssql) SQLType(c *schemas.Column) string {
|
|||
if c.Length == 0 {
|
||||
res += "(MAX)"
|
||||
}
|
||||
case schemas.TimeStamp:
|
||||
res = schemas.DateTime
|
||||
case schemas.TimeStamp, schemas.DateTime:
|
||||
if c.Length > 3 {
|
||||
res = "DATETIME2"
|
||||
} else {
|
||||
return schemas.DateTime
|
||||
}
|
||||
case schemas.TimeStampz:
|
||||
res = "DATETIMEOFFSET"
|
||||
c.Length = 7
|
||||
|
@ -357,7 +361,7 @@ func (db *mssql) SQLType(c *schemas.Column) string {
|
|||
res = t
|
||||
}
|
||||
|
||||
if res == schemas.Int || res == schemas.Bit || res == schemas.DateTime {
|
||||
if res == schemas.Int || res == schemas.Bit {
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -498,6 +502,12 @@ func (db *mssql) GetColumns(queryer core.Queryer, ctx context.Context, tableName
|
|||
col.Length /= 2
|
||||
col.Length2 /= 2
|
||||
}
|
||||
case "DATETIME2":
|
||||
col.SQLType = schemas.SQLType{Name: schemas.DateTime, DefaultLength: 7, DefaultLength2: 0}
|
||||
col.Length = scale
|
||||
case "DATETIME":
|
||||
col.SQLType = schemas.SQLType{Name: schemas.DateTime, DefaultLength: 3, DefaultLength2: 0}
|
||||
col.Length = scale
|
||||
case "IMAGE":
|
||||
col.SQLType = schemas.SQLType{Name: schemas.VarBinary, DefaultLength: 0, DefaultLength2: 0}
|
||||
case "NCHAR":
|
||||
|
|
|
@ -5,50 +5,57 @@
|
|||
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.Date:
|
||||
return t.Format("2006-01-02"), nil
|
||||
case schemas.Time:
|
||||
var layout = "15:04:05"
|
||||
if col.Length > 0 {
|
||||
layout += "." + strings.Repeat("0", col.Length)
|
||||
}
|
||||
return t.Format(layout), nil
|
||||
case schemas.DateTime, schemas.TimeStamp:
|
||||
var layout = "2006-01-02 15:04:05"
|
||||
if col.Length > 0 {
|
||||
layout += "." + strings.Repeat("0", col.Length)
|
||||
}
|
||||
return t.Format(layout), 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))
|
||||
}
|
||||
|
|
10
engine.go
10
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
|
||||
|
|
|
@ -15,8 +15,12 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func formatTime(t time.Time) string {
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
func formatTime(t time.Time, scales ...int) string {
|
||||
var layout = "2006-01-02 15:04:05"
|
||||
if len(scales) > 0 && scales[0] > 0 {
|
||||
layout += "." + strings.Repeat("0", scales[0])
|
||||
}
|
||||
return t.Format(layout)
|
||||
}
|
||||
|
||||
func TestTimeUserTime(t *testing.T) {
|
||||
|
@ -565,3 +569,53 @@ func TestDeletedInt64(t *testing.T) {
|
|||
assert.True(t, has)
|
||||
assert.EqualValues(t, d1, d4)
|
||||
}
|
||||
|
||||
func TestTimestamp(t *testing.T) {
|
||||
{
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
type TimestampStruct struct {
|
||||
Id int64
|
||||
InsertTime time.Time `xorm:"DATETIME(6)"`
|
||||
}
|
||||
|
||||
assertSync(t, new(TimestampStruct))
|
||||
|
||||
var d1 = TimestampStruct{
|
||||
InsertTime: time.Now(),
|
||||
}
|
||||
cnt, err := testEngine.Insert(&d1)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var d2 TimestampStruct
|
||||
has, err := testEngine.ID(d1.Id).Get(&d2)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, formatTime(d1.InsertTime, 6), formatTime(d2.InsertTime, 6))
|
||||
}
|
||||
|
||||
/*{
|
||||
assert.NoError(t, PrepareEngine())
|
||||
|
||||
type TimestampzStruct struct {
|
||||
Id int64
|
||||
InsertTime time.Time `xorm:"TIMESTAMPZ"`
|
||||
}
|
||||
|
||||
assertSync(t, new(TimestampzStruct))
|
||||
|
||||
var d3 = TimestampzStruct{
|
||||
InsertTime: time.Now(),
|
||||
}
|
||||
cnt, err := testEngine.Insert(&d3)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 1, cnt)
|
||||
|
||||
var d4 TimestampzStruct
|
||||
has, err := testEngine.ID(d3.Id).Get(&d4)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.EqualValues(t, formatTime(d3.InsertTime, 6), formatTime(d4.InsertTime, 6))
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user