I'm using the following to parse a time string to a TimeSpan:
string[] formats = { "hhmm", "hmm", @"hh\:mm", @"h\:mm\:ss", @"h\:mm", "hh:mm tt" };
parseSuccess = TimeSpan.TryParseExact(value, formats, CultureInfo.CurrentCulture, TimeSpanStyles.None, out dtValue);
This returns false when I try to parse a value like 9:00 AM, 5:00 PM. What is wrong here?

tt doesn't exist as one of the format specifiers for custom TimeSpan format strings. That makes sense in that TimeSpan is really meant to be a duration, not a time-of-day value - it's unfortunate that DateTime.TimeOfDay does return a TimeSpan.
It's probably simplest to parse the value as a DateTime and then get the time of day from that:
string text = "5:00 PM";
string[] formats = { "hhmm", "hmm", @"hh\:mm", @"h\:mm\:ss", @"h:mm", @"h:mm tt" };
var success = DateTime.TryParseExact(text, formats, CultureInfo.CurrentCulture,
DateTimeStyles.None, out var value);
Console.WriteLine(value.TimeOfDay);
Note that I've corrected the hh:mm tt to h:mm tt as your sample data isn't 0-padded. You quite possibly want HHmm, and HH:mm instead of hhmm and hh:mm as well, to accept values like "21:00".
Alternatively, use my Noda Time library that has a specific type for time-of-day (LocalTime) that can be parsed directly with a LocalTimePattern :)
See more on this question at Stackoverflow