timetracking-rs: Tracking Hours in Rust

I worked with Technology for a long time. I created a Python script 10 years ago to manage my working hours for control, observability, backpressure purposes. Last Friday night(What a nerd thing to do, I know), I re-wrote this script into a Rust program. I always like to build my own tools, for several reasons, like make my work more productive or just because I want an excuse to do something useful for me in a language that I liked. It took me about 4h to figure out how to do this in Rust. There were 2 basic challenges with was Strings(OH I hate Strings in Rust) and working effectively with Data/Time math operations. I would 2 hours figure it out and making this work and 2 other hours refactoring the code to make it better. Overall code with Rust is pretty productive and fun however Strings and pain in the ass. So I'm using this program every day (decommission my old python script) - so this might be useful for you too. Let's get started.



Show me the code

mod time_tracking;
mod model;
use crate::model::*;
use std::env;
use std::process::exit;
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 3 {
eprint!("Error! You need pass 2 arguments: Worked_hours and holidays_count.\nI.g: ./time-tracking 10 0");
exit(1);
}
let data = parse_configs(args);
println!("{}", time_tracking::hours_report(data));
}
fn parse_configs(args:Vec<String>) -> TimeTrackingData{
let worked_hours = args[1].parse::<f32>().unwrap_or(1.0);
let holidays = args[2].parse::<i32>().unwrap_or(0);
let project_name = args.get(3).unwrap_or(&String::from("Work")).to_string();
let data = TimeTrackingData {
month_data: MonthData {
holidays_count: holidays,
worked_hours: worked_hours,
},
config: WorkConfig{
project_name: project_name,
base: 200,
},
};
return data;
}
view raw main.rs hosted with ❤ by GitHub
pub struct WorkConfig{
pub base:i32,
pub project_name:String,
}
pub struct MonthData{
pub holidays_count:i32,
pub worked_hours:f32,
}
pub struct TimeTrackingData{
pub month_data:MonthData,
pub config:WorkConfig,
}
view raw model.rs hosted with ❤ by GitHub
#[path = "time_utils.rs"] mod time_utils;
#[path = "model.rs"] mod model {}
use crate::model::TimeTrackingData;
use crate::time_tracking::time_utils::{workable_days, business_days, current_month, current_year};
pub fn hours_report(data:TimeTrackingData) -> String {
let today = time_utils::today().to_string();
let mut report:String = format!("======================================\n\
TIME_TRACKING.rs by Diego Pacheco\n\
--------------------------------------
Project : {}
Goal : {} hours
Days Off : {} days
======================================\n",data.config.project_name,data.config.base,data.month_data.holidays_count);
report = format!("{} {} {}/{}/{}\n",report,"Today Is : ",today, current_month(),current_year());
report = format!("{} {} {}\n",report,"Business Days : ",time_utils::business_days());
report = format!("{} {} {}\n",report,"Worked Days : ",time_utils::worked_days());
report = format!("{} {} {}\n",report,"Remain Days : ",time_utils::workable_days() - data.month_data.holidays_count);
report = format!("{} {} {}\n",report,"Worked Hours : ",data.month_data.worked_hours);
report = format!("{}{}",report, calculate_need_to_work(data));
report = format!("{}{}",report,"-------------------------------------");
report = format!("{} {}",report,get_avg_hours_predictions());
report = format!("{}{}",report,"=====================================");
return report;
}
fn calculate_need_to_work(data:TimeTrackingData) -> String {
let mut report:String = format!("");
let total_hours = data.config.base as f32 - data.month_data.worked_hours as f32;
report = format!("{} {} {}{}\n",report,"Need to Work : ",total_hours," hours total <<< ");
let hours_to_work = data.config.base as f32 - data.month_data.worked_hours as f32;
let available_days = (workable_days() - data.month_data.holidays_count) as f32;
let avg_working_hours_yet = hours_to_work / available_days;
report = format!("{} {} {:.2}{}\n",report,"Need to Work : ",avg_working_hours_yet," avg hours yet ");
return report;
}
fn get_avg_hours_predictions() -> String {
let mut report:String = String::from("\n Hours Predictions \n");
report = format!("{} {} {} {}\n", report, "7h per day : ", business_days()*7, "h");
report = format!("{} {} {} {}\n", report, "8h per day : ", business_days()*8, "h");
report = format!("{} {} {} {}\n", report, "9h per day : ", business_days()*9, "h");
report = format!("{} {} {} {}\n", report, "10h per day : ", business_days()*10, "h");
return report;
}
use chrono::prelude::*;
use chrono::NaiveDate;
use bdays::HolidayCalendar;
pub fn current_year() -> i32 {
return Local::now().date().year();
}
pub fn current_month() -> u32 {
return Local::now().date().month();
}
pub fn today() -> u32 {
return Local::now().date().day();
}
pub fn business_days() -> i64 {
let cal = bdays::calendars::WeekendsOnly;
let year = current_year();
let month = current_month();
let mut days = 0;
for i in 1..31 {
let date = NaiveDate::from_ymd(year, month, i);
if cal.is_bday(date) {
days += 1;
}
}
return days;
}
pub fn workable_days() -> i32 {
let working_days = business_days() as i32;
let worked_days = worked_days() as i32;
return working_days - worked_days;
}
pub fn worked_days() -> u32 {
let today = today() as u32;
let cal = bdays::calendars::WeekendsOnly;
let year = current_year();
let month = current_month();
let mut days = 0;
for i in 1..31 {
let date = NaiveDate::from_ymd(year, month, i);
if cal.is_bday(date) {
days += 1;
}
if date.day()==today{
break;
}
}
return days;
}
view raw time_utils.rs hosted with ❤ by GitHub


So we have 2 Rust files here: main, time_tracking, time_utils, and model. main we have the main application which reads the configs from the main arguments vector and parses it into a model::TimeTrackingData Struct.

The model files have structs for config and time tracking math operations. time_utils use chrono and bdays in order todo the date/time math I need for the program to work. The time_tracking has the report building pretty much and calls the hours calculations.

The complete code is here: https://github.com/diegopacheco/timetracking.rs

Video


timetracking-rs from Diego Pacheco on Vimeo.


Cheers,
Diego Pacheco

Popular posts from this blog

Having fun with Zig Language

C Unit Testing with Check

Cool Retro Terminal