From 5cafa34ae971dc5319c71fd87b133b377165eb2d Mon Sep 17 00:00:00 2001 From: sputn1ck Date: Tue, 8 Aug 2023 15:05:52 +0200 Subject: [PATCH] loopdb: fix timestamp parsing --- loopdb/postgres.go | 2 +- loopdb/sql_test.go | 47 ++++++++++++++++++++++++++++++++++++++++------ loopdb/sqlite.go | 29 ++++++++++++++++++++++------ 3 files changed, 65 insertions(+), 13 deletions(-) diff --git a/loopdb/postgres.go b/loopdb/postgres.go index 7359e63..86d8e6c 100644 --- a/loopdb/postgres.go +++ b/loopdb/postgres.go @@ -115,7 +115,7 @@ func NewPostgresStore(cfg *PostgresConfig, ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - err = baseDB.FixFaultyTimestamps(ctx, parsePostgresTimeStamp) + err = baseDB.FixFaultyTimestamps(ctx) if err != nil { log.Errorf("Failed to fix faulty timestamps: %v", err) return nil, err diff --git a/loopdb/sql_test.go b/loopdb/sql_test.go index 402de8b..b30ea1b 100644 --- a/loopdb/sql_test.go +++ b/loopdb/sql_test.go @@ -375,19 +375,54 @@ func TestIssue615(t *testing.T) { require.NoError(t, err) } - parseFunc := parseSqliteTimeStamp - if testDBType == "postgres" { - parseFunc = parsePostgresTimeStamp - } - // Fix the faulty timestamp. - err = sqlDB.FixFaultyTimestamps(ctxb, parseFunc) + err = sqlDB.FixFaultyTimestamps(ctxb) require.NoError(t, err) _, err = sqlDB.GetLoopOutSwaps(ctxb) require.NoError(t, err) } +func TestTimeConversions(t *testing.T) { + tests := []struct { + timeString string + expectedTime time.Time + }{ + { + timeString: "2018-11-01 00:00:00 +0000 UTC", + expectedTime: time.Date(2018, 11, 1, 0, 0, 0, 0, time.UTC), + }, + { + timeString: "2018-11-01 00:00:01.10000 +0000 UTC", + expectedTime: time.Date(2018, 11, 1, 0, 0, 1, 0, time.UTC), + }, + { + timeString: "2053-12-29T02:40:44.269009408Z", + expectedTime: time.Date( + 2053, 12, 29, 2, 40, 44, 0, time.UTC, + ), + }, + { + timeString: "55563-06-27 02:09:24 +0000 UTC", + expectedTime: time.Date( + 55563, 6, 27, 2, 9, 24, 0, time.UTC, + ), + }, + { + timeString: "2172-03-11 10:01:11.849906176 +0000 UTC", + expectedTime: time.Date( + 2172, 3, 11, 10, 1, 11, 0, time.UTC, + ), + }, + } + + for _, test := range tests { + time, err := parseTimeStamp(test.timeString) + require.NoError(t, err) + require.Equal(t, test.expectedTime, time) + } +} + const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" func randomString(length int) string { diff --git a/loopdb/sqlite.go b/loopdb/sqlite.go index a264822..f8e22bd 100644 --- a/loopdb/sqlite.go +++ b/loopdb/sqlite.go @@ -122,7 +122,7 @@ func NewSqliteStore(cfg *SqliteConfig, network *chaincfg.Params) (*SqliteSwapSto ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - err = baseDB.FixFaultyTimestamps(ctx, parseSqliteTimeStamp) + err = baseDB.FixFaultyTimestamps(ctx) if err != nil { log.Errorf("Failed to fix faulty timestamps: %v", err) return nil, err @@ -209,8 +209,7 @@ func (db *BaseDB) ExecTx(ctx context.Context, txOptions TxOptions, // FixFaultyTimestamps fixes faulty timestamps in the database, caused // by using milliseconds instead of seconds as the publication deadline. -func (b *BaseDB) FixFaultyTimestamps(ctx context.Context, - parseTimeFunc func(string) (time.Time, error)) error { +func (b *BaseDB) FixFaultyTimestamps(ctx context.Context) error { // Manually fetch all the loop out swaps. rows, err := b.DB.QueryContext( @@ -248,7 +247,7 @@ func (b *BaseDB) FixFaultyTimestamps(ctx context.Context, defer tx.Rollback() //nolint: errcheck for _, swap := range loopOutSwaps { - faultyTime, err := parseTimeFunc(swap.PublicationDeadline) + faultyTime, err := parseTimeStamp(swap.PublicationDeadline) if err != nil { return err } @@ -309,6 +308,21 @@ func (r *SqliteTxOptions) ReadOnly() bool { return r.readOnly } +// parseTimeStamp tries to parse a timestamp string with both the +// parseSqliteTimeStamp and parsePostgresTimeStamp functions. +// If both fail, it returns an error. +func parseTimeStamp(dateTimeStr string) (time.Time, error) { + t, err := parseSqliteTimeStamp(dateTimeStr) + if err != nil { + t, err = parsePostgresTimeStamp(dateTimeStr) + if err != nil { + return time.Time{}, err + } + } + + return t, nil +} + // parseSqliteTimeStamp parses a timestamp string in the format of // "YYYY-MM-DD HH:MM:SS +0000 UTC" and returns a time.Time value. // NOTE: we can't use time.Parse() because it doesn't support having years @@ -316,7 +330,7 @@ func (r *SqliteTxOptions) ReadOnly() bool { func parseSqliteTimeStamp(dateTimeStr string) (time.Time, error) { // Split the date and time parts. parts := strings.Fields(strings.TrimSpace(dateTimeStr)) - if len(parts) <= 2 { + if len(parts) < 2 { return time.Time{}, fmt.Errorf("invalid timestamp format: %v", dateTimeStr) } @@ -385,7 +399,10 @@ func parseTimeParts(datePart, timePart string) (time.Time, error) { return time.Time{}, err } - second, err := strconv.Atoi(timeParts[2]) + // Parse the seconds and ignore the fractional part. + secondParts := strings.Split(timeParts[2], ".") + + second, err := strconv.Atoi(secondParts[0]) if err != nil { return time.Time{}, err }