9

I want to be able to parse strings of time (hours, minutes, seconds) where the hours run from 0 to 23, and where the preceding zero for one-digit hours is optional.

Examples of time strings that I want to be able to parse into valid DateTime objects:

  • 212540
  • 061525
  • 94505

I am trying to use the C# method DateTime.ParseExact to manage the parsing, but I cannot for the life of it come up with a format string that can handle the "single-digit hour without preceding zero" scenario.

How should I specify the DateTime.ParseExact format string to sufficiently parse all examples above with the same line of code?

Inspired by the MSDN page on custom date and time formats, I have tried the following approaches:

DateTime.ParseExact(time_string, "Hmmss", CultureInfo.InvariantCulture);
DateTime.ParseExact(time_string, "%Hmmss", CultureInfo.InvariantCulture);
DateTime.ParseExact(time_string, "HHmmss", CultureInfo.InvariantCulture);

All these format strings work for the first two example cases above, but faced with a single-digit hour and no preceding zero, all formulations throw a FormatException.

Anders Gustafsson
  • 15,837
  • 8
  • 56
  • 114
  • 1
    Why not add a zero if `time_string` is only five characters wide? – Bart Friederichs Sep 24 '13 at 10:31
  • You would need to use Reflector to disassemble the code for `DateTime.Parse` because from what I can see `Hmmss` works for the first 2 but not the last. The strange thing is according to the docs it *technically* shouldn't work for the second and should on the last. – James Sep 24 '13 at 10:41

3 Answers3

5

You can insert delimiters between hours, minutes and seconds like this:

string timeString = "94505";
string formatedTimeString = Regex.Replace(str, @"\d{1,2}(?=(\d{2})+$)", "$&:");
var datetime = DateTime.ParseExact(formatedTimeString, "H:mm:ss", CultureInfo.InvariantCulture);

UPDATE: I've found the cause of failure when parsing "94505" with format string "Hmmss":

What's happening is that H, m and s actually grabs two digits when they can, even if there won't be enough digits for the rest of the format. So the for example with the format Hmm and the digits 123, H would grab 12 and there would only be a 3 left. And mm requires two digits, so it fails.

So basically you have two options for handling the "single-digit hour without preceding zero" scenario:

  1. Change time format: place hours to the end (for example, "ssmmH" or "mmssH") or use delimiters (for example, "H:mm:ss")

  2. Modify the string like I've suggested earlier or like keyboardP has.

Community
  • 1
  • 1
Alexander Simonov
  • 1,564
  • 1
  • 9
  • 15
  • Thanks, @Alexander, but ideally, I would not want to modify my time string at all before parsing. If I still have to, it would be considerably easier to pad the string with a preceding zero, like [keyboardP is suggesting](http://stackoverflow.com/a/18979163/650012). – Anders Gustafsson Sep 24 '13 at 11:12
  • 1
    Thanks for the update and the link to the related question, @Alexander. Since I cannot influence the format of the input time strings, this is quite a frustrating situation, but now I at least know a little more about the background. – Anders Gustafsson Sep 24 '13 at 11:55
2

You could pad your input string if you know that you'll always have six characters.

string input = "94505";
if(input.Length < 6)
   input = input.PadLeft(6, '0');

(Or use input.Length == 5 if you have other valid formats that are shorter).

keyboardP
  • 68,824
  • 13
  • 156
  • 205
  • 2
    Thanks, @keyboardP, but it seems a little far-fetched that I would have to pad my time string to make the parsing work? After all, using "Hmmss" as an *output* format specifier works perfectly well, for example `new DateTime(2013, 9, 24, 7, 0, 0).ToString("Hmmss")` would output "70000". I am still hoping that someone can come up with the "magic" format string :-) – Anders Gustafsson Sep 24 '13 at 11:02
  • Ran a few tests and it seems that the `h` or `H` is trying to take two characters which throws the rest of the format off. If you reverse the order to `05459` and use `ssmmH`, it works fine. Not an answer but might help :) – keyboardP Sep 24 '13 at 11:39
  • Thanks for looking further into this, @keyboardP. Very annoying limitation; I'll see how I'll solve this in my own code in the end. – Anders Gustafsson Sep 24 '13 at 11:52
  • In your particular case, you can intuit what the correct meaning is, but in more general cases you get problems like the input string "111" and the format string "Hm". This could have been encoded from 1:11, or it could have been 11:01. They had to choose a deterministic, performant, and ideally intuitive (or at least easy to remember) rule on how to resolve these. Greedy parsing fits all of those most of the time. As it turns out, we humans know that Hmmss should be deterministic over 12345, but it could be a significant amount of overhead for the processor. – GrandOpener Mar 04 '14 at 03:49
0

What about using:

DateTime.ParseExact(time_string, "Hmmss", CultureInfo.InvariantCulture).ToString("HH:mm:ss")

chimino
  • 29
  • 1
  • 4