diff --git a/cmd/gf/internal/cmd/gendao/gendao_structure.go b/cmd/gf/internal/cmd/gendao/gendao_structure.go index e9acc8bff77..5603bd3a15a 100644 --- a/cmd/gf/internal/cmd/gendao/gendao_structure.go +++ b/cmd/gf/internal/cmd/gendao/gendao_structure.go @@ -99,7 +99,7 @@ func generateStructFieldDefinition( } localTypeNameStr = string(localTypeName) switch localTypeName { - case gdb.LocalTypeDate, gdb.LocalTypeDatetime: + case gdb.LocalTypeDate, gdb.LocalTypeTime, gdb.LocalTypeDatetime: if in.StdTime { localTypeNameStr = "time.Time" } else { diff --git a/contrib/drivers/mysql/mysql_z_unit_core_test.go b/contrib/drivers/mysql/mysql_z_unit_core_test.go index 41fcf475c62..b656788980f 100644 --- a/contrib/drivers/mysql/mysql_z_unit_core_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_core_test.go @@ -104,6 +104,7 @@ func Test_DB_Insert(t *testing.T) { "password": "25d55ad283aa400af464c76d713c07ad", "nickname": "T1", "create_time": gtime.Now().String(), + "create_date": gtime.Date(), }) t.AssertNil(err) @@ -114,6 +115,7 @@ func Test_DB_Insert(t *testing.T) { "password": "25d55ad283aa400af464c76d713c07ad", "nickname": "name_2", "create_time": gtime.Now().String(), + "create_date": gtime.Date(), }) t.AssertNil(err) n, _ := result.RowsAffected() @@ -121,19 +123,22 @@ func Test_DB_Insert(t *testing.T) { // struct type User struct { - Id int `gconv:"id"` - Passport string `json:"passport"` - Password string `gconv:"password"` - Nickname string `gconv:"nickname"` - CreateTime string `json:"create_time"` - } - timeStr := gtime.New("2024-10-01 12:01:01").String() + Id int `gconv:"id"` + Passport string `json:"passport"` + Password string `gconv:"password"` + Nickname string `gconv:"nickname"` + CreateTime string `json:"create_time"` + CreateDate *gtime.Time `json:"create_date"` + } + gTime := gtime.New("2024-10-01 12:01:01") + timeStr, dateStr := gTime.String(), "2024-10-01 00:00:00" result, err = db.Insert(ctx, table, User{ Id: 3, Passport: "user_3", Password: "25d55ad283aa400af464c76d713c07ad", Nickname: "name_3", CreateTime: timeStr, + CreateDate: gTime, }) t.AssertNil(err) n, _ = result.RowsAffected() @@ -147,15 +152,18 @@ func Test_DB_Insert(t *testing.T) { t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad") t.Assert(one["nickname"].String(), "name_3") t.Assert(one["create_time"].GTime().String(), timeStr) + t.Assert(one["create_date"].GTime().String(), dateStr) // *struct - timeStr = gtime.New("2024-10-01 12:01:01").String() + gTime = gtime.New("2024-10-01 12:01:01") + timeStr, dateStr = gTime.String(), "2024-10-01 00:00:00" result, err = db.Insert(ctx, table, &User{ Id: 4, Passport: "t4", Password: "25d55ad283aa400af464c76d713c07ad", Nickname: "name_4", CreateTime: timeStr, + CreateDate: gTime, }) t.AssertNil(err) n, _ = result.RowsAffected() @@ -168,9 +176,11 @@ func Test_DB_Insert(t *testing.T) { t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad") t.Assert(one["nickname"].String(), "name_4") t.Assert(one["create_time"].GTime().String(), timeStr) + t.Assert(one["create_date"].GTime().String(), dateStr) // batch with Insert - timeStr = gtime.New("2024-10-01 12:01:01").String() + gTime = gtime.New("2024-10-01 12:01:01") + timeStr, dateStr = gTime.String(), "2024-10-01 00:00:00" r, err := db.Insert(ctx, table, g.Slice{ g.Map{ "id": 200, @@ -178,6 +188,7 @@ func Test_DB_Insert(t *testing.T) { "password": "25d55ad283aa400af464c76d71qw07ad", "nickname": "T200", "create_time": timeStr, + "create_date": gTime, }, g.Map{ "id": 300, @@ -185,6 +196,7 @@ func Test_DB_Insert(t *testing.T) { "password": "25d55ad283aa400af464c76d713c07ad", "nickname": "T300", "create_time": timeStr, + "create_date": gTime, }, }) t.AssertNil(err) @@ -198,6 +210,7 @@ func Test_DB_Insert(t *testing.T) { t.Assert(one["password"].String(), "25d55ad283aa400af464c76d71qw07ad") t.Assert(one["nickname"].String(), "T200") t.Assert(one["create_time"].GTime().String(), timeStr) + t.Assert(one["create_date"].GTime().String(), dateStr) }) } @@ -1644,7 +1657,7 @@ func Test_Core_ClearTableFields(t *testing.T) { gtest.C(t, func(t *gtest.T) { fields, err := db.TableFields(ctx, table) t.AssertNil(err) - t.Assert(len(fields), 5) + t.Assert(len(fields), 6) }) gtest.C(t, func(t *gtest.T) { err := db.GetCore().ClearTableFields(ctx, table) diff --git a/contrib/drivers/mysql/mysql_z_unit_init_test.go b/contrib/drivers/mysql/mysql_z_unit_init_test.go index ea4b8694cfd..1326378c36e 100644 --- a/contrib/drivers/mysql/mysql_z_unit_init_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_init_test.go @@ -129,6 +129,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { password char(32) NULL, nickname varchar(45) NULL, create_time timestamp(6) NULL, + create_date date NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; `, name, diff --git a/contrib/drivers/mysql/mysql_z_unit_issue_test.go b/contrib/drivers/mysql/mysql_z_unit_issue_test.go index e30bc1733c5..0ccc54ab3d1 100644 --- a/contrib/drivers/mysql/mysql_z_unit_issue_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_issue_test.go @@ -1063,7 +1063,7 @@ func Test_Issue2552_ClearTableFieldsAll(t *testing.T) { ctx = context.Background() sqlArray, err = gdb.CatchSQL(ctx, func(ctx context.Context) error { one, err := db.Model(table).Ctx(ctx).One() - t.Assert(len(one), 5) + t.Assert(len(one), 6) return err }) t.AssertNil(err) @@ -1078,7 +1078,7 @@ func Test_Issue2552_ClearTableFieldsAll(t *testing.T) { ctx = context.Background() sqlArray, err = gdb.CatchSQL(ctx, func(ctx context.Context) error { one, err := db.Model(table).Ctx(ctx).One() - t.Assert(len(one), 4) + t.Assert(len(one), 5) return err }) t.AssertNil(err) @@ -1109,7 +1109,7 @@ func Test_Issue2552_ClearTableFields(t *testing.T) { ctx = context.Background() sqlArray, err = gdb.CatchSQL(ctx, func(ctx context.Context) error { one, err := db.Model(table).Ctx(ctx).One() - t.Assert(len(one), 5) + t.Assert(len(one), 6) return err }) t.AssertNil(err) @@ -1124,7 +1124,7 @@ func Test_Issue2552_ClearTableFields(t *testing.T) { ctx = context.Background() sqlArray, err = gdb.CatchSQL(ctx, func(ctx context.Context) error { one, err := db.Model(table).Ctx(ctx).One() - t.Assert(len(one), 4) + t.Assert(len(one), 5) return err }) t.AssertNil(err) diff --git a/contrib/drivers/mysql/mysql_z_unit_model_test.go b/contrib/drivers/mysql/mysql_z_unit_model_test.go index e6ef9fc9c6a..02532d42b2c 100644 --- a/contrib/drivers/mysql/mysql_z_unit_model_test.go +++ b/contrib/drivers/mysql/mysql_z_unit_model_test.go @@ -2335,7 +2335,7 @@ func Test_Model_FieldsEx(t *testing.T) { r, err := db.Model(table).FieldsEx("create_time, id").Where("id in (?)", g.Slice{1, 2}).Order("id asc").All() t.AssertNil(err) t.Assert(len(r), 2) - t.Assert(len(r[0]), 3) + t.Assert(len(r[0]), 4) t.Assert(r[0]["id"], "") t.Assert(r[0]["passport"], "user_1") t.Assert(r[0]["password"], "pass_1") @@ -2982,7 +2982,7 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) { "CreateTime": 1, }).Where("id", 2).One() t.AssertNil(err) - t.Assert(len(one), 2) + t.Assert(len(one), 3) t.Assert(one["id"], 2) t.Assert(one["nickname"], "name_2") }) @@ -2999,7 +2999,7 @@ func Test_Model_FieldsEx_AutoMapping(t *testing.T) { CreateTime: 0, }).Where("id", 2).One() t.AssertNil(err) - t.Assert(len(one), 2) + t.Assert(len(one), 3) t.Assert(one["id"], 2) t.Assert(one["nickname"], "name_2") }) @@ -3157,8 +3157,8 @@ func Test_TimeZoneInsert(t *testing.T) { gtest.AssertNil(err) CreateTime := "2020-11-22 12:23:45" - UpdateTime := "2020-11-22 13:23:45" - DeleteTime := "2020-11-22 14:23:45" + UpdateTime := "2020-11-22 13:23:46" + DeleteTime := "2020-11-22 14:23:47" type User struct { Id int `json:"id"` CreatedAt *gtime.Time `json:"created_at"` @@ -3176,13 +3176,14 @@ func Test_TimeZoneInsert(t *testing.T) { } gtest.C(t, func(t *gtest.T) { - _, _ = db.Model(tableName).Unscoped().Insert(u) + _, err = db.Model(tableName).Unscoped().Insert(u) + t.AssertNil(err) userEntity := &User{} - err := db.Model(tableName).Where("id", 1).Unscoped().Scan(&userEntity) + err = db.Model(tableName).Where("id", 1).Unscoped().Scan(&userEntity) t.AssertNil(err) t.Assert(userEntity.CreatedAt.String(), "2020-11-22 11:23:45") - t.Assert(userEntity.UpdatedAt.String(), "2020-11-22 12:23:45") - t.Assert(gtime.NewFromTime(userEntity.DeletedAt).String(), "2020-11-22 13:23:45") + t.Assert(userEntity.UpdatedAt.String(), "2020-11-22 12:23:46") + t.Assert(gtime.NewFromTime(userEntity.DeletedAt).String(), "2020-11-22 13:23:47") }) } diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 8bffa26366d..4d62dea6364 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -446,6 +446,7 @@ type LocalType string const ( LocalTypeUndefined LocalType = "" LocalTypeString LocalType = "string" + LocalTypeTime LocalType = "time" LocalTypeDate LocalType = "date" LocalTypeDatetime LocalType = "datetime" LocalTypeInt LocalType = "int" @@ -492,6 +493,7 @@ const ( fieldTypeBool = "bool" fieldTypeBit = "bit" fieldTypeDate = "date" + fieldTypeTime = "time" fieldTypeDatetime = "datetime" fieldTypeTimestamp = "timestamp" fieldTypeTimestampz = "timestamptz" diff --git a/database/gdb/gdb_core_structure.go b/database/gdb/gdb_core_structure.go index 852c7aa5855..e491d1def18 100644 --- a/database/gdb/gdb_core_structure.go +++ b/database/gdb/gdb_core_structure.go @@ -83,6 +83,10 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field err error convertedValue = fieldValue ) + switch fieldValue.(type) { + case time.Time, *time.Time, gtime.Time, *gtime.Time: + goto Default + } // If `value` implements interface `driver.Valuer`, it then uses the interface for value converting. if valuer, ok := fieldValue.(driver.Valuer); ok { if convertedValue, err = valuer.Value(); err != nil { @@ -90,6 +94,7 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field } return convertedValue, nil } +Default: // Default value converting. var ( rvValue = reflect.ValueOf(fieldValue) @@ -100,6 +105,9 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field rvKind = rvValue.Kind() } switch rvKind { + case reflect.Invalid: + convertedValue = nil + case reflect.Slice, reflect.Array, reflect.Map: // It should ignore the bytes type. if _, ok := fieldValue.([]byte); !ok { @@ -109,7 +117,6 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field return nil, err } } - case reflect.Struct: switch r := fieldValue.(type) { // If the time is zero, it then updates it to nil, @@ -117,11 +124,28 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field case time.Time: if r.IsZero() { convertedValue = nil + } else if fieldType == fieldTypeDate { + convertedValue = r.Format("2006-01-02") + } else if fieldType == fieldTypeTime { + convertedValue = r.Format("15:04:05") + } + + case *time.Time: + if r == nil { + // Nothing to do. + } else if fieldType == fieldTypeDate { + convertedValue = r.Format("2006-01-02") + } else if fieldType == fieldTypeTime { + convertedValue = r.Format("15:04:05") } case gtime.Time: if r.IsZero() { convertedValue = nil + } else if fieldType == fieldTypeDate { + convertedValue = r.Layout("2006-01-02") + } else if fieldType == fieldTypeTime { + convertedValue = r.Layout("15:04:05") } else { convertedValue = r.Time } @@ -129,13 +153,14 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field case *gtime.Time: if r.IsZero() { convertedValue = nil + } else if fieldType == fieldTypeDate { + convertedValue = r.Layout("2006-01-02") + } else if fieldType == fieldTypeTime { + convertedValue = r.Layout("15:04:05") } else { convertedValue = r.Time } - case *time.Time: - // Nothing to do. - case Counter, *Counter: // Nothing to do. @@ -157,6 +182,7 @@ func (c *Core) ConvertValueForField(ctx context.Context, fieldType string, field } } } + return convertedValue, nil } @@ -247,6 +273,10 @@ func (c *Core) CheckLocalTypeForField(ctx context.Context, fieldType string, fie fieldTypeDate: return LocalTypeDate, nil + case + fieldTypeTime: + return LocalTypeTime, nil + case fieldTypeDatetime, fieldTypeTimestamp, @@ -353,13 +383,19 @@ func (c *Core) ConvertValueForLocal( return gconv.Bool(fieldValue), nil case LocalTypeDate: - // Date without time. if t, ok := fieldValue.(time.Time); ok { return gtime.NewFromTime(t).Format("Y-m-d"), nil } t, _ := gtime.StrToTime(gconv.String(fieldValue)) return t.Format("Y-m-d"), nil + case LocalTypeTime: + if t, ok := fieldValue.(time.Time); ok { + return gtime.NewFromTime(t).Format("H:i:s"), nil + } + t, _ := gtime.StrToTime(gconv.String(fieldValue)) + return t.Format("H:i:s"), nil + case LocalTypeDatetime: if t, ok := fieldValue.(time.Time); ok { return gtime.NewFromTime(t), nil diff --git a/database/gdb/gdb_model_soft_time.go b/database/gdb/gdb_model_soft_time.go index c2817d12ad2..bee3da23f07 100644 --- a/database/gdb/gdb_model_soft_time.go +++ b/database/gdb/gdb_model_soft_time.go @@ -339,7 +339,7 @@ func (m *softTimeMaintainer) getConditionByFieldNameAndTypeForSoftDeleting( switch m.softTimeOption.SoftTimeType { case SoftTimeTypeAuto: switch fieldType { - case LocalTypeDate, LocalTypeDatetime: + case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: return fmt.Sprintf(`%s IS NULL`, quotedFieldName) case LocalTypeInt, LocalTypeUint, LocalTypeInt64, LocalTypeUint64, LocalTypeBool: return fmt.Sprintf(`%s=0`, quotedFieldName) @@ -368,7 +368,7 @@ func (m *softTimeMaintainer) GetValueByFieldTypeForCreateOrUpdate( var value any if isDeletedField { switch fieldType { - case LocalTypeDate, LocalTypeDatetime: + case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: value = nil default: value = 0 @@ -378,7 +378,7 @@ func (m *softTimeMaintainer) GetValueByFieldTypeForCreateOrUpdate( switch m.softTimeOption.SoftTimeType { case SoftTimeTypeAuto: switch fieldType { - case LocalTypeDate, LocalTypeDatetime: + case LocalTypeDate, LocalTypeTime, LocalTypeDatetime: value = gtime.Now() case LocalTypeInt, LocalTypeUint, LocalTypeInt64, LocalTypeUint64: value = gtime.Timestamp()