DEV Community

Cover image for Tutorial: Build a Digital Clock in Rust 🦀⏰
Eleftheria Batsou
Eleftheria Batsou

Posted on • Originally published at eleftheriabatsou.hashnode.dev on

Tutorial: Build a Digital Clock in Rust 🦀⏰

Hello, amazing people and welcome back to my blog! Today we're going to build a digital clock in Rust. If you're following my tutorials you will notice that this one is a bit simpler and I'd suggest it to juniors.

It's going to be a quick and fun one.

Introduction to Time in Rust

For this project, we will have some digital numbers. I did a little Wikipedia search to create these numbers (graphics characters). These characters existed long before Unicode existed and were available on all of the terminals. These graphic characters are useful for drawing various things, and these days the Unicode character set has an enormous number of things. The ones I'm going to be using today are used mostly for building forms and what I've done is taken those graphics characters and I've built them into a little array:

(feel free to copy-paste)

// https://www.w3.org/TR/xml-entity-names/025.html
const DIGITS : [[&str; 11]; 7] = [
    ["┏━┓ ","  ╻  "," ┏━┓ ", " ┏━┓ "," ╻ ╻ "," ┏━┓ "," ┏   "," ┏━┓ "," ┏━┓ "," ┏━┓ ","   "],
    ["┃ ┃ ","  ┃  ","   ┃ ", "   ┃ "," ┃ ┃ "," ┃   "," ┃   ","   ┃ "," ┃ ┃ "," ┃ ┃ "," ╻ "],
    ["┃ ┃ ","  ┃  ","   ┃ ", "   ┃ "," ┃ ┃ "," ┃   "," ┃   ","   ┃ "," ┃ ┃ "," ┃ ┃ ","   "],
    ["┃ ┃ ","  ┃  "," ┏━┛ ", " ┣━┫ "," ┗━┫ "," ┗━┓ "," ┣━┓ ","   ┃ "," ┣━┫ "," ┗━┫ ","   "],
    ["┃ ┃ ","  ┃  "," ┃   ", "   ┃ ","   ┃ ","   ┃ "," ┃ ┃ ","   ┃ "," ┃ ┃ ","   ┃ ","   "],
    ["┃ ┃ ","  ┃  "," ┃   ", "   ┃ ","   ┃ ","   ┃ "," ┃ ┃ ","   ┃ "," ┃ ┃ ","   ┃ "," ╹ "],
    ["┗━┛ ","  ╹  "," ┗━━ ", " ┗━┛ ","   ╹ "," ┗━┛ "," ┗━┛ ","   ╹ "," ┗━┛ "," ┗━┛ ","   "],
];
Enter fullscreen mode Exit fullscreen mode

These graphics characters allow us to do little corners and this is how we can create 11 characters in a sort of graphical character set. Each row of this table consists of 11 entries each of which contains a string slice. This structure enables us to print exactly what we want. Let's move on...

How can we tell the time in Rust? There is a crate called Chrono in Rust, in particular, we want date time in Chrono. This is a structure that allows us to represent time in various ways. In my example, I'm going to use local time which gives the time on my local time of my computer. An alternative is to use UTC time.

So, to answer my initial question, 'How can we tell the time in Rust?', let's use date time to tell the time!

Dependencies

The first thing we need to do is to put a dependency into our cargo.toml.

[dependencies]
chrono = "0.4"
Enter fullscreen mode Exit fullscreen mode

Main

If I want to get the local time I can do: use chrono::{Local};

and inside the main: let t = Local::now();

If I want to format it better, I can do: let time = t.format("%H:%M:%S").to_string();

(that way I'll show the hour, minutes and seconds.)

Now we want our clock to run forever so'll put it in a infinite loop. The loop enables us to keep running the same code.

If you run this code you get every second it changes. (We don't want that...!)

loop {
        let t = Local::now();
        let time = t.format("%H:%M:%S").to_string();
        println!("{:?}", time);
}
Enter fullscreen mode Exit fullscreen mode

To improve it, we can "sleep" the thread like so: std::thread::sleep(std::time::Duration::from_millis(999));

(we're going to do a few milliseconds.)

If it is a thousand milliseconds we'd get the answer every second.

How can we print the digits we created above using using thetimestring?

Let's convert time string into some nice digits to make a digital clock. I can start by iterating over my digits. (I'll do that for every row). Then I need to actually call the digits. Essentially we're building a line printer:

    loop {
        let t = Local::now();
        let time = t.format("%H:%M:%S").to_string();
        for row in &DIGITS {
            .
            .
            .
            .
        }
        std::thread::sleep(std::time::Duration::from_millis(999));
        print!("\x1b[7A");
    }
Enter fullscreen mode Exit fullscreen mode

The next thing I need to do is iterate over the characters in my time string. I want to iterate as characters:


        for row in &DIGITS {
            for c in time.chars() {
                let col = match c {
                    .
                    .
                };
                .
            }
            .
        }
Enter fullscreen mode Exit fullscreen mode

I also need to translate those characters in a matchC , this essentially matches a pattern and does something depending on what the pattern is. In this particular case anything between the 0 and 9, these are contiguous digits in ASCII and Unicode.

let col = match c {
        '0'..='9' => c as usize - '0' as usize,
         _ => 10,
 };
Enter fullscreen mode Exit fullscreen mode

What we're going to do now is we're going to turn that c into an index. Let's do the digits first:

'0'..='9' => c as usize - '0' as usize, _ => 10,

  • usize is a special type that we use for indexing arrays.

  • - '0' as usize will give us an integer.

  • You'll notice I did ..= this will include the digit 9 at the end. Otherwise, it won't be printing any 9.

  • Then, the next one is the _. We'll just map that to 10 which is our end.

This match statement let col = match c will give us either a number between 0 and 9 or 10 depending on which digit we want.

Finally, I'm going to print the column from my row: print!("{} ", row[col]); this will print the corresponding row of my character. Then at the end of each row, I want to just use print alone with no arguments, and it'll print a blank line, otherwise, all my digits are going to run into each other println!();.

Woohoo! We now have a working program! 🔥

From now on, we can only improve it! How can we make this prettier? There are a few useful things we can do but I'm not going into details about them, instead, I'm going to show you the whole code below and if you have any questions feel free to ask me.

fn main() {
    print!("\x1b[2J");
    print!("\x1b[?25l");
    loop {
        let t = Local::now();
        let time = t.format("%H:%M:%S").to_string();
        for row in &DIGITS {
            for c in time.chars() {
                let col = match c {
                    '0'..='9' => c as usize - '0' as usize,
                    _ => 10,
                };
                print!("{} ", row[col]);
            }
            println!();
        }
        std::thread::sleep(std::time::Duration::from_millis(999));
        print!("\x1b[7A");
    }
}
Enter fullscreen mode Exit fullscreen mode

Please keep in mind there are many other ways to build a digital clock in Rust, I only did this for learning purposes.

Code on GitHub.

Digital Clock in Rust

using the Date time crate

to run it: cargo run

timeDigitalClock







👋 Hello, I'm Eleftheria, Community Manager, developer, public speaker, and content creator.

🥰 If you liked this article, consider sharing it.

🔗 All links | X | LinkedIn

Top comments (1)

Collapse
 
lexlohr profile image
Alex Lohr

One thing to consider when creating a clock is that the update may take a few milliseconds itself, so sleeping for an arbitrary number of milliseconds can get you over the second limit so that you jump over a second.

While this is a minor issue, there's also a simple a solution: take the current time into account to jump into the next second.