Repository: dateutil/dateutil (Python)

Issue:
Indentation typo in _parse?
parser.py, lines 794-802:

    if idx == 1:
        res.minute = int(value)

        if value % 1:
            res.second = int(60*(value % 1))
        elif idx == 2:
            res.second, res.microsecond = \
                                    _parsems(value_repr)
            i += 1

The "idx == 2" branch will never be reached.  I'm pretty sure this is supposed to be:

    if idx == 1:
        res.minute = int(value)

        if value % 1:
            res.second = int(60*(value % 1))
    elif idx == 2:
        res.second, res.microsecond = \
                                _parsems(value_repr)
        i += 1

Maybe the "i+=1" is supposed to be applied in both branches?


Hints:
Yes, this definitely seems wrong, I'll have to look into it to understand what this code does...
OK, the `elif` branch definitely is supposed to be on the level of `if idx == 1`. Working on a test to show that this actually causes a bug. (I think it will fail to parse, e.g. `14 m 30` incorrectly)

Still not sure what the `i += 1` is supposed to do, though. It might be redundant, or it might be to handle some situation we're not testing for.

Patch:
diff --git a/dateutil/parser.py b/dateutil/parser.py
--- a/dateutil/parser.py
+++ b/dateutil/parser.py
@@ -793,17 +793,21 @@ def _parse(self, timestr, dayfirst=None, yearfirst=None, fuzzy=False,
                     elif (i == len_l and l[i-2] == ' ' and
                           info.hms(l[i-3]) is not None):
                         # X h MM or X m SS
-                        idx = info.hms(l[i-3]) + 1
+                        idx = info.hms(l[i-3])
 
-                        if idx == 1:
+                        if idx == 0:               # h
                             res.minute = int(value)
 
-                            if value % 1:
-                                res.second = int(60*(value % 1))
-                            elif idx == 2:
-                                res.second, res.microsecond = \
-                                    _parsems(value_repr)
-                                i += 1
+                            sec_remainder = value % 1
+                            if sec_remainder:
+                                res.second = int(60 * sec_remainder)
+                        elif idx == 1:             # m
+                            res.second, res.microsecond = \
+                                _parsems(value_repr)
+
+                        # We don't need to advance the tokens here because the
+                        # i == len_l call indicates that we're looking at all
+                        # the tokens already.
 
                     elif i+1 < len_l and l[i] == ':':
                         # HH:MM[:SS[.ss]]


Test patch:
diff --git a/dateutil/test/test_parser.py b/dateutil/test/test_parser.py
--- a/dateutil/test/test_parser.py
+++ b/dateutil/test/test_parser.py
@@ -452,6 +452,26 @@ def testHourWithLettersStrip4(self):
         self.assertEqual(parse("10 h 36", default=self.default),
                          datetime(2003, 9, 25, 10, 36))
 
+    def testHourWithLetterStrip5(self):
+        self.assertEqual(parse("10 h 36.5", default=self.default),
+                         datetime(2003, 9, 25, 10, 36, 30))
+
+    def testMinuteWithLettersSpaces1(self):
+        self.assertEqual(parse("36 m 5", default=self.default),
+                         datetime(2003, 9, 25, 0, 36, 5))
+
+    def testMinuteWithLettersSpaces2(self):
+        self.assertEqual(parse("36 m 5 s", default=self.default),
+                         datetime(2003, 9, 25, 0, 36, 5))
+
+    def testMinuteWithLettersSpaces3(self):
+        self.assertEqual(parse("36 m 05", default=self.default),
+                         datetime(2003, 9, 25, 0, 36, 5))
+
+    def testMinuteWithLettersSpaces4(self):
+        self.assertEqual(parse("36 m 05 s", default=self.default),
+                         datetime(2003, 9, 25, 0, 36, 5))
+
     def testAMPMNoHour(self):
         with self.assertRaises(ValueError):
             parse("AM")