@@ -3,6 +3,7 @@ package loopdb
33import (
44 "context"
55 "database/sql"
6+ "errors"
67 "fmt"
78 "net/url"
89 "path/filepath"
@@ -247,19 +248,25 @@ func (b *BaseDB) FixFaultyTimestamps(ctx context.Context) error {
247248 defer tx .Rollback () //nolint: errcheck
248249
249250 for _ , swap := range loopOutSwaps {
250- faultyTime , err := parseTimeStamp (swap .PublicationDeadline )
251+
252+ // Get the year of the timestamp.
253+ year , err := getTimeStampYear (swap .PublicationDeadline )
251254 if err != nil {
252255 return err
253256 }
254257
255- // Skip if the time is not faulty.
256- if ! isMilisecondsTime (faultyTime .Unix ()) {
258+ // Skip if the year is not in the future.
259+ thisYear := time .Now ().Year ()
260+ if year <= thisYear {
257261 continue
258262 }
259263
264+ fixedTime , err := fixTimeStamp (swap .PublicationDeadline )
265+ if err != nil {
266+ return err
267+ }
268+
260269 // Update the faulty time to a valid time.
261- secs := faultyTime .Unix () / 1000
262- correctTime := time .Unix (secs , 0 )
263270 _ , err = tx .ExecContext (
264271 ctx , `
265272 UPDATE
@@ -269,7 +276,7 @@ func (b *BaseDB) FixFaultyTimestamps(ctx context.Context) error {
269276 WHERE
270277 swap_hash = $2;
271278 ` ,
272- correctTime , swap .Hash ,
279+ fixedTime , swap .Hash ,
273280 )
274281 if err != nil {
275282 return err
@@ -308,120 +315,84 @@ func (r *SqliteTxOptions) ReadOnly() bool {
308315 return r .readOnly
309316}
310317
311- // parseTimeStamp tries to parse a timestamp string with both the
318+ // fixTimeStamp tries to parse a timestamp string with both the
312319// parseSqliteTimeStamp and parsePostgresTimeStamp functions.
313320// If both fail, it returns an error.
314- func parseTimeStamp (dateTimeStr string ) (time.Time , error ) {
315- t , err := parseSqliteTimeStamp (dateTimeStr )
321+ func fixTimeStamp (dateTimeStr string ) (time.Time , error ) {
322+ year , err := getTimeStampYear (dateTimeStr )
316323 if err != nil {
317- t , err = parsePostgresTimeStamp (dateTimeStr )
318- if err != nil {
319- return time.Time {}, err
320- }
321- }
322-
323- return t , nil
324- }
325-
326- // parseSqliteTimeStamp parses a timestamp string in the format of
327- // "YYYY-MM-DD HH:MM:SS +0000 UTC" and returns a time.Time value.
328- // NOTE: we can't use time.Parse() because it doesn't support having years
329- // with more than 4 digits.
330- func parseSqliteTimeStamp (dateTimeStr string ) (time.Time , error ) {
331- // Split the date and time parts.
332- parts := strings .Fields (strings .TrimSpace (dateTimeStr ))
333- if len (parts ) < 2 {
334- return time.Time {}, fmt .Errorf ("invalid timestamp format: %v" ,
335- dateTimeStr )
336- }
337-
338- datePart , timePart := parts [0 ], parts [1 ]
339-
340- return parseTimeParts (datePart , timePart )
341- }
342-
343- // parseSqliteTimeStamp parses a timestamp string in the format of
344- // "YYYY-MM-DDTHH:MM:SSZ" and returns a time.Time value.
345- // NOTE: we can't use time.Parse() because it doesn't support having years
346- // with more than 4 digits.
347- func parsePostgresTimeStamp (dateTimeStr string ) (time.Time , error ) {
348- // Split the date and time parts.
349- parts := strings .Split (dateTimeStr , "T" )
350- if len (parts ) != 2 {
351- return time.Time {}, fmt .Errorf ("invalid timestamp format: %v" ,
352- dateTimeStr )
324+ return time.Time {}, err
353325 }
354326
355- datePart , timePart := parts [0 ], strings .TrimSuffix (parts [1 ], "Z" )
356-
357- return parseTimeParts (datePart , timePart )
358- }
359-
360- // parseTimeParts takes a datePart string in the format of "YYYY-MM-DD" and
361- // a timePart string in the format of "HH:MM:SS" and returns a time.Time value.
362- func parseTimeParts (datePart , timePart string ) (time.Time , error ) {
363- // Parse the date.
364- dateParts := strings .Split (datePart , "-" )
365- if len (dateParts ) != 3 {
366- return time.Time {}, fmt .Errorf ("invalid date format: %v" ,
367- datePart )
327+ // If the year is in the future. It was a faulty timestamp.
328+ thisYear := time .Now ().Year ()
329+ if year > thisYear {
330+ dateTimeStr = strings .Replace (
331+ dateTimeStr ,
332+ fmt .Sprintf ("%d" , year ),
333+ fmt .Sprintf ("%d" , thisYear ),
334+ 1 ,
335+ )
368336 }
369337
370- year , err := strconv . Atoi ( dateParts [ 0 ] )
338+ parsedTime , err := parseLayouts ( defaultLayouts (), dateTimeStr )
371339 if err != nil {
372- return time.Time {}, err
340+ return time.Time {}, fmt .Errorf ("unable to parse timestamp %v: %v" ,
341+ dateTimeStr , err )
373342 }
374343
375- month , err := strconv .Atoi (dateParts [1 ])
376- if err != nil {
377- return time.Time {}, err
378- }
344+ return parsedTime .UTC (), nil
345+ }
379346
380- day , err := strconv .Atoi (dateParts [2 ])
381- if err != nil {
382- return time.Time {}, err
347+ // parseLayouts parses time based on a list of provided layouts.
348+ // If layouts is empty list or nil, the error with unknown layout will be returned.
349+ func parseLayouts (layouts []string , dateTime string ) (time.Time , error ) {
350+ for _ , layout := range layouts {
351+ parsedTime , err := time .Parse (layout , dateTime )
352+ if err == nil {
353+ return parsedTime , nil
354+ }
383355 }
384356
385- // Parse the time.
386- timeParts := strings .Split (timePart , ":" )
387- if len (timeParts ) != 3 {
388- return time.Time {}, fmt .Errorf ("invalid time format: %v" ,
389- timePart )
390- }
357+ return time.Time {}, errors .New ("unknown layout" )
358+ }
391359
392- hour , err := strconv .Atoi (timeParts [0 ])
393- if err != nil {
394- return time.Time {}, err
360+ // defaultLayouts returns a default list of ALL supported layouts.
361+ // This function returns new copy of a slice.
362+ func defaultLayouts () []string {
363+ return []string {
364+ "2006-01-02 15:04:05.99999 -0700 MST" , // Custom sqlite layout.
365+ time .RFC3339Nano ,
366+ time .RFC3339 ,
367+ time .RFC1123Z ,
368+ time .RFC1123 ,
369+ time .RFC850 ,
370+ time .RFC822Z ,
371+ time .RFC822 ,
372+ time .Layout ,
373+ time .RubyDate ,
374+ time .UnixDate ,
375+ time .ANSIC ,
376+ time .StampNano ,
377+ time .StampMicro ,
378+ time .StampMilli ,
379+ time .Stamp ,
380+ time .Kitchen ,
395381 }
382+ }
396383
397- minute , err := strconv .Atoi (timeParts [1 ])
398- if err != nil {
399- return time.Time {}, err
384+ // getTimeStampYear returns the year of a timestamp string.
385+ func getTimeStampYear (dateTimeStr string ) (int , error ) {
386+ parts := strings .Split (dateTimeStr , "-" )
387+ if len (parts ) < 1 {
388+ return 0 , fmt .Errorf ("invalid timestamp format: %v" ,
389+ dateTimeStr )
400390 }
401391
402- // Parse the seconds and ignore the fractional part.
403- secondParts := strings .Split (timeParts [2 ], "." )
404-
405- second , err := strconv .Atoi (secondParts [0 ])
392+ year , err := strconv .Atoi (parts [0 ])
406393 if err != nil {
407- return time. Time {} , err
394+ return 0 , fmt . Errorf ( "unable to parse year: %v" , err )
408395 }
409396
410- // Construct a time.Time value.
411- return time .Date (
412- year , time .Month (month ), day , hour , minute , second , 0 , time .UTC ,
413- ), nil
414- }
415-
416- // isMilisecondsTime returns true if the unix timestamp is likely in
417- // milliseconds.
418- func isMilisecondsTime (unixTimestamp int64 ) bool {
419- length := len (fmt .Sprintf ("%d" , unixTimestamp ))
420- if length >= 13 {
421- // Likely a millisecond timestamp
422- return true
423- } else {
424- // Likely a second timestamp
425- return false
426- }
397+ return year , nil
427398}
0 commit comments