Repository: kiorky/croniter

Issue:
Croniter expression "* * 5 3 1-7" does not match weeks
I want to match March 5th or Monday to Sunday in March, so I used the expression `* * 5 3 1-7`, but the expression optimization will convert the expression to `* * 5 3 *`, so that only Match March 5th. Let's look at another example, `* * 5 3 1-6` is correct, the expression will be converted to `* * 5 3 1,2,3,4,5,6`, so it will not only match March 5 , it can also match every day in March except Sunday.

Discussion:
Cherie0125: @kiorky Please review and fix it as soon as possible

Patch:
diff --git a/src/croniter/croniter.py b/src/croniter/croniter.py
index 2a3e3c8..9124954 100644
--- a/src/croniter/croniter.py
+++ b/src/croniter/croniter.py
@@ -787,7 +787,12 @@ class croniter(object):
             res = set(res)
             res = sorted(res, key=lambda i: "{:02}".format(i) if isinstance(i, int) else i)
             if len(res) == cls.LEN_MEANS_ALL[i]:
-                res = ['*']
+                # Make sure the wildcard is used in the correct way (avoid over-optimization)
+                if ((i == 2 and '*' not in expressions[4]) or
+                    (i == 4 and '*' not in expressions[2])):
+                    pass
+                else:
+                    res = ['*']
 
             expanded.append(['*'] if (len(res) == 1
                                       and res[0] == '*')
@@ -798,7 +803,8 @@ class croniter(object):
             dow_expanded_set = set(expanded[4])
             dow_expanded_set = dow_expanded_set.difference(nth_weekday_of_month.keys())
             dow_expanded_set.discard("*")
-            if dow_expanded_set:
+            # Skip: if it's all weeks instead of wildcard
+            if dow_expanded_set and len(set(expanded[4])) != cls.LEN_MEANS_ALL[4]:
                 raise CroniterUnsupportedSyntaxError(
                     "day-of-week field does not support mixing literal values and nth day of week syntax.  "
                     "Cron: '{}'    dow={} vs nth={}".format(expr_format, dow_expanded_set, nth_weekday_of_month))
@@ -824,6 +830,11 @@ class croniter(object):
 
     @classmethod
     def is_valid(cls, expression, hash_id=None):
+        if hash_id:
+            if not isinstance(hash_id, (bytes, str)):
+                raise TypeError('hash_id must be bytes or UTF-8 string')
+            if not isinstance(hash_id, bytes):
+                hash_id = hash_id.encode('UTF-8')
         try:
             cls.expand(expression, hash_id=hash_id)
         except CroniterError:


Test patch:
diff --git a/src/croniter/tests/test_croniter.py b/src/croniter/tests/test_croniter.py
index 1dcfb0b..b2ea829 100755
--- a/src/croniter/tests/test_croniter.py
+++ b/src/croniter/tests/test_croniter.py
@@ -344,11 +344,15 @@ class CroniterTest(base.TestCase):
         # Test each field individually
         self.assertEqual(croniter('0-59 0 1 1 0').expanded[m], wildcard)
         self.assertEqual(croniter('0 0-23 1 1 0').expanded[h], wildcard)
-        self.assertEqual(croniter('0 0 1-31 1 0').expanded[d], wildcard)
+        self.assertNotEqual(croniter('0 0 1-31 1 0').expanded[d], wildcard)
+        self.assertEqual(croniter('0 0 1-31 1 *').expanded[d], wildcard)
         self.assertEqual(croniter('0 0 1 1-12 0').expanded[mon], wildcard)
-        self.assertEqual(croniter('0 0 1 1 0-6').expanded[dow], wildcard)
-        self.assertEqual(croniter('0 0 1 1 1-7').expanded[dow], wildcard)
-        self.assertEqual(croniter('0 0 1 1 1-7,sat#3').expanded[dow], wildcard)
+        self.assertNotEqual(croniter('0 0 1 1 0-6').expanded[dow], wildcard)
+        self.assertEqual(croniter('0 0 * 1 0-6').expanded[dow], wildcard)
+        self.assertNotEqual(croniter('0 0 1 1 1-7').expanded[dow], wildcard)
+        self.assertEqual(croniter('0 0 * 1 1-7').expanded[dow], wildcard)
+        self.assertNotEqual(croniter('0 0 1 1 1-7,sat#3').expanded[dow], wildcard)
+        self.assertEqual(croniter('0 0 * 1 1-7,sat#3').expanded[dow], wildcard)
         self.assertEqual(croniter('0 0 1 1 0 0-59').expanded[s], wildcard)
         # Real life examples
         self.assertEqual(croniter('30 1-12,0,10-23 15-21 * fri').expanded[h], wildcard)