I have a simple task that should be relatively easy to implement by means of code. Your goal is to write a program that will output the time written in Spanish, given the time in HH:MM format. Many people likely don't know how to do this, so I will elaborate.

Time in Spanish is fairly logical. It usually follows the pattern of "Es la/Son las (hour) y (minutes)." Hours are in a 12-hour format, and "Es la" is only used if the hour is 1 (i.e. one o'clock). The minutes are a different story. If the minute is less than 30, then it is represented as shown above. If the minute is over 30, however, then the hour is rounded up and the minute is subtracted. For example, 7:35 is translated to the equivalent of "8 hours minus 25 minutes." Some more examples will be given below. The list of Spanish numbers that are necessary can be found here. There are accents on some numbers, but these are not necessary.

Note: The source says "uno," but to be grammatically correct it should be "una." This shouldn't affect any answers so far.

Note 2: Also, "cero" is not necessary. If your program outputs "Es la una" or "Son las tres," that is fine with me. Sorry for these rule changes.


  • Input will be provided through STDIN or the equivalent in your language.
  • No reading from outside libraries.
  • Your code can do anything with invalid input.


  • -10 if your code adds these extra phrases - "y cuarto" for :15, "y media" for :30, and "menos cuarto" for :45 (rounded up).
  • -15 if your code can handle A.M. and P.M., responding with "de la mañana" and "de la tarde," accordingly.
  • -15 if your code can translate the current time if no input is provided.


  • This is a code-golf challenge, and it will be scored by bytes, not characters.


Input: 8:10 Output: Son las ocho y diez.

Input: 6:45 Output: Son las siete menos quince (or cuarto).

Input: 1:29 Output: Es la una y veintinueve.

Input: 12:55 Output: Es la una menos cinco.

Let me know if there is anything to specify here. This is my first question, so it is definitely not perfect.

  • \$\begingroup\$ For your information, you can get feedback before you post at the sandbox. \$\endgroup\$ Nov 7, 2014 at 1:39
  • 1
    \$\begingroup\$ @Sparr I doubt it. There are probably enough numbers that have to be hardcoded for this to happen - at least 45 characters worth, even accounting for patterns. \$\endgroup\$
    – mdc32
    Nov 7, 2014 at 1:41
  • 1
    \$\begingroup\$ @Dennis Hmm... I guess either would be fine. Es la una y cero is pretty repetitive, so I see your point. I didn't specify this, so I guess either one would be fine. Thanks for the feedback. \$\endgroup\$
    – mdc32
    Nov 7, 2014 at 3:18
  • 1
    \$\begingroup\$ You should add 01:21 as a test case, because at least one answerer was confused by what you said about una vs uno. \$\endgroup\$ Nov 8, 2014 at 9:23
  • 3
    \$\begingroup\$ The second bonus is (almost?) never worth it because the phrases "de la", "manana" and "tarde" alone count up to 16 bytes already. \$\endgroup\$
    – britishtea
    Nov 8, 2014 at 19:11

13 Answers 13


JavaScript (ES6) 308 316

Edit2 bug fix Edit forgot to claim the bonus
As a program with I/O via popup

alert(((h=(10-~h+(m>30))%12)?'Son las '+N(1+h):'Es la una')+(m>30?' menos '+N(60-m):-m?' y '+N(~~m):''))

As a testable function

  ((h=(10-~h+(m>30))%12)?'Son las '+N(1+h):'Es la una')+(m>30?' menos '+N(60-m):-m?' y '+N(~~m):'')

Test In FireFox/FireBug console

   console.log(F(i+':'+i)+'. '+F(i+':'+(i+15))+'. '+F(i+':'+(i+30))+'. '+F(i+':'+(i+45)))


Son las doce. Son las doce y cuarto. Son las doce y media. Es la una menos cuarto
Es la una y uno. Es la una y dieciseis. Son las dos menos veintinueve. Son las dos menos catorce
Son las dos y dos. Son las dos y diecisiete. Son las tres menos veintiocho. Son las tres menos trece
Son las tres y tres. Son las tres y dieciocho. Son las cuatro menos veintisiete. Son las cuatro menos doce
Son las cuatro y cuatro. Son las cuatro y diecinueve. Son las cinco menos veintiseis. Son las cinco menos once
Son las cinco y cinco. Son las cinco y veinte. Son las seis menos veinticinco. Son las seis menos diez
Son las seis y seis. Son las seis y veintiuno. Son las siete menos veinticuatro. Son las siete menos nueve
Son las siete y siete. Son las siete y veintidos. Son las ocho menos veintitres. Son las ocho menos ocho
Son las ocho y ocho. Son las ocho y veintitres. Son las nueve menos veintidos. Son las nueve menos siete
Son las nueve y nueve. Son las nueve y veinticuatro. Son las diez menos veintiuno. Son las diez menos seis
Son las diez y diez. Son las diez y veinticinco. Son las once menos veinte. Son las once menos cinco
Son las once y once. Son las once y veintiseis. Son las doce menos diecinueve. Son las doce menos cuatro
Son las doce y doce. Son las doce y veintisiete. Es la una menos dieciocho. Es la una menos tres

Yes, the least expected language to appear on a golf contest, coded by the world's worst golfer, is back!

Java - 676 bytes (716-10-15-15)


class A{void main(String[]a){java.util.Calendar c=java.util.Calendar.getInstance();int h,m;String s="";h=c.get(c.HOUR);m=c.get(c.MINUTE);String[]e={"doce","una","dos","tres","quatro","cinco","ses","siete","ocho","nueve","diez","once","doce","trece","catorce","quarto","çseís","çsiete","çocho","çnueve","xe","xiuno","xidós","xitrés","xiquatro","xicinco","xiséis","xisiete","xiocho","xinueve","media"};for(int i=0;++i<30;e[i]=e[i].replace("ç","dieci"),e[i]=e[i].replace("x","vient"));s+=(h==1&m<30|h==12&m>30)?"Es la ":"Son las ";s+=(m<=30)?e[h]:(h==12&m>30)?e[1]:e[h+1];s+=(m==0)?" certas":(m<=30)?" y "+e[m]:" menos "+e[60-m];s+=(c.get(c.AM_PM)==0)?" de la mañana.":" de la tarde.";System.out.println(s);}}


public class A {

    public static void main(String[] a) {
        java.util.Calendar c = java.util.Calendar.getInstance();
        int h, m;
        String s = "";
        h = c.get(c.HOUR);
        m = c.get(c.MINUTE);
        String[] e = {"doce", "una", "dos", "tres", "quatro", "cinco", "ses", "siete", "ocho", "nueve", "diez", "once", "doce", "trece", "catorce", "quarto", "çseís", "çsiete", "çocho", "çnueve", "xe", "xiuno", "xidós", "xitrés", "xiquatro", "xicinco", "xiséis", "xisiete", "xiocho", "xinueve", "media"};
        for (int i = 0; ++i < 30; e[i] = e[i].replace("ç", "dieci"), e[i] = e[i].replace("x", "vient"));
        s += (h == 1 & m < 30 | h == 12 & m > 30) ? "Es la " : "Son las ";
        s += (m <= 30) ? e[h] : (h == 12 & m > 30) ? e[1] : e[h + 1];
        s += (m == 0) ? " certas" : (m <= 30) ? " y " + e[m] : " menos " + e[60 - m];
        s += (c.get(c.AM_PM) == 0) ? " de la mañana." : " de la tarde.";

Deals with the quarto and media, with the AM/PM and has no input. So I can claim all the bonuses, even though that, if I didn't implement those features, I'd have an even lower score, lol. facepalms


Python 3: 294 chars - 10 = 284

if m>30:h=h%12+1;m=60-m;t="menos"
print(["Es la una","Son las "+d[h]][h>1],t,[d[m]or"cero",["dieci","veint"+'ei'[m>20],"media"][m//10-1]+d[m%10]][m>15]+".")

This gets the ten-point bonus for using "cuarto" and "media"

We read the hours and minutes as ints. If the minutes are above 30, we move to the next hour, measure minutes away from 60, and change the conjunction to "menos".

The list d has translations of Spanish numbers up to 15. We make d[0] be '' to prevent things like "diecicero". This is done by awkwardly calling split(' ') with an initial space; the regular split would just ignore it. The zero-minute case is handled later.

To get numbers above 15, we combine the tens-digit string with the appropriate one-digit string. 15 and 30 are written as "media" and "cuarto" at no cost.

Python 3 saves one char net over Python 2: -4 for input instead of raw_input, +2 for parens in print, +1 for //.

  • \$\begingroup\$ I'm sad to say that it's more complicated than that. 01:21 should be la una y veintiuno because hours are feminine but minutes are masculine. \$\endgroup\$ Nov 8, 2014 at 9:22
  • \$\begingroup\$ @PeterTaylor I see. Is uno/una the only number affected? \$\endgroup\$
    – xnor
    Nov 8, 2014 at 9:45
  • \$\begingroup\$ Yes, although if it went up to numbers in the hundreds there would be others. \$\endgroup\$ Nov 8, 2014 at 12:29
  • \$\begingroup\$ @PeterTaylor Fixed, for 5 chars. \$\endgroup\$
    – xnor
    Nov 8, 2014 at 16:08
  • \$\begingroup\$ @edc65 Whoops, I forgot to paste in the actual change of una to uno, should work now. \$\endgroup\$
    – xnor
    Nov 8, 2014 at 19:13

PHP, 351 349 360 - 15 = 345 Bytes


This program is not command line: it takes input via $_GET[0]. You may have to disable notices in your php.ini. Now comes with auto time with no input, thanks to Niet the Dark Absol.

Tricks used:

~(...) saves one byte by bitwise inverting a string, as you don't need quote marks as PHP usually assumes all ASCII from 0x80 to 0xFF is a string.

<?=preg_filter(...,...): The <?= is a shortcut for writing <? echo. preg_filter() usually applies replacements on a string using a regex, but we can use the depreciated /e modifier to evaluate the resulting string as PHP code. Hence, instead of having to split the input string into two separate variables, we can use backreferences ($1 and $2) on the matched input string, saving large amounts of bytes.

  • 2
    \$\begingroup\$ You can claim the -15 bonus if you use $_GET[0]?:date(~·Å–) to subtract 3 from your score. \$\endgroup\$ Nov 8, 2014 at 15:32
  • \$\begingroup\$ Thanks. I have made that and one other minor improvement. \$\endgroup\$
    – Tryth
    Nov 9, 2014 at 1:09

C++: 474 ... 422 411 bytes

This version is redeeming the cuarto/media bonus (-10).

int main(int u,char**v){char*b[]={"cero","una","dos","tres","cuatro","cinco","seis","siete","ocho","nueve","diez","once","doce","trece","catorce","cuarto","dieci","veinti","media",0,"veinte"};int h=atoi(v[1]),m=atoi(v[1]+2+(v[1][2]>57)),n=m>30,o;h=n?h%12+1:h;m=o=n?60-m:m;if(u=m>15&m!=20)o=m%10;printf("%s %s %s %s%s",h>1?"Son las":"Es la",b[h],n?"menos":"y",u?b[m/10+15]:"",b[o?o:m]);}

My first attempt ever at code golfing! Will try to improve it this weekend.


int main(int u,char**v)
int h=atoi(v[1]),m=atoi(v[1]+2+(v[1][2]>57)),n=m>30,o;
printf("%s %s %s %s%s",h>1?"Son las":"Es la",b[h],n?"menos":"y",u?b[m/10+15]:"",b[o?o:m]);
  • 1
    \$\begingroup\$ Couldn't you m%=10 \$\endgroup\$
    – Timtech
    Nov 7, 2014 at 12:20
  • \$\begingroup\$ Good point! Unfortunately in my new revision I have to assign that value to a different variable, so I can't. \$\endgroup\$
    – BMac
    Nov 7, 2014 at 16:47
  • \$\begingroup\$ Ok, just wondering :) \$\endgroup\$
    – Timtech
    Nov 7, 2014 at 22:35
  • \$\begingroup\$ 401 bytes \$\endgroup\$
    – ceilingcat
    Jul 12, 2020 at 19:58

Lua, 450 - 10 (cuarto/media) - 15 (manana/tarde) = 425

n={'uno','dos','tres','cuatro','cinco','seis','siete','ocho','nueve','diez','once','doce','trece','catorce','cuarto',[20]='veinte',[30]='media'}for i=1,9 do n[i+10]=n[i+10]or'dieci'..n[i]n[i+20]='veinti'..n[i]end H,M=io.read():match('(%d+):(%d+)')H,M=H+0,M+0 X,Y='tarde',' y 'if H<12 then X='manana'end if M>30 then H,M,Y=H+1,60-M,' menos 'end H=(H-1)%12+1 S=H==1 and'es la una'or'son las '..n[H]if M>0 then S=S..Y..n[M]end S=S..' de la '..X print(S)
  • Dropped 12 bytes by rewriting the generator for 21-29.
  • Dropped 1 more by replacing H>=12 with H<12 and switching the dependent expression around.
  • Dropped 4 more by polluting the global namespace from a function (evil, but in the interest of golfing :).
  • Fixed the regex, I forgot the colon. Doesn't change the byte count, however.
  • Fixed the case of zero minutes, swapped table.concat out for string ops, and added @edc65's suggestion, ultimately adding 22 bytes.
  • I am shamed. Pulling the function body out into the main chunk reduced the length by a whopping 15 bytes.
  • \$\begingroup\$ Should use 'una' for hours, 'uno' for minutes. So '01:01' should give Es la una y uno \$\endgroup\$
    – edc65
    Nov 8, 2014 at 19:17

D - 484 bytes

import std.stdio,std.conv,std.string;void main(){auto n="cero una dos tres cuatro cinco seis siete ocho nueve diez once doce trece catorce quince dieciséis diecisiete dieciocho diecinueve e iuno idos itres icuatro icinco iseis isiete iocho inueve treinta".split;auto p=stdin.readln()[0..$-1];int b=to!int(p[0..$-3]),a=to!int(p[$-2..$]);auto c=a;b=a>30?b+1:b;b%=12;a=a>30?60-a:a;writefln("%s %s %s %s", b==1||b==12?"Es la":"Son las",n[b],c>30?"menos":"y",(a/10==2?"vient":"")~n[a]);}
  • \$\begingroup\$ Should use 'una' for hours, 'uno' for minutes. So '01:01' should give Es la una y uno \$\endgroup\$
    – edc65
    Nov 8, 2014 at 19:17

Python 3, 409 bytes

d='cero uno dos tres cuatro cinco seis siete ocho nueve diez once doce trece catorce quince dieciseis diecisiete dieciocho diecinueve veinte xuno xdos xtres xcuatro xcinco xseis xsiete xocho xnueve treinta';d=str(d.replace('x','veinti')).split();t=input().split(':');i=int(t[1]);j=int(t[0]);print(["Son las","Es la"][1<(2*j+i/30)%24<=3],[d[[j%12+1,j][i<31]],'una'][j==1],'y'if i<31 else'menos',d[min(i,60-i)])
  • \$\begingroup\$ A long list of strings can be shortened like 'string1 string2 string3'.split() \$\endgroup\$
    – xnor
    Nov 7, 2014 at 3:13
  • \$\begingroup\$ @xnor Alright, thanks! \$\endgroup\$
    – user10766
    Nov 7, 2014 at 3:18
  • \$\begingroup\$ @hosch250 Also, make sure you're following the comments in the original post again. I will likely be making many minor rule changes, and these will probably help you golf. \$\endgroup\$
    – mdc32
    Nov 7, 2014 at 3:20
  • \$\begingroup\$ @mdc32 Adjusting right now. I had some errors anyway. \$\endgroup\$
    – user10766
    Nov 7, 2014 at 3:22
  • 2
    \$\begingroup\$ You can shorten d[j]if i<31 else d[(j+1)%12] to d[(j+(i>30))%12]. In general, if your two alternatives has a similar structure, you can often make a simple expression that equals each respective one depending on the Boolean. \$\endgroup\$
    – xnor
    Nov 7, 2014 at 9:16

Ruby, 313 (338 - 15 - 10)

This solution translates the current time when no input was given and adds the three phrases "y cuarto", "y media" and "menos cuarto".

t,x,s=Time,$*[0],%w[cero una dos tres cuatro cinco seis siete ocho nueve diez once doce trece catorce cuarto]
c=x ?t.parse(x):t.new
m<31?(a=" y "):(h,m,a=h+1,60-m," menos ")
$><<(h<2?"Es la ":"Son las ")+s[h]+a+s[m]
  • \$\begingroup\$ Does it work? Tried in ideone, input '01:01', current time 20:09, output: Son las ocho y diecioch \$\endgroup\$
    – edc65
    Nov 8, 2014 at 19:12
  • \$\begingroup\$ All the test cases passed for me locally. I see something went wrong with copying the numbers, so I'll fix that. \$\endgroup\$
    – britishtea
    Nov 8, 2014 at 19:19
  • 1
    \$\begingroup\$ "seite" should be "siete" and "neuve" should be "nueve" \$\endgroup\$
    – jmm
    Nov 10, 2014 at 17:38

Bash 423

(433 - 10 = 423, removing diacritics and cuarto we could go down to 381)

IFS=: read h m
[ $m -gt 30 ]&&h=$(($h+1))&&s=menos
[ -z ${m%0} ]&&s=en&&m=punto
S=" séis siete ocho nueve"
n=(punto una dos trés cuatro cinco $S diez {on,do,tre,cator,quin}ce ${S// / dieci} veinte)
n=($(eval echo "${n[@]}" veinti\$\{n[{1..9}]\}))
n=(${n[@]} media\  $(tac -s' '<<<${n[@]}))
a=Son\ las
[ $h = 1 ]&&a=Es\ la
echo $a ${n[$h]/p*/cero} $s ${o[$m]/%a/o}
  • \$\begingroup\$ It already uses 'una' for hours and 'uno' for minutes. Look more carefully :) \$\endgroup\$
    – Ángel
    Nov 11, 2014 at 9:39
  • \$\begingroup\$ Sorry, it's not clear at a glance and bash is difficult to test in windows. +1 then. \$\endgroup\$
    – edc65
    Nov 11, 2014 at 16:12
  • \$\begingroup\$ @edc66, the ordinals are in femenine, then ${o[$m]/%a/o} replaces the trailing a with o for the minutes. \$\endgroup\$
    – Ángel
    Nov 12, 2014 at 20:29

Perl - 297 - 10 + 1 = 288 (counting the p flag)

Edit: thanks to @guifa, I can now claim a bonus :)

#!/usr/bin/perl -p
sub n{($_=shift)%10?(once,doce,trece,catorce,cuarto)[$_>9?$_-11:5]||('',dieci,veinti)[$_/10].(0,un.pop,dos,tres,cuatro,cinco,seis,siete,ocho,nueve)[$_%10]:(cero,diez,veinte,media)[$_/10]}/:/;$a=$`%12;$b=$';$c=$b>30?(++$a,$b=60-$b,menos):'y';$_=($a-1?'Son las ':'Es la ').n($a,a)." $c ".n($b,o).'.'

Here is the same code in multiple lines for readability:

sub n {
        ($_ = shift) % 10
            ? (once, doce, trece, catorce, cuarto)[$_ > 9 ? $_ -11 : 5]
                || ('', dieci, veinti)[$_ / 10]
                . (0, un.pop, dos, tres, cuatro, cinco, seis, siete, ocho, nueve)[$_ % 10]
            : (cero, diez, veinte, media)[$_ / 10]
$a = $` % 12;
$b = $';
$c = $b > 30 ? (++$a, $b = 60 - $b, menos) : 'y';
$_ = ($a - 1 ? 'Son las ' : 'Es la ') . n($a, a) . " $c " . n($b, o) . '.'
  • \$\begingroup\$ Should use 'una' for hours, 'uno' for minutes. So '01:01' - should give Es la una y uno \$\endgroup\$
    – edc65
    Nov 8, 2014 at 19:04
  • \$\begingroup\$ If you just change "quince" to "cuarto" and "treinta" to "media", you'll nab a -10 bonus. \$\endgroup\$ Nov 9, 2014 at 1:59
  • \$\begingroup\$ @edc65 I hope It is OK now... \$\endgroup\$
    – core1024
    Nov 10, 2014 at 1:02

Scala 652 bytes - 25

import java.util.Scanner
object S extends App{val s=new Scanner(System.in).useDelimiter("[:\n]")
var h=s.nextInt
var m=s.nextInt
if(m>30)m-=60 else h-=1
val n=m.abs
val p=h%12
val l=List("una","dos","tres","cuatro","cinco","seis","siete","ocho","nueve","diez","once","doce","trece","catorce","cuarto")
val k=List("úno","dós","trés",l(3),l(4),"séis",l(6),"ócho",l(8))
println(s"""${if(p==0)"Es la"else"Son las"} ${l(p)} ${if(m>0)"y "else if(m<0)"menos "}${if(n==1)"uno"else if(n==0)""else if(n<=15)l(n-1) else if(n==30)"media"else if(n<20)"dieci"+k(n-11)else if(n==20)"veinte"else"veinti"+k(n-21)} de la ${if(h<12)"mañana"else"tarde"}.""")}

Pyth: 277 a bunch more 234 - 10 (cuarto/media bonus) = 224 bytes

Now reduced over 50 bytes from original!

=H" y "ANkmsdcz\:Kc"cero uno dos tres cuatro cinco seis siete ocho nueve diez once doce trece catorce cuarto veinte"dI>k30=k-60k=H" menos "=N?1qN12+N1)++?+"Son las "@KN>N1"Es la una"H??eKqk20?@Kk<k16+?"dieci"<k21+PeK\i@K%kT<k30"media"

Insanely long for Pyth but that's because there's some raw data. Can probably be golfed even further. Uses obvious technique of splitting task up into hours, y/menos, tens digit of minutes, and ones digit of minutes and then translates numbers using translation array and everything else using a crap-ton of ternaries.

=H" y "                 Set H to " y "
A                       Double Assignment
 Nk                     The variables N and k (hours and mins)
 m  cz\:                Map input split by ":"
  sd                    Make both int
Kc"..."d                Set K to string split by spaces
I>k30                   If k>30
     =k-60k             Set k to 60-k
     =H" menos "        Set k to menos instead of y
     =N                 Set N
      ?   qN12          If N=12
       1                Go back to one
       +N1              Increment N
)                       Close entire if block 
+                       Concat of hours and minutes
 +                      Concat hours and y/menos
  ?           >N1       If hour greater than one
   +                    Concat of son las and hour
    "Son las "          "Son las "
    @KN                 Index translation array for hour
   "Es la una"          Else es la una
  H                     " y " or " menos "
 ?               <k30   If minutes less than 30
  ?  qk20               If minutes is 20
   ek                   Last element of array (veinte)
   ?   <k16             Else check if minutes<16
    @Kk                 Get minutes directly from array
    +                   Else tens and ones sum
     ?       <k21       If minutes<21
      "dieci"           "dieci"
      +PeK\i            Else get veinti from veinte
     @K%kT              Ones digit of minutes
  "media"               Else get "media"

Golfing History

  • 10 bytes - bonus, quince and trienta can just be replaced in translation array so no changes required except translation essay and its same size.
  • 6 bytes - reorganized ternaries - unfortunately this removed the 4 consecutive ternary operators :(
  • 6 bytes - other various golfing
  • 6 bytes - golfed initial hour/min assignment
  • +3 bytes - fixed uno/una
  • 3 bytes - constructed veinti from veinte, not hardcoded
  • 18 bytes - extracted dieci from teens<16
  • 2 bytes - removed some spaces in there for no reason
  • 2 bytes - used \ for one char strings
  • \$\begingroup\$ For 1 character strings, such as ":" and "i", use \: and \i instead. \$\endgroup\$
    – isaacg
    Jan 30, 2015 at 5:36
  • \$\begingroup\$ @isaacg oh cool I didn't know you could do that, updating \$\endgroup\$
    – Maltysen
    Jan 30, 2015 at 14: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.