diff --git a/gronx.go b/gronx.go index 82c0baa..63b4137 100644 --- a/gronx.go +++ b/gronx.go @@ -174,6 +174,12 @@ func IsValid(expr string) bool { return false } + // First check syntax without time dependency + if !isSyntaxValid(segs) { + return false + } + + // Then check with time dependency for pos, seg := range segs { if _, err := checker.CheckDue(seg, pos); err != nil { return false @@ -182,3 +188,35 @@ func IsValid(expr string) bool { return true } + +// isSyntaxValid checks if the cron segments are syntactically valid without time dependency. +// It returns bool. +func isSyntaxValid(segs []string) bool { + for _, seg := range segs { + // Check for empty segments + if seg == "" { + return false + } + + // Split by comma to check each part + parts := strings.Split(seg, ",") + for _, part := range parts { + // Check for empty parts + if part == "" { + return false + } + + // Check for invalid characters + if strings.ContainsAny(part, "*/") { + // If contains /, must have a number after it + if strings.Contains(part, "/") { + parts := strings.Split(part, "/") + if len(parts) != 2 || parts[1] == "" { + return false + } + } + } + } + } + return true +} diff --git a/gronx_test.go b/gronx_test.go index 109ba3d..ed65d26 100644 --- a/gronx_test.go +++ b/gronx_test.go @@ -99,6 +99,28 @@ func TestIsValid(t *testing.T) { } }) + t.Run("sensitivity to reference time", func(t *testing.T) { + originalRef := checker.ref + defer func() { checker.ref = originalRef }() + + expr := "*/15, * * * *" + moments := []time.Time{ + time.Date(2025, 4, 29, 12, 13, 0, 0, time.UTC), + time.Date(2025, 4, 29, 12, 14, 0, 0, time.UTC), + time.Date(2025, 4, 29, 12, 15, 0, 0, time.UTC), + time.Date(2025, 4, 29, 12, 16, 0, 0, time.UTC), + time.Date(2025, 4, 29, 12, 17, 0, 0, time.UTC), + } + + for _, moment := range moments { + checker.ref = moment + + if gron.IsValid(expr) { + t.Errorf("expected false, got true at %v", moment) + } + } + + }) } func TestAddTag(t *testing.T) {