diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a124211e89..25b21b57419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements +* [#12995](https://github.com/cosmos/cosmos-sdk/pull/12995) Add `FormatTime` and `ParseTimeString` methods * [#12952](https://github.com/cosmos/cosmos-sdk/pull/12952) Replace keyring module to Cosmos fork. * [#12352](https://github.com/cosmos/cosmos-sdk/pull/12352) Move the `RegisterSwaggerAPI` logic into a separate helper function in the server package. * [#12876](https://github.com/cosmos/cosmos-sdk/pull/12876) Remove proposer-based rewards. diff --git a/types/utils.go b/types/utils.go index efd9a9c71a8..f299756fdb0 100644 --- a/types/utils.go +++ b/types/utils.go @@ -63,17 +63,42 @@ const SortableTimeFormat = "2006-01-02T15:04:05.000000000" // Formats a time.Time into a []byte that can be sorted func FormatTimeBytes(t time.Time) []byte { - return []byte(t.UTC().Round(0).Format(SortableTimeFormat)) + return []byte(FormatTimeString(t)) +} + +// Formats a time.Time into a string +func FormatTimeString(t time.Time) string { + return t.UTC().Round(0).Format(SortableTimeFormat) } // Parses a []byte encoded using FormatTimeKey back into a time.Time func ParseTimeBytes(bz []byte) (time.Time, error) { - str := string(bz) - t, err := time.Parse(SortableTimeFormat, str) + return ParseTime(bz) +} + +// Parses an encoded type using FormatTimeKey back into a time.Time +func ParseTime(T any) (time.Time, error) { //nolint:gocritic + var ( + result time.Time + err error + ) + + switch t := T.(type) { + case time.Time: + result, err = t, nil + case []byte: + result, err = time.Parse(SortableTimeFormat, string(t)) + case string: + result, err = time.Parse(SortableTimeFormat, t) + default: + return time.Time{}, fmt.Errorf("unexpected type %T", t) + } + if err != nil { - return t, err + return result, err } - return t.UTC().Round(0), nil + + return result.UTC().Round(0), nil } // NewLevelDB instantiate a new LevelDB instance according to DBBackend. diff --git a/types/utils_test.go b/types/utils_test.go index 7a196acafe2..8bdba4082b3 100644 --- a/types/utils_test.go +++ b/types/utils_test.go @@ -110,6 +110,12 @@ func (s *utilsTestSuite) TestFormatTimeBytes() { s.Require().Equal("2020-03-03T19:54:00.000000000", string(sdk.FormatTimeBytes(tm))) } +func (s *utilsTestSuite) TestFormatTimeString() { + tm, err := time.Parse("Jan 2, 2006 at 3:04pm (MST)", "Mar 3, 2020 at 7:54pm (UTC)") + s.Require().NoError(err) + s.Require().Equal("2020-03-03T19:54:00.000000000", sdk.FormatTimeString(tm)) +} + func (s *utilsTestSuite) TestParseTimeBytes() { tm, err := sdk.ParseTimeBytes([]byte("2020-03-03T19:54:00.000000000")) s.Require().NoError(err) @@ -119,6 +125,58 @@ func (s *utilsTestSuite) TestParseTimeBytes() { s.Require().Error(err) } +func (s *utilsTestSuite) TestParseTime() { + testCases := []struct { + name string + input any + expectErr bool + expectedOutput string + }{ + { + name: "valid time string", + input: "2020-03-03T19:54:00.000000000", + expectErr: false, + }, + { + name: "valid time []byte", + input: []byte("2020-03-03T19:54:00.000000000"), + expectErr: false, + }, + { + name: "valid time", + input: time.Date(2020, 3, 3, 19, 54, 0, 0, time.UTC), + expectErr: false, + }, + { + name: "valid time different timezone", + input: func() time.Time { + ams, _ := time.LoadLocation("Asia/Seoul") // no daylight saving time + return time.Date(2020, 3, 4, 4, 54, 0, 0, ams) + }(), + expectErr: false, + }, + { + name: "invalid time", + input: struct{}{}, + expectErr: true, + }, + } + + for _, tc := range testCases { + tc := tc + + s.Run(tc.name, func() { + tm, err := sdk.ParseTime(tc.input) + if tc.expectErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().True(tm.Equal(time.Date(2020, 3, 3, 19, 54, 0, 0, time.UTC))) + } + }) + } +} + func (s *utilsTestSuite) TestAppendParseBytes() { test1 := "test1" test2 := "testString2"