2

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;
        }

    }
Mizipzor
  • 51,151
  • 22
  • 97
  • 138

5 Answers5

4

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
  • 123,721
  • 27
  • 225
  • 246
  • 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 Is Fiddling By Firelight Sep 09 '10 at 14:47
  • 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 Sep 09 '10 at 14:51
  • +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 Sep 09 '10 at 17:51
1

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
  • 9,531
  • 1
  • 24
  • 43
1

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
  • 33,729
  • 8
  • 75
  • 94
0

I used a regex option (I know normally the worse thing to use but the system I was using required it... don't ask):

Regex.Replace("935", "^([0-9]{3})$", "0$1"); // outputs "0935"
dav_i
  • 27,509
  • 17
  • 104
  • 136
0

I solved this as follows:

DateTime.TryParseExact(
    userInput,
    new[] { "%H", "Hm", "H:m" },
    CultureInfo.InvariantCulture,
    DateTimeStyles.None,
    out parsedDate);

I must give credit for the help I got from: Why is TryParseExact failing on Hmm and Hmmss?

Community
  • 1
  • 1
davmos
  • 9,324
  • 4
  • 40
  • 43