tags:

views:

110

answers:

5

I need to calculate (using python) how much time a user has inputed, whether they input something like 3:30 or 3.5. I'm not really sure what the best way to go about this is and I thought I'd ask for advice from the experts.

=== Edit ==================

To specify more clearly, I want the user to input hours and minutes or just minutes. I want them to be able to input the time in two formats, either in hh:mm (3:30 or 03:30) or as a float (3.5) hours.

The overall goal is to keep track of the time they have worked. So, I will be adding the time they enter up to get a total.

A: 

There are a few possible solutions, but at some point you're gonna run into ambiguous cases that will result in arbitrary conversions.

Overall I'd suggest taking any input and parsing the separators (whether : or . or something else) and then converting to seconds based on some schema of units you've defined.

Alternatively you could do a series of try/except statements to test it against different time formatting schemes to see if it matches.

I'm not sure what will be best in your case...

Gabriel Hurley
+1  A: 

Can you precisely define the syntax of the strings that the user is allowed to input? Once you do that, if it's simple enough it can be matched by simple Python string expressions, else you may be better off with pyparsing or the like. Also, a precise syntax will make it easier to identify any ambiguities so you can either change the rules (so that no input string is ever ambiguous) or at least decide precisely how to interpret them (AND document the fact for the user's benefit!-).

edit: given the OP's clarification (hh:mm or just minutes as a float) it seems simple:

  while True:
    s = raw_input('Please enter amount of time (hh:mm or just minutes):')
    try:
      if ':' in s:
        h, m = s.split(':')
      else:
        h = ''
        m = s
      t = int(h)*3600 + float(m)* 60
    except ValueError, e:
      print "Problems with your input (%r): %s" % (s, e)
      print "please try again!"
    else:
      break

You may want to get finer-grained in diagnosing exactly what problem the user input may have (when you accept and parse user input, 99% of the effort goes into identifying incredibly [[expletive deleted]] mistakes: it's VERY hard to make your code foolproof, because fools are do deucedly INGENUOUS!-), but this should help you get started.

Alex Martelli
Please see edit above
Joe
A: 

First of all, you'll need some conventions. Is 3.55 five minutes to four hours, five milliseconds to four seconds, or 3 and 55/100 of a minute/hour/second? The same applies to 3:55. At least have a distinction between dot and colon, specifying that a dot means a fraction and a colon, a separator of hour/minute/second.

Although you haven't specified what "time" is (since or o'clock?), you'll need that too.

Then, it's simple a matter of having a final representation of a time that you want to work with, and keep converting the input until your final representation is achieved. Let's say you decide that ultimately time should be represented as MM:SS (two digits for minutes, a colon, two digits for seconds), you'll need to search the string for allowed occurrences of characters, and act accordingly. For example, having a colon and a dot at the same time is not allowed. If there's a single colon, you have a fraction, therefore you'll treat the second part as a fraction of 60.

Keep doing this until you have your final representation, and then just do what you gotta do with said "time".

I don't know on what constraints you're working with, but the problem could be narrowed if instead of a single "time" input, you had two: The first, where people type the hours, and the second, where they type the minutes. Of course, that would only work if you can divide the input...

inerte
Sorry to nitpick, but a millisecond is 1/1000 of a second, so 3.55 seconds is actually 450 milliseconds short of 4 seconds. Not sure how you would even come up with 5, unless you were thinking a millisecond was 1/60 of a second?
John Y
@John Y No problem with the nitpicking, thanks for noticing :)
inerte
A: 

Hello Joe,

This is the code that we have in one of our internal web applications that we use for time-tracking purposes. When the user enters a time, the string value is passed through this function, which returns a structure of time data.

It's written in javascript, and the code could be directly ported to python.

I hope it helps a bit.

var ParseTime_NOW_MATCH = /^ *= *$/
var ParseTime_PLUS_MATCH = /^ *\+ *([0-9]{0,2}(\.[0-9]{0,3})?) *$/
var ParseTime_12_MATCH = /^ *([0-9]{1,2}):?([0-9]{2}) *([aApP])[mM]? *$/
var ParseTime_24_MATCH = /^ *([0-9]{1,2}):?([0-9]{2}) *$/


// ########################################################################################
// Returns either:
//  {
//      Error: false,
//      HourDecimal: NN.NN,
//      HourInt: NN,
//      MinuteInt: NN,
//      Format12: "SS:SS SS",
//      Format24: "SS:SS"
//  }
// or
//  {
//      Error: true,
//      Message: "Error Message"
//  }
function ParseTime(sTime)
{
    var match;

    var HH12;
    var HH24;
    var MM60;
    var AMPM;

    ///////////////////////////////////////////////////////////////////////////////////////
    if((match = ParseTime_NOW_MATCH.exec(sTime)) != null)
    {
//              console.log(match);
        return {Error: true, Message: "Unsupported format"};
    }
    ///////////////////////////////////////////////////////////////////////////////////////
    else if((match = ParseTime_PLUS_MATCH.exec(sTime)) != null)
    {
//              console.log(match);
        return {Error: true, Message: "Unsupported format"};
    }
    ///////////////////////////////////////////////////////////////////////////////////////
    else if((match = ParseTime_24_MATCH.exec(sTime)) != null)
    {
//              console.log("24");
//              console.log(match);
        HH24 = parseInt(match[1], 10);
        MM60 = parseInt(match[2], 10);

        if(HH24 > 23 || MM60 > 59)
        {
            return {Error: true, Message: "Invalid Hour or Minute (24)."};
        }
        else if(HH24 == 0)
        {
            HH12 = 12;
            AMPM = 'AM';
        }
        else if(HH24 <= 11)
        {
            HH12 = HH24;
            AMPM = 'AM';
        }
        else if(HH24 == 12)
        {
            HH12 = HH24;
            AMPM = 'PM';
        }
        else
        {
            HH12 = HH24 - 12;
            AMPM = 'PM';
        }

    }
    ///////////////////////////////////////////////////////////////////////////////////////
    else if((match = ParseTime_12_MATCH.exec(sTime)) != null)
    {
//              console.log(match);
        AMPM = ((match[3] == 'A' || match[3] == 'a') ? 'AM' : 'PM');
        HH12 = parseInt(match[1], 10);
        MM60 = parseInt(match[2], 10);

        if(HH12 > 12 || HH12 < 1 || MM60 > 59)
        {
            return {Error: true, Message: "Invalid Hour or Minute (12)."};
        }
        else if(HH12 == 12 && AMPM == 'AM')
        {
            HH24 = 0;
        }
        else if(AMPM == 'AM')
        {
            HH24 = HH12;
        }
        else if(AMPM == 'PM')
        {
            HH24 = HH12 + 12;
        }
    }
    ///////////////////////////////////////////////////////////////////////////////////////
    else
    {
        return {Error: true, Message: "Invalid Time Format."};
    }

    return {
        Error       : false,
        HourDecimal : HH24 + (MM60 / 60),
        HourInt     : HH24,
        MinuteInt   : MM60,
        Format12    : HH12 + ':' + (MM60 < 10 ? "0"+MM60 : MM60) + ' ' + AMPM,
        Format24    : (HH24 < 10 ? "0"+HH24 : HH24) + ':' + (MM60 < 10 ? "0"+MM60 : MM60)
    }

}
gahooa
Thanks gahooa, this is interesting
Joe
A: 

Can you do this with a GUI and restrict the user input? Processing the text seems super error prone otherwise (on the part of the user, not to mention the programmer), and for hours worked... you sort-of want to get that right.

tom10