@@ -3,6 +3,7 @@ package loopdb
33import (
44 "context"
55 "database/sql"
6+ "errors"
67 "fmt"
78 "net/url"
89 "path/filepath"
@@ -311,117 +312,81 @@ func (r *SqliteTxOptions) ReadOnly() bool {
311312// parseTimeStamp tries to parse a timestamp string with both the
312313// parseSqliteTimeStamp and parsePostgresTimeStamp functions.
313314// If both fail, it returns an error.
314- func parseTimeStamp (dateTimeStr string ) (time.Time , error ) {
315- t , err := parseSqliteTimeStamp (dateTimeStr )
315+ func fixTimeStamp (dateTimeStr string ) (time.Time , error ) {
316+ year , err := getTimeStampYear (dateTimeStr )
316317 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 )
318+ return time.Time {}, err
353319 }
354320
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 )
321+ // If the year is in the future. It was a faulty timestamp.
322+ thisYear := time .Now ().Year ()
323+ if year > thisYear {
324+ dateTimeStr = strings .Replace (
325+ dateTimeStr ,
326+ fmt .Sprintf ("%d" , year ),
327+ fmt .Sprintf ("%d" , thisYear ),
328+ 1 ,
329+ )
368330 }
369331
370- year , err := strconv . Atoi ( dateParts [ 0 ] )
332+ parsedTime , err := parseLayouts ( defaultLayouts (), dateTimeStr )
371333 if err != nil {
372- return time.Time {}, err
334+ return time.Time {}, fmt .Errorf ("unable to parse timestamp %v: %v" ,
335+ dateTimeStr , err )
373336 }
374337
375- month , err := strconv .Atoi (dateParts [1 ])
376- if err != nil {
377- return time.Time {}, err
378- }
338+ return parsedTime .UTC (), nil
339+ }
379340
380- day , err := strconv .Atoi (dateParts [2 ])
381- if err != nil {
382- return time.Time {}, err
341+ // parseLayouts parses time based on a list of provided layouts.
342+ // If layouts is empty list or nil, the error with unknown layout will be returned.
343+ func parseLayouts (layouts []string , dateTime string ) (time.Time , error ) {
344+ for _ , layout := range layouts {
345+ parsedTime , err := time .Parse (layout , dateTime )
346+ if err == nil {
347+ return parsedTime , nil
348+ }
383349 }
384350
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- }
351+ return time.Time {}, errors .New ("unknown layout" )
352+ }
391353
392- hour , err := strconv .Atoi (timeParts [0 ])
393- if err != nil {
394- return time.Time {}, err
354+ // defaultLayouts returns a default list of ALL supported layouts.
355+ // This function returns new copy of a slice.
356+ func defaultLayouts () []string {
357+ return []string {
358+ "2006-01-02 15:04:05.99999 -0700 MST" , // Custom sqlite layout.
359+ time .RFC3339Nano ,
360+ time .RFC3339 ,
361+ time .RFC1123Z ,
362+ time .RFC1123 ,
363+ time .RFC850 ,
364+ time .RFC822Z ,
365+ time .RFC822 ,
366+ time .Layout ,
367+ time .RubyDate ,
368+ time .UnixDate ,
369+ time .ANSIC ,
370+ time .StampNano ,
371+ time .StampMicro ,
372+ time .StampMilli ,
373+ time .Stamp ,
374+ time .Kitchen ,
395375 }
376+ }
396377
397- minute , err := strconv .Atoi (timeParts [1 ])
398- if err != nil {
399- return time.Time {}, err
378+ // getTimeStampYear returns the year of a timestamp string.
379+ func getTimeStampYear (dateTimeStr string ) (int , error ) {
380+ parts := strings .Split (dateTimeStr , "-" )
381+ if len (parts ) < 1 {
382+ return 0 , fmt .Errorf ("invalid timestamp format: %v" ,
383+ dateTimeStr )
400384 }
401385
402- // Parse the seconds and ignore the fractional part.
403- secondParts := strings .Split (timeParts [2 ], "." )
404-
405- second , err := strconv .Atoi (secondParts [0 ])
386+ year , err := strconv .Atoi (parts [0 ])
406387 if err != nil {
407- return time. Time {} , err
388+ return 0 , fmt . Errorf ( "unable to parse year: %v" , err )
408389 }
409390
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- }
391+ return year , nil
427392}
0 commit comments