summaryrefslogtreecommitdiff
path: root/internal/reports/daterange.go
diff options
context:
space:
mode:
authorT <t@tjp.lol>2025-08-04 09:49:52 -0600
committerT <t@tjp.lol>2025-08-04 15:15:18 -0600
commit56e0af3b41742876b471332aeb943a5a2ca8dfbf (patch)
treeef75f4900107ef28977823eabd11ec3014cd40ba /internal/reports/daterange.go
parent4c29dfee9be26996ce548e2edf0328422df598d0 (diff)
Generate invoice PDFs
Diffstat (limited to 'internal/reports/daterange.go')
-rw-r--r--internal/reports/daterange.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/internal/reports/daterange.go b/internal/reports/daterange.go
new file mode 100644
index 0000000..3478615
--- /dev/null
+++ b/internal/reports/daterange.go
@@ -0,0 +1,108 @@
+package reports
+
+import (
+ "fmt"
+ "strings"
+ "time"
+)
+
+type DateRange struct {
+ Start time.Time
+ End time.Time
+}
+
+func ParseDateRange(dateStr string) (DateRange, error) {
+ dateStr = strings.TrimSpace(dateStr)
+ now := time.Now().UTC()
+
+ // Check for predefined ranges (case-insensitive)
+ lowerDateStr := strings.ToLower(dateStr)
+ switch lowerDateStr {
+ case "last week":
+ return getLastWeek(now), nil
+ case "last month":
+ return getLastMonth(now), nil
+ }
+
+ // Check for custom date range format: "YYYY-MM-DD to YYYY-MM-DD"
+ if strings.Contains(dateStr, " to ") {
+ return parseCustomDateRange(dateStr)
+ }
+
+ return DateRange{}, fmt.Errorf("unsupported date range: %s (supported: 'last week', 'last month', or 'YYYY-MM-DD to YYYY-MM-DD')", dateStr)
+}
+
+func parseCustomDateRange(dateStr string) (DateRange, error) {
+ parts := strings.Split(dateStr, " to ")
+ if len(parts) != 2 {
+ return DateRange{}, fmt.Errorf("invalid date range format: expected 'YYYY-MM-DD to YYYY-MM-DD'")
+ }
+
+ startStr := strings.TrimSpace(parts[0])
+ endStr := strings.TrimSpace(parts[1])
+
+ // Parse start date
+ startDate, err := time.Parse("2006-01-02", startStr)
+ if err != nil {
+ return DateRange{}, fmt.Errorf("invalid start date '%s': expected YYYY-MM-DD format", startStr)
+ }
+
+ // Parse end date
+ endDate, err := time.Parse("2006-01-02", endStr)
+ if err != nil {
+ return DateRange{}, fmt.Errorf("invalid end date '%s': expected YYYY-MM-DD format", endStr)
+ }
+
+ // Validate that start date is before or equal to end date
+ if startDate.After(endDate) {
+ return DateRange{}, fmt.Errorf("start date '%s' must be before or equal to end date '%s'", startStr, endStr)
+ }
+
+ // Convert to UTC and set times appropriately
+ // Start date: beginning of day (00:00:00)
+ startUTC := time.Date(startDate.Year(), startDate.Month(), startDate.Day(), 0, 0, 0, 0, time.UTC)
+
+ // End date: end of day (23:59:59.999999999)
+ endUTC := time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 23, 59, 59, 999999999, time.UTC)
+
+ return DateRange{
+ Start: startUTC,
+ End: endUTC,
+ }, nil
+}
+
+func getLastWeek(now time.Time) DateRange {
+ // Find the start of current week (Monday)
+ weekday := int(now.Weekday())
+ if weekday == 0 { // Sunday
+ weekday = 7
+ }
+
+ // Start of current week
+ currentWeekStart := now.AddDate(0, 0, -(weekday-1)).Truncate(24 * time.Hour)
+
+ // Last week is the week before current week
+ lastWeekStart := currentWeekStart.AddDate(0, 0, -7)
+ lastWeekEnd := currentWeekStart.Add(-time.Nanosecond)
+
+ return DateRange{
+ Start: lastWeekStart,
+ End: lastWeekEnd,
+ }
+}
+
+func getLastMonth(now time.Time) DateRange {
+ // Start of current month
+ currentMonthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC)
+
+ // Last month start
+ lastMonthStart := currentMonthStart.AddDate(0, -1, 0)
+
+ // Last month end (last nanosecond of last month)
+ lastMonthEnd := currentMonthStart.Add(-time.Nanosecond)
+
+ return DateRange{
+ Start: lastMonthStart,
+ End: lastMonthEnd,
+ }
+} \ No newline at end of file