Skip to content

Commit e093a0c

Browse files
authored
Merge pull request #657 from sputn1ck/fix_leapyear_bug
loopdb: fix leapyear parsing
2 parents 6eebf9f + b8ada04 commit e093a0c

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

loopdb/sql_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,18 @@ func TestTimeConversions(t *testing.T) {
439439
2023, 8, 4, 8, 7, 49, 0, time.UTC,
440440
),
441441
},
442+
{
443+
timeString: "2188-02-29 15:34:23.847906176 +0000 UTC",
444+
expectedTime: time.Date(
445+
2023, 2, 28, 15, 34, 23, 847906176, time.UTC,
446+
),
447+
},
448+
{
449+
timeString: "2188-02-29T16:07:49+08:00",
450+
expectedTime: time.Date(
451+
2023, 2, 28, 8, 7, 49, 0, time.UTC,
452+
),
453+
},
442454
}
443455

444456
for _, test := range tests {

loopdb/sqlite.go

+55
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,26 @@ func fixTimeStamp(dateTimeStr string) (time.Time, error) {
342342
)
343343
}
344344

345+
// If the year is a leap year and the date is 29th of February, we
346+
// need to change it to 28th of February. Otherwise, the time.Parse
347+
// function will fail, as a non-leap year cannot have 29th of February.
348+
day, month, err := extractDayAndMonth(dateTimeStr)
349+
if err != nil {
350+
return time.Time{}, fmt.Errorf("unable to parse timestamp day "+
351+
"and month %v: %v", dateTimeStr, err)
352+
}
353+
354+
if !isLeapYear(thisYear) &&
355+
month == 2 && day == 29 {
356+
357+
dateTimeStr = strings.Replace(
358+
dateTimeStr,
359+
fmt.Sprintf("%d-02-29", thisYear),
360+
fmt.Sprintf("%d-02-28", thisYear),
361+
1,
362+
)
363+
}
364+
345365
parsedTime, err := parseLayouts(defaultLayouts(), dateTimeStr)
346366
if err != nil {
347367
return time.Time{}, fmt.Errorf("unable to parse timestamp %v: %v",
@@ -403,3 +423,38 @@ func getTimeStampYear(dateTimeStr string) (int, error) {
403423

404424
return year, nil
405425
}
426+
427+
// extractDayAndMonth extracts the day and month from a date string.
428+
func extractDayAndMonth(dateStr string) (int, int, error) {
429+
// Split the date string into parts using various delimiters.
430+
parts := strings.FieldsFunc(dateStr, func(r rune) bool {
431+
return r == '-' || r == ' ' || r == 'T' || r == ':' || r == '+' || r == 'Z'
432+
})
433+
434+
if len(parts) < 3 {
435+
return 0, 0, fmt.Errorf("Invalid date format: %s", dateStr)
436+
}
437+
438+
// Extract year, month, and day from the parts.
439+
_, err := strconv.Atoi(parts[0])
440+
if err != nil {
441+
return 0, 0, err
442+
}
443+
444+
month, err := strconv.Atoi(parts[1])
445+
if err != nil {
446+
return 0, 0, err
447+
}
448+
449+
day, err := strconv.Atoi(parts[2])
450+
if err != nil {
451+
return 0, 0, err
452+
}
453+
454+
return day, month, nil
455+
}
456+
457+
// isLeapYear returns true if the year is a leap year.
458+
func isLeapYear(year int) bool {
459+
return (year%4 == 0 && year%100 != 0) || (year%400 == 0)
460+
}

0 commit comments

Comments
 (0)