One day, you boss walks up to you and tells you he needs to know the time. He still insists after you pointing at the clock directly above your head, and confesses he has a severe case of dyscalculia, which causes him to not even be able to see numbers on a clock. While you're still left wondering how someone unable to see numbers can run a company, he tells you to "do something about it", whatever that is.

Your task is now to create a program or function that, given an input time with hours and minutes (seconds are optional)(hours are 24-based), outputs a readable english sentence, that doesn't contain numbers.

It is not necessary to ouput the seconds.

For example, 08:45:15 should output quarter to nine o'clock AM

Test cases

00:00:00 > twelve night
12:00:00 > twelve noon
06:00:00 > six o'clock AM
18:00:00 > six o'clock PM
06:15:00 > quarter past six o'clock AM
18:45:00 > quarter to seven o'clock PM
11:30:00 > half past eleven o'clock AM
13:22:00 > twentytwo minutes past one o'clock PM
17:43:00 > seventeen minutes to six o'clock PM
00:09:00 > nine minutes past twelve o'clock AM


Outputting numbers can be both in the form of fortytwo or forty-two, whichever you like best. Standard loopholes are not allowed. Trailing newlines are allowed, but the sentence should be on 1 line.

Input can be in any format you want, for example HH:MM:SS, HH,MM,SS, HHMMSS, HH/MM/SS, or any variant without seconds, but you should make the input format clear in your answer. It's not needed to output one minute instead of one minutes

for minutes ranging from 0 trough 30 (0 and 30 included) you should use past, and for the others you should use to

Because time is relative, shortest code in bytes wins.

  • 1
    \$\begingroup\$ This looks like a nice challenge, bit I strongly suggest removing the bonuses. Unless you know exactly what you are doing, those are one thing to avoid when writing challenges. \$\endgroup\$
    – Denker
    Feb 5, 2016 at 14:35
  • 1
    \$\begingroup\$ Two more (minor) points: why does it matter how you write forty-five when the test cases clearly state quarter to as the appropriate input format? (just change it to forty-two ;) ) Also, kolmogorov-complexity is not an appropriate tag. \$\endgroup\$
    – Sanchises
    Feb 5, 2016 at 14:36
  • \$\begingroup\$ @sanchises edited. I misunderstood the kolmogorov tag, and thought it had to do with problems in outputting any string, instead of a given string only. \$\endgroup\$ Feb 5, 2016 at 14:41
  • 1
    \$\begingroup\$ @TimmyD The -70% one was a joke, I didn't imagine anyone would try to translate french in code-golf. However, I feel that having negative size is a decent reward for a perfect french-english translator \$\endgroup\$ Feb 5, 2016 at 14:43
  • \$\begingroup\$ That looks much better, but a few more formatting clarifications are needed. Specifically regarding choosing between "past" and "to" -- from the examples, it sounds like you're wanting to only utilize the numbers one to twenty-nine, with quarter replacing fifteen and half representing thirty, but that's not clear. Also, for future questions, I heartily recommend the Sandbox to work out the kinks before posting. \$\endgroup\$ Feb 5, 2016 at 14:43

8 Answers 8


LaTeX, 466 bytes

\usepackage{fmtcount,etoolbox}\def\x{\the\numexpr}\def\n[#1]{\numberstringnum{#1}}\def\h[#1]{\ifnumequal{#1}{0}{twelve night}{\ifnumequal{#1}{12}{twelve noon}{\ifnumless{#1}{13}{\n[#1] o'clock AM}{\n[\x#1-12\relax] o'clock PM}}}}\def\m[#1]{\ifnumequal{#1}{15}{quarter}{\ifnumequal{#1}{30}{half}{\n[#1] minutes}}}\newcounter{c}\def\f[#1]#2{\ifnumequal{#2}{0}{\h[#1]}{\ifnumless{#2}{31}{\m[#2] past \h[#1]}{\setcounter{c}{\x60-#2\relax}\m[\thec] to \h[\x#1+1\relax]}}}

Just call the macro \f as \f[hour]{minutes}, here some tests:


\textbf{Test cases:} \\ \\
00:00 = \f[00]{00} \\
12:00 = \f[12]{00} \\
06:00 = \f[6]{00} \\
18:00 = \f[18]{00} \\
06:15 = \f[6]{15} \\
18:45 = \f[18]{45} \\
11:30 = \f[11]{30} \\
13:22 = \f[13]{22} \\
17:43 = \f[17]{43} \\

\textbf{More test cases:} \\ \\
00:13 = \f[00]{13} \\
12:12 = \f[12]{12} \\
12:15 = \f[12]{15} \\
11:45 = \f[11]{45} \\
11:41 = \f[11]{41} \\

  • \$\begingroup\$ Do you know how and where I can try this? I tried arachnoid and papeeria but they didn't work. \$\endgroup\$ Feb 5, 2016 at 20:56
  • \$\begingroup\$ @DennisvanGils I tried this pastebin.com/kdpSQHSb in papeeria and it worked. \$\endgroup\$
    – Bob
    Feb 5, 2016 at 22:35
  • \$\begingroup\$ Tried it with the pastebin code and that worked, guessing it didn't work because I didn't have \documentclass[a4paper,12pt]{article} \$\endgroup\$ Feb 5, 2016 at 22:46

Python 2, 498 bytes

Input comes as function argument. First hour and then minute, both as integer. Result gets printed to the screen.

def g(h,m):
 a=lambda x:"twelve one two three four five six seven eigth nine ten eleven".split()[x%12];b=lambda x:a(x)+" o'clock "+["AM","PM"][x>=12]
 if m:
    z=m if m<30else 60-m;print(a(z)if z<13else("twenty"+(a(z-20)if z-20else"")if z>19else"thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split()[z-13])if m%15else"quarter"if m-30else"half")+(" minutes"if m-30and m%15else"")+[" past "+b(h)," to "+b(h+1)][m>30]
 else:print"twelve "+("noon"if h else "night")if h in[0,12]else b(h)

Try it online! (with slightly extended testcases)

That was a pretty fun golf. Although all those nested ternaries drove me a little bit crazy ^^
I was planning on doing this in Pyth after this, but I don't think that I am mentally able to do this at the moment (or ever).


def ungolfed(h,m):
    toStr=lambda x:"twelve one two three four five six seven eigth nine ten eleven".split()[x%12]
    hour=lambda x:toStr(x)+" o'clock "+["AM","PM"][x>=12]
    minute=lambda x:"twenty"+(toStr(x-20)if x-20else"")if x>19else"thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split()[x-13]
    if m:
        if m==30:
            if m%15==0:
                z=m if m<30 else 60-m
                if z<13:
        print s+(" minutes"if m-30and m%15else "")+[" past "+hour(h)," to "+hour(h+1)][m>30]
        if h in[0,12]:
            print"twelve "+("noon"if h else "night")
            print hour(h)
  • \$\begingroup\$ Your [x>12] should be [x>=12], 12:01 is PM, not AM \$\endgroup\$ Feb 5, 2016 at 21:05
  • 1
    \$\begingroup\$ @DennisvanGils Good catch, thank you! Fixed it. :) We Germans aren't that comfortable with this whole AM/PM-thing I guess :P \$\endgroup\$
    – Denker
    Feb 5, 2016 at 21:12
  • \$\begingroup\$ I'm dutch, so I had to look it up myself to be sure too. \$\endgroup\$ Feb 5, 2016 at 21:14

Python 3.8 (pre-release), 376 bytes

lambda h,m,N='one two three four five six seven eight nine ten eleven twelve thir four _ six seven eigh nine'.split()+['']:m+h%12and((m%15<1and['half','quarter'][m%2]or(lambda T:T<20and N[T-1]+T//13*'teen'or'twenty'+N[T%10-1])([m,60-m][m//31])+' minutes')+[' past ',' to '][m//31])*(m>0)+N[(h-1+m//31)%12]+" o'clock "+'APA'[(h+m//31)//12]+'M'or'twelve n'+['ight','oon'][h//12]

Try it online!

Stumbled on this challenge by accident and decided to give it a go.


Handles the edge cases of midnight and noon by using a standard ... and ... or ... that has become ubiquitous for any Python code-golfer. For these edge cases return twelve , followed by night or noon depending on the hour.

In the general case, we build a string representing the minutes. If the minutes is 15 or 45, we start with quarter; if it is 30, we start with half; otherwise, we determine whether to use itself or 60 minus itself (think of it this way: 5:21 is 21 past 5, but 6:37 is 23 to 7, not 37 to 7). Let M denote this number. We build the appropriate number:

  • if M is less than 20, start with the conversion of itself to letters, minus teen.
  • if M is 20 or more, start with twenty.


  • if M is less than 20 but more than 12, add teen.
  • if M is 20 or more, add the conversion to letters of its units digit, with nothing being added if its units digit is zero.

If the minutes count was not divisible by 15, we also have to add minutes.

Next, we have to deal with past and to. This is determined by indexing a list.

If the minutes count is zero, the above string goes to waste. Because zero or something similar does not appear in N, one might assume that building the string for zero would cause the program to error with an IndexError. But because Python allows negative indices, it doesn't error. When the minutes count is zero, we just don't use the above string.

Next we have to deal with o' clock, which is simply done using % and our list of text numbers again.

Finally we append AM or PM depending on the hour.


Vyxal, 155 bytes

1→m?\:/⌊:£t¬[¥h12ḋ:→d÷¬[0→m12←d h¬[‛⟇Ẏ|‛₄⟨]|¥h12%]|¥t15ḋ:→d÷¬[←d h∷[‛ɽ¢|‛»⌈]|¥t:30>[60ε]‛«¦]¥t30≤[‛×Ṡ¥h12Ḋ[12|¥h12%]|‛to¥h12%›]]←m[`o'∵₅`¥h12<‛PAi\M+]WḢ∆ċṄ

Takes input as HH:MM.

Try it Online!


Javascript, 384 381 bytes

(h,m)=>(o='one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thir,,fif,,,eigh,'.split`,`,o=o.map((x,i)=>i>11?(x||o[i-10])+'teen':x),(a=m>30,t=12,m?(m-30?m%15?((m=a?59-m:m)>=(b=a?19:20)?'twenty'+((m%=b)?o[a?m:m-1]:''):o[a?m:m-1])+' minutes':'quarter':'half')+(a?' to ':' past '):'')+(h%t|m?o[(a?h:(h?h-1:11))%t]+` o'clock ${h>t?'P':'A'}M`:o[11]+(h-t?' night':' noon')))

                :o[a?m:m-1])+' minutes'
        :'half')+(a?' to ':' past ')
        o[(a?h:(h?h-1:11))%t]+` o'clock ${h>t?'P':'A'}M`
    :o[11]+(h-t?' night':' noon'))

document.body.innerHTML = '<pre>' +
'f(00,00) = ' + f(00,00) + '\n' +
'f(12,00) = ' + f(12,00) + '\n' +
'f(06,00) = ' + f(06,00) + '\n' +
'f(18,00) = ' + f(18,00) + '\n' +
'f(06,15) = ' + f(06,15) + '\n' +
'f(18,45) = ' + f(18,45) + '\n' +
'f(11,30) = ' + f(11,30) + '\n' +
'f(13,22) = ' + f(13,22) + '\n' +
'f(17,43) = ' + f(17,43) + '\n' +
'f(00,09) = ' + f(00,09) + '\n' +
'f(23,59) = ' + f(23,59) + '\n' +

  • 1
    \$\begingroup\$ Works fine, and even has the Perl one beat by 4 bytes! Nice trick with fif, eigh etc btw. \$\endgroup\$ Feb 8, 2016 at 16:25
  • \$\begingroup\$ You have wrong output for the last one. Should be one minutes to twelve o'clock AM (not PM) \$\endgroup\$
    – ophact
    May 16, 2022 at 11:53

Perl 5, 367 361 385 Bytes


use integer;$_=<>;~/(\d+):(\d+)/;@b=(one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve); @x=(@b,thirteen,(map{/fi/?quarter:$_.teen}@b[3..8]),(map{twenty.$_}'',@b[0..8]),half);@y=map{/^q|h/?$_:$_.' minutes'}@x;$s=1-$2/31;$y=abs((-29,0)[$s]+$2%31)-1;$h=$1-$s;print((+-$2?@y[$y].' '.(to,past)[$s].' ':'').@b[$h++%12]." o' clock ".($h%12?(AM,PM)[$h/12]:(night,noon)[$h/12%2]))

Formatted and test cases:

use integer;
    # capture (implicit $_)
    # bare words
    # construct up to 30
    # quarter/half business
    @y=map{/^q|h/?$_:$_.' minutes'}@x;
    # whether we are to/past
    # num minutes wrapped around
    # num hours
    print($_.' > ');
    #minute component
          ?@y[$y].' '.(to,past)[$s].' '
        # hours
        ." o' clock "
        # Night/Noon
    print "\n"
  • \$\begingroup\$ This seems to give one minutes to one o' clock night as output for 00:01, instead of one minutes past twelve o'clock AM or one minutes past twelve night \$\endgroup\$ Feb 5, 2016 at 23:51
  • \$\begingroup\$ You're right. I've added test cases around the 12 times. \$\endgroup\$
    – walpen
    Feb 6, 2016 at 0:32

C, 478 bytes

*w(i){char*p,*n[]={"","one","two","three","four","five","six","seven","eight","nine","ten","eleven","twelve","thirteen","fourteen","fifteen","sixteen","seventeen","eighteen","nineteen"};return i>19?asprintf(&p,"twenty%s",n[i%20]),p:n[i];}main(m,h){scanf("%d%d",&h,&m);m>30?h++:0;printf("%s%s%s%s%s %s\n",m%15?m>30?w(60-m):w(m):m^30?m?"quarter":"":"half",m%15?" minutes":"",m?m>30?" to ":" past ":"",w((h+11)%12+1),h%12||m?" o'clock":"",h%12||m?h>11?"PM":"AM":h?"noon":"night");}

Test Cases

$ echo "0 0" | ./a.out
twelve night
$ echo "12 0" | ./a.out
twelve noon
$ echo "6 0" | ./a.out
six o'clock AM
$ echo "18 0" | ./a.out
six o'clock PM
$ echo "6 15" | ./a.out
quarter past six o'clock AM
$ echo "18 45" | ./a.out
quarter to seven o'clock PM
$ echo "11 30" | ./a.out
half past eleven o'clock AM
$ echo "13 22" | ./a.out
twentytwo minutes past one o'clock PM
$ echo "17 43" | ./a.out
seventeen minutes to six o'clock PM
$ echo "0 9" | ./a.out
nine minutes past twelve o'clock AM

Try it here.

  • \$\begingroup\$ Looks fine. Also interesting how 00 00 gives the correct output, while 00:00 gives one minutes past twelve o'clock AM, although I'm sure that has something to do with how you parse input. \$\endgroup\$ Feb 8, 2016 at 16:22
  • \$\begingroup\$ Yes, it expects two integers (hours and minutes). scanf probably doesn't parse '00:00' as one might hope :) \$\endgroup\$ Feb 8, 2016 at 16:53

Batch, 779 774 752 749 bytes

@echo off
set t=%1
set p=past
if %m% gtr 30 set p=to&set/am=60-m,h+=1
for %%a in ("one minute.1" quarter.15 half.30)do if %%~xa==.%m% set o=%%~na&goto g
set o=
if %m% gtr 20 set o=twenty-&set/am-=20
for %%a in (one.1 two.2 three.3 four.4 five.5 six.6 seven.7 eight.8 nine.9 ten.10 eleven.11 twelve.12 thirten.13 fourteen.14 sixteen.16 seventeen.17 eighteen.18 nineteen.19 twenty.20)do if %%~xa==.%m% set o=%o%%%~na minutes
if not %m%==0 set o=%o% %p% 
set a= AM
if %h% gtr 12 set a= PM&set/ah%%=12
if %h%==0 (echo %o%twelve night)else if %h%==12 (echo %o%twelve noon)else for %%a in (one.1 two.2 three.3 four.4 five.5 six.6 seven.7 eight.8 nine.9 ten.10 eleven.11)do if %%~xa==.%h% echo %o%%%~na o'clock%a%

Change the second line to set t=%1%time% to default to saying the current time if no time is provided.

Edit: Saved 22 bytes by optimising my set/a statements. Saved 3 bytes thanks to @EʀɪᴋᴛʜᴇGᴏʟғᴇʀ.

When I wrote my original version I made the mistake of saying the time as I would actually say it, i.e. o'clock only on exact hours, and midnight and midday instead of twelve night and twelve noon:

@echo off
set t=%1
set/a h=%t:~0,2%
set/a m=%t:~3,2%
set p=past
if %m% gtr 30 set p=to&set/a m=60-m&set/a h=h+1
for %%a in ("one minute.1" quarter.15 half.30) do if %%~xa==.%m% set o=%%~na&goto g
set o=
if %m% gtr 20 set o=twenty-&set/a m=%m%-20
for %%a in (one.1 two.2 three.3 four.4 five.5 six.6 seven.7 eight.8 nine.9 ten.10 eleven.11 twelve.12 thirten.13 fourteen.14 sixteen.16 seventeen.17 eighteen.18 nineteen.19 twenty.20) do if %%~xa==.%m% set o=%o%%%~na minutes
set o=%o% %p% 
set a= AM
if %h% gtr 12 set a= PM&set/a h=h%%12
if %m%==0 set a= o'clock%a%&set o=
if %h%==0 (echo %o%midnight)else if %h%==12 (echo %o%midday)else for %%a in (one.1 two.2 three.3 four.4 five.5 six.6 seven.7 eight.8 nine.9 ten.10 eleven.11) do if %%~xa==.%h% echo %o%%%~na%a%
  • \$\begingroup\$ Your 12:00 and 00:00 tests don't work, they only echo midnight and midday instead of twelve night and twelve noon. I really like how you use fake extensions and names in your for loop btw. \$\endgroup\$ Feb 5, 2016 at 20:54
  • 2
    \$\begingroup\$ @DennisvanGils There are probably other tests it fails; I'll fix it up later. \$\endgroup\$
    – Neil
    Feb 5, 2016 at 21:04
  • \$\begingroup\$ @DennisvanGils I think it's working now, and conveniently it's also shorter! \$\endgroup\$
    – Neil
    Feb 6, 2016 at 18:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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