views:

107

answers:

3

I'm trying to convert user entered times into TimeSpans. Because TimeSpan does not have a TryParseExact method I'm using the one in DateTime and converting the output from it.

The formats I want to handle are: 04:00, 0400, 4:00, and 400. The first three aren't a problem and correspond to the first three cases in the if/else structure in the method below. The forth could correspond to either of the last two, but neither is working.

    private void dataGridView1_CellParsing(object sender, DataGridViewCellParsingEventArgs e)
    {
        CultureInfo enUS = new CultureInfo("en-US");
        DateTime parsedDate = new DateTime();

        string userInput = (string)e.Value;
        if (DateTime.TryParseExact(userInput, "HH:mm", enUS, DateTimeStyles.None, out parsedDate))
        {
            e.Value = parsedDate.TimeOfDay;
            e.ParsingApplied = true;
        }
        else if (DateTime.TryParseExact(userInput, "HHmm", enUS, DateTimeStyles.None, out parsedDate))
        {
            e.Value = parsedDate.TimeOfDay;
            e.ParsingApplied = true;                
        }
        else if (DateTime.TryParseExact(userInput, "H:mm", enUS, DateTimeStyles.None, out parsedDate))
        {
            e.Value = parsedDate.TimeOfDay;
            e.ParsingApplied = true;
        }
        else if (DateTime.TryParseExact(userInput, "hmm", enUS, DateTimeStyles.None, out parsedDate))
        {
            e.Value = parsedDate.TimeOfDay;
            e.ParsingApplied = true;
        }
        else if (DateTime.TryParseExact(userInput, "Hmm", enUS, DateTimeStyles.None, out parsedDate))
        {
            e.Value = parsedDate.TimeOfDay;
            e.ParsingApplied = true;
        }

    }
+1  A: 

just a general comment- you can have a boolean variable (like isValidDate) initialized to false and set to true where you have

 e.Value = parsedDate.TimeOfDay;
 e.ParsingApplied = true;

then move that code to an if block at the end

if isValidDate then
        {
            e.Value = parsedDate.TimeOfDay;
            e.ParsingApplied = true;
        }
Beth
+1  A: 

You can use this overload of DateTime.TryParseExact to specify multiple formats in one go. It doesn't look like the Hmm format will work but you can use integer parsing instead like this:

internal static class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine(ParseTime("04:00"));
        Console.WriteLine(ParseTime("0400"));
        Console.WriteLine(ParseTime("4:00"));
        Console.WriteLine(ParseTime("400"));
    }

    public static TimeSpan ParseTime(string input)
    {
        CultureInfo cultureInfo = new CultureInfo("en-US");
        DateTime parsedDateTime;

        if (!DateTime.TryParseExact(input, new [] { "HH:mm", "HHmm", "H:mm" }, cultureInfo, DateTimeStyles.None, out parsedDateTime))
        {
            int parsedInt32;

            if (!int.TryParse(input, NumberStyles.None, cultureInfo,  out parsedInt32))
            {
                throw new ArgumentException("Unable to parse input value as time in any of the accepted formats.", "input");
            }

            int remainder;
            int quotient = Math.DivRem(parsedInt32, 100, out remainder);

            return new TimeSpan(quotient, remainder, 0);
        }

        return parsedDateTime.TimeOfDay;
    }
}
Daniel Renshaw
Thanks for mentioning the overload. Int parsing was my planned fallback, but I was hoping I'd overlooked something.
Dan Neely
+2  A: 

I would simply do a string.PadLeft(int totalWidth, char paddingChar) on the user input to ensure the length of the string is 4 characters (minimum). As a result, your Hmm input would then qualify for the HHmm format.

userInput = userInput.PadLeft(4, '0'); // "400" becomes "0400"

If your string already meets or exceeds 4 in length, it will be left unmodified.

Anthony Pegram
This could work in part, but would need more code to protect against other problems. "030" (12:30 am) is valid, "30" is not.
Dan Neely
Yes, in this case, 30 would become 0030. If 2-character inputs are invalid, you would need to enforce that with code when using this method. It would be a trivial check, obviously.
Anthony Pegram
+1: This is the best workaround. When the "H" custom format specifier is used, it actually tries to get 2 digits rather than just one, thus invalidating the "mm" specifier (hence the 12:00am syndrome). The "Hmm" format specifier has been historically problematic and has never worked out of the box. On the other hand, "HHmm" has always worked fine.
code4life