27
\$\begingroup\$

Amazingly, this simple task doesn't seem to exist already, so...

Your task is to write a program that takes as input a 12 hour time, and converts it into "military time", or 24-hour time format.

Input will be in the form:

HH:MM am/pm

Although slight variations are allowed:

  • The space separating the am/pm from the rest of the time is optional.

  • The last part can either be "am"/"pm", or "a"/"p".

  • Any capitalization is fine.

Output will be the inputted time, transformed into 24-hour format. It can be a number, or a string.

For both the input and output:

  • 0s in the first place are optional. 0s in the last 3 places are mandatory.

  • the delimiter separating hours and minutes can be a ":", " " (a space), or nothing.

Other notes:

  • Midnight can be expressed either as 0000, or 2400.

  • Midnight will be considered "am", while noon will be considered "pm".

Your program can be a function, or a full program, and should either return the result, or output it to the stdout. Trailing whitespace is ok.

Examples (you aren't required to support every format):

12:00am -> 0000
1200 pm -> 1200
1234Am  -> 00:34
134a    -> 134
01:34PM -> 13 34
1234pm  -> 1234  

This is code golf, so the smallest number of bytes wins. Since this is so trivial to solve using a built-in, it would be nice to see some code that manually solves this (but use of built-ins are fine).

Just to clarify, you aren't required to support every possible format. Supporting only a single input and a single output format (of your choice) is fine. I would however like to limit the format as outlined above (which is already quite free). {1134,'pm'}, for example, would be unacceptable.

\$\endgroup\$
  • \$\begingroup\$ What formats are we required to support? Just one? \$\endgroup\$ – redstarcoder Jan 6 '17 at 14:33
  • \$\begingroup\$ @redstarcoder Yes. Any format that conforms to the requirements above is ok, but as noted above the example cases, you aren't required to support every format. A single format of input and a single format of output are fine. \$\endgroup\$ – Carcigenicate Jan 6 '17 at 14:36
  • \$\begingroup\$ are our formats allowed to require leading zeroes? For example, 01:54pm would work, but 1:54pm wouldn't? \$\endgroup\$ – FlipTack Jan 6 '17 at 20:41
  • \$\begingroup\$ Yes. You can pick the required input format. \$\endgroup\$ – Carcigenicate Jan 6 '17 at 20:47
  • \$\begingroup\$ Are we allowed to include seconds in our input and output? \$\endgroup\$ – 12Me21 Jan 26 '17 at 13:26

39 Answers 39

1
2
1
\$\begingroup\$

AWK, 18 bytes

/p/{$1+=1200}$0=$1

Try it online!

Input is hhmm am/pm, i.e. the time as a 3 or 4 digit number with whitespace separating the am/pm designator.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

T-SQL, 48 bytes

CREATE PROC t @ DATETIME AS PRINT FORMAT(@,'Hm')

Must be used in SQL Server 2012+. The input is converted to '1900-01-01 HH:mm:ss.sss' and the FORMAT function gets the hour and minute part.

Usage:

EXECUTE t @ = '10:45pm'
| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Retina, 46 bytes

.*p
12$0
\D

..(?=..)
$*!
!+
$.&
^12
00
^24
12

Try it online!

Input must supply at minimum 2 digits each for HH & MM and a "p" if applicable. Output only returns a leading 0 for the 12 AM hour (I'm unclear if the note on midnight should be interpreted literally, but if not, that would shave off 1 byte). Works on the example cases, provided you prepend 134a with a 0.

Non-digit characters (besides p) are stripped. Lines lacking a "p/pm" are treated as AM. PM times are shifted up by 12 hours, and 12/24 are simply swapped at the end.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Groovy, 49 50 bytes

+1 byte because of invalid output format :(

{t=it.split();(​​​t[0]​as int)+(t[2]=="pm"?12:0)+t[1]}​​​​​​​

Input is given in format HH MM am or HH MM pm. Output is in format HHMM.

Quick explaination:

it.split() splits the input in an array [hour, minute, am/pm ] Then it returns the hours concatenated with the minutes, but adds 12 to the hours before returning if the last item of the array is "pm"

Previous solution

 {t=it.split();t[2]=="pm"?(t[0]as int)+12+t[1]:it}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ What about "12 00 pm"? \$\endgroup\$ – 12Me21 Apr 1 '18 at 5:36
1
\$\begingroup\$

tcl, 66 67 68

scan $s %d%c t c;puts [format %03d [expr $c>97?($t+1200)%2400:$t]]

in http://rextester.com/live/VDSG70132 there is a script that runs it from 0100a to 1259p

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

Tcl, 76 bytes

if [scan $s %dp%n n -]>1 {set n [expr ($n+1200)%2400]};puts [format %03d $n]

Try it online!

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

tcl, 88 102 107 110

Naïve attempt:

puts [format %03d [expr {[regsub p $s "" n]?([scan $n %d]+1200)%2400:[regsub a $s ""]}]]

testable on http://rextester.com/live/XUKJU62994 where there is a script that runs it from 0100a to 1259p

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Clojure, 143 108 96 bytes

+54 bytes to fix a "noon/midnight bug" where times within the hour after noon/midnight would return wrong values.

-35 bytes by getting ideas from other answers. If the hour is == 12, I make it 0. This reduces the number of cases that need to be dealt with.

-12 bytes thanks to BRFennPocock.

I'm not sure if I'm allowed to compete. I can remove this or mark it as non-competing if necessary. It's not like I'm winning though :)

V3

#(let[[h m[i]](clojure.string/split % #" ")](str(+(if(= i\p)12 0)(mod(Integer/parseInt h)12))m))

Ungolfed:

(defn to-24h [time-str]
  ; Split the string on spaces, then deconstruct out the hour,
  ; minute, and whether it's am or pm
  (let [[hours minutes [ampm]] (clojure.string/split time-str #" ")
        pm? (= ampm \p)
        h (mod (Integer/parseInt hours) 12)]
    (str (+ (if pm? 12 0) h)
         minutes)))

V2

#(let[[h m[i]](clojure.string/split % #" ")h(Integer/parseInt h)h(if(= h 12)0 h)](str(if(= i\p)(+ h 12)h)m))

Ungolfed:

(defn to-24h [time-str]
  ; Split the string on spaces, then deconstruct out the hour,
  ; minute, and whether it's am or pm
  (let [[hours minutes [ampm]] (clojure.string/split time-str #" ")
        pm? (= ampm \p)
        h (Integer/parseInt hours)
        h (if (= h 12) 0 h)] ; 0-out the hours if they're equal to 12 to minimize cases
    (str (if pm?
           (+ h 12)
           h)
         minutes)))

V1

#(let[[h m[i]](clojure.string/split % #" ")p(= i\p)h(Integer/parseInt h)t(= h 12)](str(cond(and(not t)p)(+ h 12)(and t (not p))(- h 12):e h)m))

The Java-interop and library reference are killer, but I think they're probably the best options here. Thank God for restructuring though. Easiest parse ever.

Takes input in the format 12 34 am, and outputs as 034. Drops 0s in the first place.

Ungolfed:

(defn to-24h [time-str]
  ; Split the string on spaces, then deconstruct out the hour,
  ; minute, and whether it's am or pm
  (let [[hours minutes [ampm]] (clojure.string/split time-str #" ")
        pm? (= ampm \p)
        h (Integer/parseInt hours)
        twelve? (= h 12)]
    ; Dispatching to figure out how much needs to be added/subtracted.
    (str (cond (and (not twelve?) pm?) (+ h 12)
               (and twelve? (not pm?)) (- h 12)
               :e h)
         minutes)))

With Built-ins, 83 bytes

(fn[s](let[p #(java.text.SimpleDateFormat. %)](.format(p"km")(.parse(p"h m a")s))))
;                                                     Parse the 3 pieces ^
;                          Format into "squished" format ^

Basically the Java answer. Input must be in the format 12 34 pm

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You can shave it to 99 by using mod: #(let[[h m[i]](clojure.string/split % #" ")h(mod(Integer/parseInt h)12)](str(if(= i\p)(+ h 12)h)m)) — and shave another byte by moving if inside +: #(let[[h m[i]](clojure.string/split % #" ")h(mod(Integer/parseInt h)12)](str(+(if(= i\p)12 0)h)m)) \$\endgroup\$ – BRPocock Jan 11 '17 at 17:00
  • \$\begingroup\$ Oh, and then just inline h for 96B: #(let[[h m[i]](clojure.string/split % #" ")](str(+(if(= i\p)12 0)(mod(Integer/parseInt h)12))m)) \$\endgroup\$ – BRPocock Jan 11 '17 at 17:02
  • \$\begingroup\$ @BRFennPocock Cool, thanks. I'm about to start work, so I don't want to start fiddling with it now. I'll update it tonight. Thanks again. \$\endgroup\$ – Carcigenicate Jan 11 '17 at 17:06
0
\$\begingroup\$

Acc!!, 101 97 bytes

(N*10+N)%12*60+N*10+N-528+N/98*720
Write _/600+48
Write _/60%10+48
Write _%60/10+48
Write _%10+48

Expects input in the form 0123a (all digits mandatory, no separators, lowercase am/pm indicator). Gives output like 0123.

Try it online!

Explanation

Acc!! reads one character code at a time using the N special value. With a sufficiently strict input format, we can read five characters and do a little math to compute the number of minutes since midnight, and from there it's easy to print out the 24-hour time.

Deobfuscated, the first line is

((N-48)*10 + N-48)%12*60 + (N-48)*10 + N-48 + N/98*720
  • The first character is the tens digit of the hour, and the second character is the ones digit of the hour. We convert both from character codes to the associated digits (N-48) and combine them into a single number (...*10 + ...).
  • If the hour is 12, we want it to become 0; otherwise, it should stay the same. This is accomplished by a mod 12 operation (%12). (Note: in the golfed code, the %12 also takes care of the -48s here.)
  • Multiply by 60 in preparation to add the minutes (*60).
  • Process the next two characters as minutes digits, in the same way as the hours digits ((N-48)*10 + N-48).
  • Now we want to add 12 to the hour if the next character is p (ASCII 112), or add nothing if it's a (ASCII 97).
    • We can distinguish the two by int-dividing by 98: N/98 is 0 if N is 97 and 1 if N is 112.
    • Adding 12 hours is the same as adding 720 minutes: + ...*720

This value is stored in the accumulator, _.

Now for our output, we need:

  • Tens digit of the hour
  • Ones digit of the hour
  • Tens digit of the minute
  • Ones digit of the minute

The hour is _/60, and the minute is _%60. The tens digit of a number (less than 100) is .../10, and the ones digit is ...%10. Combining a few operations where possible gives the four expressions in the code (remembering that we need +48 to convert digits back to corresponding character codes).

| improve this answer | |
\$\endgroup\$
1
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.