Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Time input validation

I have the fallowing problem, Im supposed to get user input in the form of 10:10:10 (hh:mm:ss) or 10:10(mm:ss) or 10(ss). Now i need check the fallowing parameters:

  • If I'm getting only seconds then there is no limit.
  • If I'm getting mm:ss then the seconds are limited to 0..59 and minutes are unlimited.
  • If I'm getting hh:mm:ss then both seconds and minutes are limited to 0..59 while hours are unlimited.

Then return a TimeDelta object.

The naive way is to write multiply if statements to check all this. But im looking for a smoother way.

val = "11:66:11"
try:
    val = map(int, val.split(':'))
except ValueError:
    return False
if len(val) == 1:
    return val
if len(val) == 2:
    if val[1] > 59:
        print  "Bad seconds"
        return False
if len(val) == 3:
    if val[2] > 59 or val[1] >59:
        print  "Bad seconds / minutes"
        return False
while len(val) < 3:
        split.insert(0,0)
return = timedelta(hours=split[0],minutes=split[1],seconds=split[2])
like image 799
MichaelR Avatar asked Apr 30 '15 07:04

MichaelR


1 Answers

How about using regular expression here:

import re
import datetime
pattern = re.compile(r'^(\d+)(?::([0-5]?\d)(?::([0-5]?\d))?)?$')

def str2seconds(val):
    match = pattern.match(val)
    if not match:
        raise ValueError("Invalid input: %s" % val)
    else:
        result = 0
        for i in match.groups():
            if i is not None:
                result *= 60
                result += int(i)

    return datetime.timedelta(seconds=result)

Example:

>>> print(str2seconds('255'))
0:04:15
>>> print(str2seconds('255:25'))
4:15:25
>>> print(str2seconds('255:25:25'))
10 days, 15:25:25
>>> print(str2seconds('255:25:25:255'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "y.py", line 8, in str2seconds
    raise ValueError("Invalid input: %s" % val)
ValueError: Invalid input: 255:25:25:255
>>> print(str2seconds('255:25:60'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "y.py", line 8, in str2seconds
    raise ValueError("Invalid input: %s" % val)
ValueError: Invalid input: 255:25:60

The regular expression part by part:

  • ^: beginning of string
  • (\d+): 1-n digits, captured as group 1
  • (?::([0-5]?\d)(?::([0-5]?\d))?)? optional part:
    • (?:...) is a non-capturing group
    • : matches literal :
    • ([0-5]?\d) an optional digit 0-5, followed by a digit, captured as group 2
    • there is a second non-capturing group in the first: (?::([0-5]?\d))? that optionally matches second : followed by an optional digit 0-5 followed by a digit; and capturing the digits into group 3
    • and finally $ matches the end of the string. Strictly, ^ at the beginning is not necessary as match anchors the match at the beginning; however the $ is necessary, as otherwise the match is not anchored at the end of the string! (Python 3.4 added the re.fullmatch to fix this.)

The match.groups() will be a tuple of 3 items; the non-matching groups will be returned as None.

like image 157