Вот регулярное выражение, которое я придумал:
^((\d{1,2}:\d{2}\s?([ap]m?)?)|(\d{1,2}\s?[ap]m?))$
Соответствует:
2:10
14:20
10:00am
3:49p
4pm
10a
Но не:
12
22:342
14:0
20rpm
Как видно на Рубуляр
Я думаю, было бы слишком сложно, чтобы он был намного умнее этого. Например, «у меня завтра 2 класса после 2», вы не можете ожидать, что программа правильно определит, какие числа можно интерпретировать как время, если она не способна понимать семантику - но это совсем другая история
PS: регулярное выражение также соответствует строке, например 99:99, которая может быть исправлена, но сделает регулярное выражение еще более запутанным и просто не стоит исправлять IMO.