diff --git a/internal/history/main.go b/internal/history/main.go index 8b4246b..0a13656 100644 --- a/internal/history/main.go +++ b/internal/history/main.go @@ -111,52 +111,3 @@ func (h *history) Log(method, query, answer string) error { } return nil } - -func augmentHistoryQuery(hq *HistoryQuery) error { - - if after, err := strToTime(hq.AfterStr); err == nil { - hq.After = after - } - - if before, err := strToTime(hq.BeforeStr); err == nil { - hq.Before = before - } - - return nil -} - -func strToTime(input string) (*time.Time, error) { - var layout string - if len(input) == 4 { - // Input is a year, convert to YYYY-01-01 - layout = "2006" - input += "-01-01" - } else if len(input) == 10 { - // Input is already in YYYY-MM-DD format - layout = "2006-01-02" - } else if len(input) == 8 { - // Input is in HH:MM:SS format, that's today - layout = time.TimeOnly - } else if len(input) == len("2006-01-02T15:04:05") { - // Input is in RFC3339 format without time zone (local) - layout = "2006-01-02T15:04:05" - } else if len(input) == len(time.RFC3339) { - // Input is in RFC3339 format - layout = time.RFC3339 - } else { - return nil, fmt.Errorf("invalid date format: %s", input) - } - - t, err := time.Parse(layout, input) - if err != nil { - return nil, fmt.Errorf("failed to parse date: %w", err) - } - - // If the input was in TimeOnly format, set the date to today - if layout == time.TimeOnly { - now := time.Now() - t = time.Date(now.Year(), now.Month(), now.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location()) - } - - return &t, nil -} diff --git a/internal/history/utils.go b/internal/history/utils.go new file mode 100644 index 0000000..c95dd60 --- /dev/null +++ b/internal/history/utils.go @@ -0,0 +1,54 @@ +package history + +import ( + "fmt" + "time" +) + +func augmentHistoryQuery(hq *HistoryQuery) error { + if after, err := strToTime(hq.AfterStr); err == nil { + hq.After = after + } + + if before, err := strToTime(hq.BeforeStr); err == nil { + hq.Before = before + } + + return nil +} + +func strToTime(input string) (*time.Time, error) { + var layout string + if len(input) == 4 { + // Input is a year, convert to YYYY-01-01 + layout = "2006-01-01" + input += "-01-01" + } else if len(input) == 10 { + // Input is already in YYYY-MM-DD format + layout = "2006-01-02" + } else if len(input) == 8 { + // Input is in HH:MM:SS format, that's today + layout = time.TimeOnly + } else if len(input) == len("2006-01-02T15:04:05") { + // Input is in RFC3339 format without time zone (local) + layout = "2006-01-02T15:04:05" + } else if len(input) == len(time.RFC3339) { + // Input is in RFC3339 format + layout = time.RFC3339 + } else { + return nil, fmt.Errorf("invalid date format: %s", input) + } + + t, err := time.Parse(layout, input) + if err != nil { + return nil, fmt.Errorf("failed to parse date: %w", err) + } + + // If the input was in TimeOnly format, set the date to today + if layout == time.TimeOnly { + now := time.Now() + t = time.Date(now.Year(), now.Month(), now.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location()) + } + + return &t, nil +} diff --git a/internal/history/utils_test.go b/internal/history/utils_test.go new file mode 100644 index 0000000..b43f478 --- /dev/null +++ b/internal/history/utils_test.go @@ -0,0 +1,79 @@ +package history + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func timePtr(t time.Time) *time.Time { + return &t +} + +func TestAugmentHistoryQuery(t *testing.T) { + cheatingAfter := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC) + cheatingBefore := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC) + + tests := []struct { + afterStr string + beforeStr string + after *time.Time + before *time.Time + }{ + {"2024", "2025", &cheatingAfter, &cheatingBefore}, + {"2024", "", &cheatingAfter, nil}, + {"", "2025", nil, &cheatingBefore}, + {"invalid", "invalid", nil, nil}, + {"", "", nil, nil}, + } + + for _, test := range tests { + t.Run(test.afterStr, func(t *testing.T) { + hq := &HistoryQuery{ + AfterStr: test.afterStr, + BeforeStr: test.beforeStr, + } + err := augmentHistoryQuery(hq) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + assert.Equal(t, test.after, hq.After) + assert.Equal(t, test.before, hq.Before) + }) + } +} + +func TestStrToTime(t *testing.T) { + nowTime := time.Now() + tests := []struct { + input string + expected *time.Time + }{ + {"2024", timePtr(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC))}, + {"2024-01-01", timePtr(time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC))}, + {"12:34:56", timePtr(time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 12, 34, 56, 0, time.UTC))}, + {"2024-01-01T12:34:56", timePtr(time.Date(2024, 1, 1, 12, 34, 56, 0, time.UTC))}, + {nowTime.Format(time.RFC3339), timePtr(time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), nowTime.Hour(), nowTime.Minute(), nowTime.Second(), 0, nowTime.Location()))}, + {"invalid", nil}, + } + + for _, test := range tests { + t.Run(test.input, func(t *testing.T) { + actual, err := strToTime(test.input) + if err != nil { + if test.expected != nil { + t.Errorf("unexpected error: %v", err) + } + return + } + if actual == nil { + t.Error("expected time, got nil") + return + } + + assert.Equal(t, test.expected, actual) + }) + } + +}