diff options
author | T <t@tjp.lol> | 2025-08-04 09:49:52 -0600 |
---|---|---|
committer | T <t@tjp.lol> | 2025-08-04 15:15:18 -0600 |
commit | 56e0af3b41742876b471332aeb943a5a2ca8dfbf (patch) | |
tree | ef75f4900107ef28977823eabd11ec3014cd40ba /internal/reports/daterange.go | |
parent | 4c29dfee9be26996ce548e2edf0328422df598d0 (diff) |
Generate invoice PDFs
Diffstat (limited to 'internal/reports/daterange.go')
-rw-r--r-- | internal/reports/daterange.go | 108 |
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 |