(input string)
| 377 | } |
| 378 | |
| 379 | func parseFlexibleDate(input string) (string, error) { |
| 380 | now := time.Now().UTC() |
| 381 | input = strings.TrimSpace(input) |
| 382 | |
| 383 | // Try full RFC3339 first. |
| 384 | if t, err := time.Parse(time.RFC3339, input); err == nil { |
| 385 | return t.Format(time.RFC3339), nil |
| 386 | } |
| 387 | |
| 388 | // Try "YYYY-MM-DD" date only. |
| 389 | if t, err := time.Parse("2006-01-02", input); err == nil { |
| 390 | return t.UTC().Format(time.RFC3339), nil |
| 391 | } |
| 392 | |
| 393 | input = strings.ToLower(input) |
| 394 | // Keywords. |
| 395 | switch input { |
| 396 | case "yesterday": |
| 397 | return now.AddDate(0, 0, -1).Format(time.RFC3339), nil |
| 398 | case "today": |
| 399 | t := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC) |
| 400 | return t.Format(time.RFC3339), nil |
| 401 | } |
| 402 | |
| 403 | if strings.HasSuffix(input, "d") { |
| 404 | dayStr := strings.TrimSuffix(input, "d") |
| 405 | if n, err := strconv.Atoi(dayStr); err == nil { |
| 406 | return now.AddDate(0, 0, n).Format(time.RFC3339), nil |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | if strings.HasSuffix(input, "h") { |
| 411 | hourStr := strings.TrimSuffix(input, "h") |
| 412 | if n, err := strconv.Atoi(hourStr); err == nil { |
| 413 | return now.Add(time.Duration(n) * time.Hour).Format(time.RFC3339), nil |
| 414 | } |
| 415 | } |
| 416 | |
| 417 | return "", fmt.Errorf("invalid date format: use RFC3339, 'YYYY-MM-DD', or formats like 'yesterday', '-2d'") |
| 418 | } |
no test coverage detected