package core import ( "fmt" "strconv" "strings" ) // CronExplain returns a short human-readable description of a cron expression. // Supports 5-field standard cron and @hourly/@daily/@weekly/@monthly shortcuts. // Returns the raw expression if the pattern is not recognized. func CronExplain(expr string) string { expr = strings.TrimSpace(expr) // Handle shortcuts switch expr { case "@hourly": return "hourly" case "@daily", "@midnight": return "daily" case "@weekly": return "weekly" case "@monthly": return "monthly" case "@yearly", "@annually": return "yearly" } fields := strings.Fields(expr) if len(fields) < 5 { return expr } // Use first 5 fields (ignore optional 6th seconds/year field) minute := fields[0] hour := fields[1] dom := fields[2] // day of month month := fields[3] dow := fields[4] // day of week // "every N minutes": */N in minute field, rest are * if strings.HasPrefix(minute, "*/") && dom == "*" && month == "*" && dow == "*" { n := minute[2:] if n == "1" { return "every minute" } return fmt.Sprintf("every %s minutes", n) } // "every N hours": plain minute "0", */N in hour, rest are * if strings.HasPrefix(hour, "*/") && minute == "0" && dom == "*" && month == "*" && dow == "*" { n := hour[2:] if n == "1" { return "every hour" } return fmt.Sprintf("every %s hours", n) } // "daily at HH:MM": plain numbers for minute and hour, rest are * if dom == "*" && month == "*" && dow == "*" { m, errM := strconv.Atoi(minute) h, errH := strconv.Atoi(hour) if errM == nil && errH == nil { return fmt.Sprintf("daily at %02d:%02d", h, m) } } // "weekdays at HH:MM": plain minute/hour, dow == "1-5", dom/month are * if dom == "*" && month == "*" && dow == "1-5" { m, errM := strconv.Atoi(minute) h, errH := strconv.Atoi(hour) if errM == nil && errH == nil { return fmt.Sprintf("weekdays at %02d:%02d", h, m) } } // Not recognized — return raw expression return expr }