diff options
Diffstat (limited to 'internal/reports/daterange.go')
-rw-r--r-- | internal/reports/daterange.go | 162 |
1 files changed, 152 insertions, 10 deletions
diff --git a/internal/reports/daterange.go b/internal/reports/daterange.go index 3478615..adeb48b 100644 --- a/internal/reports/daterange.go +++ b/internal/reports/daterange.go @@ -2,6 +2,8 @@ package reports import ( "fmt" + "regexp" + "strconv" "strings" "time" ) @@ -11,6 +13,33 @@ type DateRange struct { End time.Time } +var monthNames = map[string]time.Month{ + "january": time.January, + "february": time.February, + "march": time.March, + "april": time.April, + "may": time.May, + "june": time.June, + "july": time.July, + "august": time.August, + "september": time.September, + "october": time.October, + "november": time.November, + "december": time.December, + "jan": time.January, + "feb": time.February, + "mar": time.March, + "apr": time.April, + "jun": time.June, + "jul": time.July, + "aug": time.August, + "sep": time.September, + "sept": time.September, + "oct": time.October, + "nov": time.November, + "dec": time.December, +} + func ParseDateRange(dateStr string) (DateRange, error) { dateStr = strings.TrimSpace(dateStr) now := time.Now().UTC() @@ -20,8 +49,22 @@ func ParseDateRange(dateStr string) (DateRange, error) { switch lowerDateStr { case "last week": return getLastWeek(now), nil + case "this week": + return getThisWeek(now), nil case "last month": return getLastMonth(now), nil + case "this month": + return getThisMonth(now), nil + } + + // Check for month name patterns (case-insensitive) + if dateRange, err := parseMonthName(dateStr, now); err == nil { + return dateRange, nil + } + + // Check for "month year" format + if dateRange, err := parseMonthYear(dateStr); err == nil { + return dateRange, nil } // Check for custom date range format: "YYYY-MM-DD to YYYY-MM-DD" @@ -29,7 +72,7 @@ func ParseDateRange(dateStr string) (DateRange, error) { 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) + return DateRange{}, fmt.Errorf("unsupported date range: %s (supported: 'this week', 'this month', 'last week', 'last month', month names like 'february', 'month year' like 'july 2023', or 'YYYY-MM-DD to YYYY-MM-DD')", dateStr) } func parseCustomDateRange(dateStr string) (DateRange, error) { @@ -61,7 +104,7 @@ func parseCustomDateRange(dateStr string) (DateRange, error) { // 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) @@ -77,14 +120,14 @@ func getLastWeek(now time.Time) DateRange { if weekday == 0 { // Sunday weekday = 7 } - + // Start of current week - currentWeekStart := now.AddDate(0, 0, -(weekday-1)).Truncate(24 * time.Hour) - + 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, @@ -94,15 +137,114 @@ func getLastWeek(now time.Time) DateRange { 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 +} + +func getThisWeek(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 + weekStart := now.AddDate(0, 0, -(weekday - 1)).Truncate(24 * time.Hour) + + // End of current week (Sunday 23:59:59.999999999) + weekEnd := weekStart.AddDate(0, 0, 7).Add(-time.Nanosecond) + + return DateRange{ + Start: weekStart, + End: weekEnd, + } +} + +func getThisMonth(now time.Time) DateRange { + // Start of current month + monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.UTC) + + // End of current month (last nanosecond) + nextMonthStart := monthStart.AddDate(0, 1, 0) + monthEnd := nextMonthStart.Add(-time.Nanosecond) + + return DateRange{ + Start: monthStart, + End: monthEnd, + } +} + +func parseMonthName(dateStr string, now time.Time) (DateRange, error) { + lowerDateStr := strings.ToLower(dateStr) + month, exists := monthNames[lowerDateStr] + if !exists { + return DateRange{}, fmt.Errorf("not a valid month name") + } + + // Find the most recent occurrence of this month + var targetYear int + currentMonth := now.Month() + + if month == currentMonth { + // If it's the same month as now, use this year + targetYear = now.Year() + } else if month < currentMonth { + // If the month has already passed this year, use this year + targetYear = now.Year() + } else { + // If the month hasn't come yet this year, use last year + targetYear = now.Year() - 1 + } + + return getMonthRange(targetYear, month), nil +} + +func parseMonthYear(dateStr string) (DateRange, error) { + // Match patterns like "july 2023", "feb 2022", etc. + re := regexp.MustCompile(`^(\w+)\s+(\d{4})$`) + matches := re.FindStringSubmatch(strings.ToLower(dateStr)) + if len(matches) != 3 { + return DateRange{}, fmt.Errorf("invalid month year format") + } + + monthStr := matches[1] + yearStr := matches[2] + + // Parse year + year, err := strconv.Atoi(yearStr) + if err != nil { + return DateRange{}, fmt.Errorf("invalid year: %s", yearStr) + } + + // Parse month + month, exists := monthNames[monthStr] + if !exists { + return DateRange{}, fmt.Errorf("invalid month name: %s", monthStr) + } + + return getMonthRange(year, month), nil +} + +func getMonthRange(year int, month time.Month) DateRange { + // Start of the specified month + monthStart := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC) + + // End of the specified month (last nanosecond) + nextMonthStart := monthStart.AddDate(0, 1, 0) + monthEnd := nextMonthStart.Add(-time.Nanosecond) + + return DateRange{ + Start: monthStart, + End: monthEnd, + } +} + |