Print a word clock

TI-Basic, 335 334 bytes

Pretty much perfect, since the TI-84 calcs have 8x16 letter displays, and this is 8x15. Date is taken as input in order to support calcs earlier than TI-84, which do not have internal clocks. With no arguments, Input gets input by default into X and Y, similar to Prompt X,Y. So make X be hours (anything >=0 will work correctly) and have Y be minutes.

Input 
ClrHome
int(Y/5-5.6
Output(1,1,"IT IS
If Ans=~6
Output(8,8,"O'CLOCK
If 2=abs(3-abs(Ans
Output(3,1,"FIVE
If 4=abs(Ans
Output(1,12,"TEN
If 3=abs(Ans
Output(2,1,"QUARTER
If 2=abs(Ans
Output(2,8,"TWENTY
If sum(abs(Ans)={1,2,4,5
Output(3,5,"MINUTES
If not(Ans
Output(1,7,"HALF
If Ans<1 and Ans+6
Output(4,1,"PAST
If Ans>0
Output(3,14,"TO
12fPart(12(^~1)(X+(Y>32->A
If not(Ans
12->A
{21,6,10,25,30,41,45,51,61,66,70,81
.05Ans(A
Output(int(4+Ans),20fPart(Ans),sub("ONE   TWO   THREE FOUR  FIVE  SIX   SEVEN EIGHT NINE  TEN   ELEVENTWELVE",6A-5,6

Note 1: For TI-84+ you can replace Input with something like getTime:Ans(1->X:getTime:Ans(2->Y. Also, int(Y/5-5.6 is equivalent to round(Y/5,0)-6. And no, in this case int( could not be interchanged with iPart(.

Note 2: This prints the input just fine, but for asethetic reasons you probably want Pause at the end of the program to avoid the Done message upon program termination.

Edit: Byte count lowered because tokenized (see note on previous revision), also a bug is fixed thanks to @Neil. Third, fixed a bug I found myself which would have costed 2 bytes but the optimization actually saved 1 byte overall, and I also realized that I am getting input so I don't need to call getTime (duh?). Last, for anyone who wants to confirm this byte count, the only two byte token is sub(.


PHP, 387 384 353 352 342 323 310 306 298 293 291 bytes

Thanks @Christoph for golfing along with his excellent findings!
At least 45 bytes are on his account; 16 or more inspired by him.

A Marvel Team Up!

IT IS <?foreach([HALF,TEN,QUARTER,TWENTY,FIVE,MINUTES,TO,PAST,TWO,THREE,ONE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN,ELEVEN,TWELVE,"O'CLOCK"]as$w)echo strstr([w,ghj,dhj,ej,fhj,fghj,cj,fghi,fhi,ei,dhi,ghi][$m=date(i)/5+.5].mklnopqrstuvm[date(h)-($m<7)],99+$i)?$w:$w^$w^"       ","
  "[$i++%3^$i<3];

loops through the data and checks if the current index is in a generated string that contains the indexes of the words to light up (mapped to letters); appends linebreak or space depending on the index.

May yield notices if you dont´t use the default value for error_reporting (22519).

Test it online.

breakdown

IT IS <?
foreach([HALF,TEN,QUARTER,TWENTY,FIVE,MINUTES,TO,PAST,  // loop through words
TWO,THREE,ONE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,TEN,ELEVEN,TWELVE,"O'CLOCK"]as$w)
    echo strstr(                    // search word index in ...
        [w,ghj,dhj,ej,fhj,fghj,cj,fghi,fhi,ei,dhi,ghi][$m=date(i)/5+.5] // minutes & co
        .mklnopqrstuvm[date(h)-($m<7)]                          // hours (-1 if minutes<33)
    ,99+$i                          // (map 0..20 to c..w)
    )?$w:$w^$w^"       ",           // if found, print string, else print spaces
    "\n  "[$i++%3^$i<3];            // if $i in [3,5,8,...], print newline, else space

Golfs:

  • $x/5+.5|0 is two bytes shorter than round($x/5).
  • Two calls of date(h) are one byte shorter than assigning the date result to a variable.
  • Using a single assigment golfed away the variable that the light-up indexes were stored in.
  • A string for the light-up indexes instead of an array saved around 30 bytes.
  • $w<A is three bytes shorter than $w=="\n" - make sure that no space is after a delimiter!
  • abs saved 8 bytes on the minutes word.
  • Thanks @Christoph: Moving from numbers to letters for the map rendered the separators obsolete and allowed one more string instead of array; saved 23+8 bytes.
    Using underscore as the first index allowed to include it into the "it is" alternatives; and it rendered the quotation for the hours obsolete.
  • Duplicating the 1 value in the hours map rendered the modulo obsolete and that with additional golfing saved 10 bytes. Inspired by @Christoph.
  • Calculating the linebreaks and spaces instead of hardcoding them shaved off 19 bytes.
  • Bit logic on strings saves 7 bytes: str_pad("",strlen($w)) -> $w^$w^" ". (Christoph)
  • If strtr´s second parameter is no string, it will be interpreted as an ascii code. saves 5 bytes.

JavaScript (ES6), 291 303 295 bytes

Takes input as two integers with currying syntax (h)(m)

h=>m=>console.log(`IT IS HALF TEN
QUARTER TWENTY
FIVE MINUTES TO
PAST TWO THREE
ONE FOUR FIVE
SIX SEVEN EIGHT
NINE TEN ELEVEN
TWELVE O'CLOCK`.replace(/\S+/g,w=>(k/=2)&1?w:w.replace(/./g,' '),k=[x=1<<19,88,81,66,84,92,64.5,60,52,34,49,56,x,h=(m<33?h+11:h)%12][m/5+.4|0]*16|6|2048<<(h?h-(h<3):2)))

How it works

The whole clock can be seen as 23 LEDs that are either ON or OFF. So, the clock state is a 23-bit integer.

Bit | Word        Bit | Word
----+--------     ----+--------
00  | IT          12  | ONE
01  | IS          13  | FOUR
02  | HALF        14  | FIVE
03  | TEN         15  | SIX
04  | QUARTER     16  | SEVEN
05  | TWENTY      17  | EIGHT
06  | FIVE        18  | NINE
07  | MINUTES     19  | TEN
08  | TO          20  | ELEVEN
09  | PAST        21  | TWELVE
10  | TWO         22  | O'CLOCK
11  | THREE             

The minutes logic that we need to implement to enable the right words is not as simple as one might think at first glance. A magic-golfing formula may exist but I went the easy way and decided to just hardcode each possible configuration:

Minutes | Words                    | Enabled bits | Value   | Value / 8
--------+--------------------------+--------------+---------+----------
  00    | O'CLOCK                  | 22           | 4194304 | 524288
  05    | FIVE MINUTES PAST        | 6, 7, 9      | 704     | 88
  10    | TEN MINUTES PAST         | 3, 7, 9      | 648     | 81
  15    | QUARTER PAST             | 4, 9         | 528     | 66
  20    | TWENTY MINUTES PAST      | 5, 7, 9      | 672     | 84
  25    | TWENTY FIVE MINUTES PAST | 5, 6, 7, 9   | 736     | 92
  30    | HALF PAST                | 2, 9         | 516     | 64.5
  35    | TWENTY FIVE MINUTES TO   | 5, 6, 7, 8   | 480     | 60
  40    | TWENTY MINUTES TO        | 5, 7, 8      | 416     | 52
  45    | QUARTER TO               | 4, 8         | 272     | 34
  50    | TEN MINUTES TO           | 3, 7, 8      | 392     | 49
  55    | FIVE MINUTES TO          | 6, 7, 8      | 448     | 56

Test cases

let f =

h=>m=>console.log(`IT IS HALF TEN
QUARTER TWENTY
FIVE MINUTES TO
PAST TWO THREE
ONE FOUR FIVE
SIX SEVEN EIGHT
NINE TEN ELEVEN
TWELVE O'CLOCK`.replace(/\S+/g,w=>(k/=2)&1?w:w.replace(/./g,' '),k=[x=1<<19,88,81,66,84,92,64.5,60,52,34,49,56,x,h=(m<33?h+11:h)%12][m/5+.4|0]*16|6|2048<<(h?h-(h<3):2)))
;

[[13,15],[13,58],[14,30],[15,35],[10,0],[12,0]].map(([h, m]) => {
  console.log('h = ' + h, 'm = ' + m);
  f(h)(m);
});