A small Java program that says the time in words in Swiss German or English on the command line. It took some inspiration from the Qlocktwo wall clock by Biegert & Funk (www.qlocktwo.com). It can be used in conjunction with STDIN readers to do some fun things like having a cow say the time.
It was built on modern Java using the Gradle build system and the Java Module System. Using jlink it
can be packaged into a small runtime image. The picocli library was used for the command line interface.
Please use this as a starting point for your owm Java Gradle Modularity projects or a text generator for some cool application.
Contributions, for instance more languages, are welcome.
Have Fun!
After installing the program, run it on the command line. It will output the current time. Here is a Linux BASH example:
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime
Es isch fascht Mittag
Or specify the time to say:
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime 13:15
Es isch viertel ab eis
If you don't understand Swiss German as spoken in Zürich:
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime --locale=en
It is almost quarter to eleven
On Linux you can install the program cowsay which puts the text you supply into a comic style text buble. Here's how to let the cow say the time:
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime 13:15 | cowsay
________________________
< Es isch viertel ab eis >
------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Cowsay has many options. Check them with man cowsay For instance, to have a
stoned cow say the time:
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime 13:15 | cowsay -s
________________________
< Es isch viertel ab eis >
------------------------
\ ^__^
\ (**)\_______
(__)\ )\/\
U ||----w |
|| ||
In the above examples I used the shells pipelining feature to send the Standard Output
of the SayTheTime command to the next command (cowsay) as Standard Input. The pipe
character (|) did that for us.
Alternatively we can also pass the time from SayTheTime as an argument to
the cowsay program. We can ask the shell to run a command for us with the
$() syntax (or backtick, which I find less readable) and then take
the output of that command and pass it as an argument
to the cowsay program. Here with the ghostbusters 'cow':
user@computer:~> cowsay -f ghostbusters $(/richi/Src/SayTheTime/build/image/bin/SayTheTime --locale=en 00:00)
________________
< It is midnight >
----------------
\
\
\ __---__
_- /--______
__--( / \ )XXXXXXXXXXX\v.
.-XXX( O O )XXXXXXXXXXXXXXX-
/XXX( U ) XXXXXXX\
/XXXXX( )--_ XXXXXXXXXXX\
/XXXXX/ ( O ) XXXXXX \XXXXX\
XXXXX/ / XXXXXX \__ \XXXXX
XXXXXX__/ XXXXXX \__---->
---___ XXX__/ XXXXXX \__ /
\- --__/ ___/\ XXXXXX / ___--/=
\-\ ___/ XXXXXX '--- XXXXXX
\-\/XXX\ XXXXXX /XXXXX
\XXXXXXXXX \ /XXXXX/
\XXXXXX > _/XXXXX/
\XXXXX--__/ __-- XXXX/
-XXXXXXXX--------------- XXXXXX-
\XXXXXXXXXXXXXXXXXXXXXXXXXX/
""VXXXXXXXXXXXXXXXXXXV""
figlet is a Linux program that prints text in large letters made up of ordinary characters. Use it to say the time:
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime 13:15 | figlet -w200
_____ _ _ _ _ _ _ _
| ____|___ (_)___ ___| |__ __ _(_) ___ _ __| |_ ___| | __ _| |__ ___(_)___
| _| / __| | / __|/ __| '_ \ \ \ / / |/ _ \ '__| __/ _ \ | / _` | '_ \ / _ \ / __|
| |___\__ \ | \__ \ (__| | | | \ V /| | __/ | | || __/ | | (_| | |_) | | __/ \__ \
|_____|___/ |_|___/\___|_| |_| \_/ |_|\___|_| \__\___|_| \__,_|_.__/ \___|_|___/
figlet comes with a set of fonts. To try each of them out use the -f option. Here is a script I asked DeepSeek to create for me that does just that:
for font in /usr/share/figlet/*.flf; do font_name=$(basename "$font" .flf); echo "Using font: $font_name"; richi/Src/SayTheTime/build/image/bin/SayTheTime 13:15 | figlet -w200 -f "$font_name"; echo -e "\n----------------------------------------\n"; donetoilet is another Linux program that prints text in large letters made up of ordinary characters.
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime 13:15 | toilet -w 200 ;/richi/Src/SayTheTime/build/image/bin/SayTheTime 13:15 | toilet -w 200 -F flop
mmmmmm " # " m ""# # "
# mmm mmm mmm mmm # mm m m mmm mmm m mm mm#mm mmm # mmm #mmm mmm mmm mmm
#mmmmm # " # # " #" " #" # "m m" # #" # #" " # #" # # " # #" "# #" # # # "
# """m # """m # # # #m# # #"""" # # #"""" # m"""# # # #"""" # """m
#mmmmm "mmm" mm#mm "mmm" "#mm" # # # mm#mm "#mm" # "mm "#mm" "mm "mm"# ##m#" "#mm" mm#mm "mmm"
#ɯɯɯɯɯ „ɯɯɯ„ ɯɯ#ɯɯ „ɯɯɯ„ „#ɯɯ„ # # # ɯɯ#ɯɯ „#ɯɯ„ # „ɯɯ „#ɯɯ„ „ɯɯ „ɯɯ„# ##ɯ#„ „#ɯɯ„ ɯɯ#ɯɯ „ɯɯɯ„
# „„„ɯ # „„„ɯ # # # #ɯ# # #„„„„ # # #„„„„ # ɯ„„„# # # #„„„„ # „„„ɯ
#ɯɯɯɯɯ # „ # # „ #„ „ #„ # „ɯ ɯ„ # #„ # #„ „ # #„ # # „ # #„ „# #„ # # # „
# ɯɯɯ ɯɯɯ ɯɯɯ ɯɯɯ # ɯɯ ɯ ɯ ɯɯɯ ɯɯɯ ɯ ɯɯ ɯɯ#ɯɯ ɯɯɯ # ɯɯɯ #ɯɯɯ ɯɯɯ ɯɯɯ ɯɯɯ
ɯɯɯɯɯɯ „ # „ ɯ „„# # „
Toilet has nice color options which don't show up here. Check them with `toilet -F gay' or 'toilet -F metal' for instance.
Here is with a border:
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime 13:15 | toilet -w 200 -F border
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ mmmmmm " # " m ""# # " │
│ # mmm mmm mmm mmm # mm m m mmm mmm m mm mm#mm mmm # mmm #mmm mmm mmm mmm │
│ #mmmmm # " # # " #" " #" # "m m" # #" # #" " # #" # # " # #" "# #" # # # " │
│ # """m # """m # # # #m# # #"""" # # #"""" # m"""# # # #"""" # """m │
│ #mmmmm "mmm" mm#mm "mmm" "#mm" # # # mm#mm "#mm" # "mm "#mm" "mm "mm"# ##m#" "#mm" mm#mm "mmm" │
│ │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Having admired the beautiful Qlocktwo wall clocks made by Biegert & Funk (www.qlocktwo.com ) I felt like doing something similar with words.
Side note: check out this cool TCL implementation of the Qlocktwo https://wiki.tcl-lang.org/page/QLOCKTWO+in+Tcl
If we had a "superphrase" that had all the words in the right order that are possible for the time phrases then we could show the words that make up the time in a highlight color and those that are not in the phrase in a normal text color. Here an example for 13:14 and 13:15:
The SayTheTime command line program has some options that you can pass into the command that affect the output. The -h option shows all available options:
user@computer:~> /richi/Src/SayTheTime/build/image/bin/SayTheTime -h
sage: SayTheTime [-ahnorsVw] [-c=<wrapCol>] [--highlightcolor=<color>]
[--locale=<locale>] [<suppliedTime>]
Outputs the time in words on STDOUT
[<suppliedTime>] The time in 24h notation to say. I.e. 13:15
-a, --all Show all time texts on STDOUT. Can be used in
combination with -w or --wordhighlight
-c, --wrap-col=<wrapCol> Which column to wrap at in wrap mode. Default is 50
-h, --help Show this help message and exit.
--highlightcolor=<color>
Highlight Color, if provided, must be one of: RED,
GREEN, BLUE, YELLOW, CYAN, PURPLE, WHITE.
Default is RED
--locale=<locale> Locale of the language to use, if provided, must
be one of: de_CHZH, en. Default is de_CHZH
-n, --no-wrap Do not wrap the lines
-o, --no-clear Do not clear the screen in wrap mode
-r, --run Run forever and refresh every 10 seconds
-s, --superphrase Show superphrase. This is the union all merge of
all time phrases that are possible
-V, --version Print version information and exit.
-w, --wordhighlight Word highlight mode
If we turn on -w or --wordhighlight mode a few things happen:
- The superphrase is printed with the words highlighted.
- The terminal screen is cleared so that the text prints at the top of the window.
- The text is wrapped and block adjusted to 50 columns
For the above example I added the --no-clear flag so that we can show
two times underneath each other.
If we use the --run option we get a clock in the terminal window that
refreshes itself every minute until you stop it with ctrl-c:
user@computer:~> `/richi/Src/SayTheTime/build/image/bin/SayTheTime --run --wordhighlightTo make this work we need a "superphrase". You can show the raw superphrase with the `--superphrase option:
user@computer:~> `/richi/Src/SayTheTime/build/image/bin/SayTheTime --superphrase
Es isch fascht foif zäh viertel zwänzg foif über halbi füfezwänzg zwänzg viertel zäh foif vor ab Mitternacht eis zwei drü vieri foifi sächsi sibni achti nüni zäni elfi zwölfi Mittag am Morge Abig gsiBut, how did this phrase come about? First we need to know all the possible time phrases
that we could encounter. The --all option gives us that:
user@computer:~> `/richi/Src/SayTheTime/build/image/bin/SayTheTime --all
Es isch Mitternacht
Es isch Mitternacht gsi
Es isch Mitternacht gsi
Es isch fascht foif ab Mitternacht
Es isch fascht foif ab Mitternacht
Es isch foif ab Mitternacht
Es isch foif ab Mitternacht gsi
[...]
Es isch fascht halbi elfi am Abig
Es isch halbi elfi am Abig
Es isch halbi elfi am Abig gsi
Es isch halbi elfi am Abig gsi
Es isch fascht foif über halbi elfi am Abig
Es isch fascht foif über halbi elfi am Abig
Es isch foif über halbi elfi am Abig
Es isch foif über halbi elfi am Abig gsi
[...]
Es isch foif vor zwölfi am Abig
Es isch foif vor zwölfi am Abig gsi
Es isch foif vor zwölfi am Abig gsi
Es isch fascht Mitternacht
Es isch fascht MitternachtTo come up with the superphrase we need to merge the lines and remove the words.
Merging the phrases is straight forward. Number the words and copy the words from each index into the superphrase:
Es[A0] isch[A1] Mitternacht[A2] gsi[A3]
Es[B0] isch[B1] fascht[B2] foif[B3] ab[B4] Mitternacht[B5]
--> Merge
Es[A0] Es[B0] isch[A1] isch[B1] Mitternacht[A2] fascht[B2] gsi[A3] foif[B3] ab[B4] Mitternacht[B5]
This gives us a superphrase which would work for highlighting the words of each time phrase.
It just ends up being a bit long, has loads of duplications in it. I.e. the first two words are
repeated and Mitternacht shows up twice in different places. So we can try to delete words
and see if we can still fit all the time texts into the shortened phrase.
Es[0] Es[1] isch[2] isch[3] Mitternacht[4] fascht[5] gsi[6] foif[7] ab[8] Mitternacht[9]
If we try to delete word 0 from the superphrase can we still fit all phrases into the superphrase?
Es[0] isch[2] Mitternacht[4] gsi[6] --> Yes
Es[0] isch[2] fascht[5] foif[7] ab[8] Mitternacht[9] --> Yes
Thus we end up with a shorter superphrase:
Es[0] isch[1] isch[2] Mitternacht[3] fascht[4] gsi[5] foif[6] ab[7] Mitternacht[8]
Now we can try again deleting the first word. But now both time phrases fail:
Superphrase: isch[0] isch[1] Mitternacht[2] fascht[3] gsi[4] foif[5] ab[6] Mitternacht[7]
Es[not found] isch[2] Mitternacht[4] gsi[6] --> Fail
Es[not found] isch[2] fascht[5] foif[7] ab[8] Mitternacht[9] --> Fail
We conclude that the first word is nescessary and check the second word. Obviously we will discover that this word can be omitted resulting in the new superphrase:
Es[0] isch[1] Mitternacht[2] fascht[3] gsi[4] foif[5] ab[6] Mitternacht[7]
Let's see what happens if we delete the 3rd word:
Superphrase: Es[0] isch[1] fascht[2] gsi[3] foif[4] ab[5] Mitternacht[6]
Es[0] isch[1] Mitternacht[6] gsi[not found] --> Fail
Es[0] isch[1] fascht[2] foif[4] ab[5] Mitternacht[6] --> Yes
If we apply the logic to the end the shortest superphrase becomes:
Es[0] isch[1] Mitternacht[2] fascht[3] gsi[4] foif[5] ab[6] Mitternacht[7]
A human will quickly sport that we can get rid of word 2 if we move word 4 behind word 7. This is an algorithm for another day.
And the current implementation of the word delete test is quite brute-force-inefficient
and should be revisited.
Instructions need to be tested....
# First clone the project from Github:
git clone https://github.com/richardeigenmann/SayTheTime.git
cd SayTheTime
# now fire up your favorite IDE and import the project as a Gradle project and
# let it run the build for you
# or build it on the command line with Gradle (which you have to install first)
gradle build
# or use the self-contained Gradle wrapper
./gradlew build
# for Windows:
gradlew.bat buildPlease mail me if you have any questions or suggestions. richard.eigenmann@gmail.com
I have licensed this project under the MIT license. Please see the LICENSE file for details.
Please take note of the licenses of the libraries used in this project declared in the build.gradle file:
- Picocli is licensed under the Apache 2.0 license. (https://picocli.info/#_license)
- System-Lambda is licensed under the MIT license. (https://github.com/stefanbirkner/system-lambda/blob/master/LICENSE)
- JUnit is licensed under the Eclipse Public License 2.0. (https://github.com/junit-team/junit5/blob/main/LICENSE.md)
- Json is licensed under its own public license. (https://www.json.org/license.html)
- Ben Manes' Versions Plugin is licensed under the Apache 2.0 license. (https://github.com/ben-manes/gradle-versions-plugin/blob/master/LICENSE.txt)
- Beryx Runtime Plugin is licensed under the Apache 2.0 license. (https://github.com/beryx/badass-runtime-plugin/blob/master/LICENSE)
- Gradle is licensed under the Apache 2.0 license. (https://github.com/gradle/gradle/blob/master/LICENSE)

