| Bytes | Lang | Time | Link |
|---|---|---|---|
| 117 | Swift 6 | 250603T183914Z | macOSist |
| nan | awk | 250604T142135Z | jena |
| 046 | Perl 5 p | 250602T183314Z | Xcali |
| 067 | APL+WIN | 250603T131548Z | Graham |
| 033 | Charcoal | 250603T074050Z | Neil |
| 050 | JavaScript ES6 | 250602T184448Z | Arnauld |
| 024 | Jelly | 250602T233952Z | Jonathan |
| 066 | Google Sheets | 250602T152524Z | doubleun |
| 061 | JavaScript Node.js | 250602T055534Z | l4m2 |
| 075 | JavaScript Node.js | 250602T095444Z | Ted |
| 056 | Retina 0.8.2 | 250602T085557Z | Neil |
| 026 | 05AB1E | 250602T075831Z | Kevin Cr |
Swift 6, 122 117 bytes
{s in(s+"").matches(of:/(.+):(..)/).map{Int($0.1)!>23||Int($0.2)!>59 ?0:(1...12~=Int($0.1)! ?1:0)+($0.0==s ?1:0)}[0]}
Explanation
{ s in
(s + "")
.matches(of: /(.+):(..)/)
.map {
Int($0.1)! < 24 && Int($0.2)! < 60
? (1...12 ~= Int($0.1)! ? 1 : 0)
+ ($0.0 == s ? 1 : 0)
: 0
}[0]
}
I'll go through this line-by-line.
{ s in
Start a closure. s is a String containing the clock.
(s + "")
.matches(of: /(.+):(..)/)
Extract data from the clock using a regex. (There will always be exactly 1 match.)
- The first capture group picks up one or more characters; in our case, it's always either 1 digit or 2. This is the hour.
- After matching a colon, the next capture group matches exactly 2 characters. This is the minute.
- The AM/PM specifier is intentionally not matched (I'll explain how we detect it later).
Code later down can use dynamic member lookup on the match result to access the captures.
s + ""serves as a hint to the type checker thatsis of typeString.
.map {
Map over the array of matches (which will only have one element). Doing it like this lets us avoid the overhead of using let to bind the match to a variable (which would then necessitate an explicit return).
Int($0.1)! < 24 && Int($0.2)! < 60
Check that the hour part of the clock ($0.1) is less than 24 and that the minute part ($0.2) is less than 60.
$0here is of type(Substring, Substring, Substring).$0.0is the entire match result;$0.1is the hour; and$0.2is the minute.
? (1...12 ~= Int($0.1)! ? 1 : 0)
+ ($0.0 == s ? 1 : 0)
If the hour and minute are in range, then we do two things:
- Check if the hour is within the range
1...12(inclusive on both ends) byabusing the pattern matching operator. - Check if AM/PM was specified by comparing the matched string to the original; if they aren't equal, there's some more text after the minute (i.e. AM/PM).
We convert the results of these checks (which are of type Bool) to Int (1 corresponds to true, 0 to false). We then add these together and return it as the final score.
This bit was adapted from @Arnauld's JavaScript answer; I've reproduced his scoring table here for reference.
1...12 ~= hourmatched == originalScore Examples false(0)false(0)0 14:00 PM,00:00 PMfalse(0)true(1)1 14:00,00:00true(1)false(0)1 07:00 PMtrue(1)true(1)2 07:00
: 0
If the hour and/or minute are not in range, return 0 — this indicates an invalid clock.
}[0]
}
Finally, get the first (only) element from the mapping and implicitly return it.
awk, first stab at golf (162 135 120 bytes)
Even newer version abusing ternary operator and without an array:
# new attempt = 120 bytes
BEGIN{FS="[: ]"}1{o=($1>23||$2>59||(NF>2&&($1>12||$1<1)))?0:(NF>2||(NF<3&&(($1>12&&$1<24)||($1<1&&$2<1))))?1:2;print o}
# older attempt = 135 bytes
{split($1,t,":");o=(t[1]>23||t[2]>59||(NF>1&&(t[1]>12||t[1]<1)))?0:(NF>1||(NF<2&&((t[1]>12&&t[1]<24)||(t[1]<1&&t[2]<1))))?1:2;print o}
It's still awfully long :( I'm sure I could make it shorter by stealing the trick from top answer, but I can't make it work (getting fatal division by zero).
Original answer:
{split($1,t,":");if(t[1]>23||t[2]>59||(NF>1&&(t[1]>12||t[1]<1)))o=0;else if(NF>1||(NF<2&&((t[1]>12&&t[1]<24)||(t[1]<1&&t[2]<1))))o=1;else if(t[1]<12)o=2;print o}
APL+WIN, 67 bytes
Prompts for time.
d←~+/'M'=t←⎕⋄h←⍎¯2↓t←t~'APM: '⋄m←⍎¯2↑t⋄(m<60)×(h<24)×((1≤h)^h≤12)+d
Charcoal, 33 bytes
≔I§⪪θ:⁰ηI∧∧‹⁻Σθη⁶⁰‹η²⁴⁺¬№θM¬÷⊖η¹²
Try it online! Link is to verbose version of code. Due to the way Charcoal's input processing works it is necessary to terminate the input with a newline. Explanation: Turns out to use the same approach as @Arnauld.
≔I§⪪θ:⁰η
Extract the hours from the time.
I∧∧‹⁻Σθη⁶⁰‹η²⁴⁺¬№θM¬÷⊖η¹²
Extract the sum of hours and minutes from the time and subtract the hours, checking that the minutes is less than 60, also that the hours is less than 24, then adding 1 if the input does not contain an M and 1 if the input time is within the 12-hour range.
JavaScript (ES6), 50 bytes
s=>([h,m,p]=s.split(/\W/),h<24&m<60)*((12%h<6)+!p)
Commented
s => // s = input string
( //
[ // get these variables ...
h, // h = hours in "H" or "HH" format
m, // m = minutes in "MM" format
p // p = "AM", "PM" or undefined
] = //
s.split(/\W/), // ... by splitting s on ":" and " "
h < 24 & // make sure that h is less than 24
m < 60 // and m is less than 60
) // this gives 1 for "valid so far", 0 for invalid
* ( // multiply by the "score", i.e. the sum of:
(12 % h < 6) // 1 if 1 ≤ h ≤ 12, 0 otherwise
+ !p // 1 if p is undefined, 0 otherwise
) // (see the table below)
Scoring table
| 12-hour compatible (12 % h < 6) | No suffix (!p) | Score | Examples |
|---|---|---|---|
| 0 | 0 | 0 | 14:00 PM, 00:00 PM |
| 0 | 1 | 1 | 14:00, 00:00 |
| 1 | 0 | 1 | 07:00 PM |
| 1 | 1 | 2 | 07:00 |
Jelly, 24 bytes
ṭḲµḢṣ”:V36%€24¤p60Ḷ¤¤ċcL
A monadic Link that accepts a list of characters matching \d{1,2}:\d{2}( [AP]M)? and yields the count.
How?
A clock will be right \$\binom{N_{24}+N_{12}}{K}\$ times, where:
- \$N_{24}\$ is \$1\$ if the hour and minute can occur on a twenty-four-hour clock, else \$0\$
- \$N_{12}\$ is \$1\$ if the hour and minute can occur on a twelve-hour clock, else \$0\$
- \$k\$ is \$2\$ if an
AMorPMis present, else \$1\$
Note that \$N_{12}\$ can only ever be \$1\$ if \$N_{24}\$ is \$1\$, so all cases are:
| N24 + N12 | K | (N24 + N12)-choose-K |
|---|---|---|
| 1 + 1 | 2 | 1 |
| 1 + 1 | 1 | 2 |
| 1 + 0 | 2 | 0 |
| 1 + 0 | 1 | 1 |
| 0 + 0 | 2 | 0 |
| 0 + 0 | 1 | 0 |
Code breakdown
ṭḲµḢṣ”:V36%€24¤p60Ḷ¤¤ċcL - Link: list of characters, T
Ḳ - split T at space characters
e.g. "14:39" -> ["14:39"]
or "10:50 AM" -> ["10:50", "AM"]
ṭ - append a copy of T -> Parts = [Hr:Min, OptionalAM/PM, T]
µ - start a new monadic chain f(Parts)...
Ḣ - head: remove Hr:Min from Parts and yield -> Hr:Min
ṣ”: - split at colons -> [Hr, Min]
V - evaluate as Jelly code -> [int H, int M]
ċ - count occurrences in... -> N
¤ - ...nilad followed by link(s) as a nilad:
¤ - nilad followed by link(s) as a nilad:
36 - thirty-six
%€24 - modulo [1..36] with 24 -> Hours = [1,2,...,23,0,1,...,12]
¤ - nilad followed by link(s) as a nilad:
60 - sixty
Ḷ - lowered range -> Minutes = [0,1,...,59]
p - {Hours} Cartesian product {Minutes}
L - length of {(remaining) Parts} -> K = 2 if AM/PM else 1
c - {N} choose {K}
Google Sheets, 66 bytes
=ifs(istext(A1),0,1<A1,0,0=A1,1,"M"=right(A1),1,12<hour(A1),1,1,2)
Put the value in cell A1 and the formula in B1. The formula assumes the United States locale and relies on automatic coercion that comes standard with spreadsheets. For some insight, see Working with date and time values in Google Sheets.

JavaScript (Node.js), 61 bytes
-1 byte Ted by non-resurcive
x=>(x=1/x[2]?0+x:x)[3]<6?1+!x[6]>>((h=x[0]+x[1])/12^h%12<1):0
JavaScript (Node.js), 59 bytes from Ted's
x=>+new Date("0 "+x)?x[v=parseInt(x),6]?v&&1:v<13&v>0?2:1:0
JavaScript (Node.js), 89 75 bytes
Thanks to @Neil for optimisations :D
(x,v=parseInt(x))=>isNaN(new Date("0 "+x))?0:/M/.test(x)?v&&1:v<13&&v>0?2:1
Not as good as @l4m2 (also I borrowed your footer code, thanks!) but this a different approach which I think is decent.
In essence:
v is the first 1 or 2 digit number in the clock
- If the clock is not parseable by
Date, return 0. - If the clock ends in
M, return 1 if v is not 0, else 0 (Datesuccessfully parses00:00 AM, but we do not allow this) - If v is between 0 and 13 (not inclusive), return 2, else 1
Retina 0.8.2, 72 56 bytes
A`:[6-9]
\d+
$*
^1{1,12}:|^1{0,23}:1*$|$(?<=^1{1,12}:1*)
Try it online! Link includes test cases. Explanation:
A`:[6-9]
Exclude inputs with minutes over 59.
\d+
$*
Convert the values to unary.
^1{1,12}:|
A time is valid if its hours is 1-12, or...
^1{0,23}:1*$|
... if its hours is 0-23 and has no AM/PM.
$(?<=^1{1,12}:1*)
Count a second match for a time whose hours is 1-12 but has no AM/PM.
05AB1E, 26 bytes
':¡`þ60‹s©12LsåIθdi>®24‹}P
Try it online or verify all test cases.
Explanation:
In pseudo-code, I check three things:
- \$m<60\$ : for every test case; as part of the valid check
- \$1\leq h\leq12\$ : for every test case; for "AM"/"PM" specified test cases as part of the valid check, and for unspecified test cases as part of the +1 count on the result
- \$h<24\$ : only for unspecified test cases; as part of the valid check
':¡ '# Split the (implicit) input-string by ":"
` # Pop and push both parts separated to the stack
þ # Only keep the digits (the minutes) of the top part
60‹ # Check that these minutes are <60
s # Swap so the hours are at the top
© # Store this in variable `®` (without popping)
12L # Push a list in the range [1,12]
så # Check if the hours are in this list
I # Push the input-string again
θ # Pop and keep its last character
di # If it's a digit (aka, it's not "M")
> # Increase the hours_in_[1,12] check by 1
® # Push the hours from variable `®` again
24‹ # Check whether it's <24
} # Close the if-statement
P # Take the product of all two or three values on the stack
# (after which the result is output implicitly)