Compare commits

...

6 commits
v2.1.0 ... main

Author SHA1 Message Date
Campbell Alden
4a86e930f9
Update README.md 2025-04-20 22:18:34 +09:00
Campbell Alden
e2c0b08c03
Create README.md 2025-04-20 22:11:19 +09:00
Campbell Alden
51a200d1a3 Bug Fix: Count time from now if the end time is before now 2024-12-27 09:36:49 +09:00
Campbell Alden
2b55df907b Count end time from the last time if the last time is in the future 2024-12-26 18:47:35 +09:00
Campbell Alden
9087e69c79 Simplify the now string 2024-12-23 16:59:09 +09:00
Campbell Alden
b6d1c0ea0c Change the default behavior for hours to 0 if discount is set 2024-12-13 18:22:22 +09:00
6 changed files with 70 additions and 18 deletions

2
Cargo.lock generated
View file

@ -384,7 +384,7 @@ dependencies = [
[[package]]
name = "time-track"
version = "2.1.0"
version = "2.1.4"
dependencies = [
"anyhow",
"chrono",

View file

@ -1,6 +1,6 @@
[package]
name = "time-track"
version = "2.1.0"
version = "2.1.4"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

22
README.md Normal file
View file

@ -0,0 +1,22 @@
# time-track
A simple CLI tool for tracking how much time you have left to work. I find myself stressing about whether I'm hitting 8 real hours, so this little tool helps me avoid wasting
time calculating when my work day will end.
Simply enter times, one per line and send an EOF character when you're done. The first lines opens a span of work and the next line closes it so that you can build up working
time be clocking in and out. Finally, you can send additional arguments to the program to configure how long you intend to work (the default is 8 hours).
```
time-track
Working for 8 hours
Input times one per line. Send an EOF character to finish inputting...
8:30
9:30
11:15
12:30
13:16
18:20
19:30
20:11 # Send an EOF
Exactly done
```

View file

@ -5,8 +5,9 @@ use clap::Parser;
#[command(author, version, about, long_about = None)]
pub struct Args {
/// How many hours you intend to work (sums with `minutes`)
#[arg(long, default_value_t = 8)]
pub hours: i64,
/// Usually defaults to 8 hours, but if discount is set then it defaults to 0
#[arg(long)]
pub hours: Option<i64>,
/// How many minutes you intend to work (sums with `hours`)
#[arg(long, default_value_t = 0)]

View file

@ -11,6 +11,21 @@ use args::Args;
fn main() -> Result<()> {
let args = Args::parse();
let stdin = io::stdin();
let hours = args.hours.unwrap_or(
if args.discount {
0
} else {
8
}
);
let target_minutes = if args.discount {
(8 * 60) - ((hours * 60) + args.minutes)
} else {
hours * 60 + args.minutes
};
let (hrs, mins) = time::to_hrs_minutes(target_minutes);
println!("Working for {}", time::show_time(hrs, mins));
println!("Input times one per line. Send an EOF character to finish inputting...");
let mut lines: Vec<String> = vec![];
for line in stdin.lock().lines() {
@ -24,6 +39,7 @@ fn main() -> Result<()> {
let mut total_minutes: i64 = 0;
let mut first: Option<DateTime<Local>> = None;
let mut last: Option<DateTime<Local>> = None;
for time in times {
match first {
None => {
@ -31,22 +47,19 @@ fn main() -> Result<()> {
},
Some(prev) => {
total_minutes += (time - prev).num_minutes();
first = None
first = None;
last = Some(time);
}
}
}
if let Some(remaining) = first {
let now = Local::now();
println!("Ended with unclosed span... assuming ending now: {}", now.time());
let now_str = now.format("%-I:%M %p");
println!("Ended with unclosed span... assuming ending now: {}", now_str);
total_minutes += (now - remaining).num_minutes();
}
let target_minutes = if args.discount {
(8 * 60) - (args.hours * 60 + args.minutes)
} else {
args.hours * 60 + args.minutes
};
println!("{}", time::get_charaterized_time_remaining(total_minutes, target_minutes));
println!("{}", time::get_charaterized_time_remaining(total_minutes, target_minutes, last.unwrap_or_else(|| Local::now())));
Ok(())
}

View file

@ -27,7 +27,7 @@ pub fn from_stream<'a>(reference_date: &DateTime<Local>, stream: impl Iterator<I
return Ok(durations);
}
fn show_time(hours: i64, minutes: i64) -> String {
pub fn show_time(hours: i64, minutes: i64) -> String {
let pluralized_hours = match hours {
1 => "1 hour".to_string(),
_ => format!("{hours} hours"),
@ -48,13 +48,17 @@ fn show_time(hours: i64, minutes: i64) -> String {
return format!("{pluralized_hours} and {pluralized_minutes}");
}
fn to_hrs_minutes(total_minutes: i64) -> (i64, i64) {
pub fn to_hrs_minutes(total_minutes: i64) -> (i64, i64) {
let minutes = total_minutes % 60;
let hours = total_minutes / 60;
(hours, minutes)
}
pub fn get_charaterized_time_remaining(total_minutes: i64, target_minutes: i64) -> String {
pub fn get_charaterized_time_remaining(
total_minutes: i64,
target_minutes: i64,
ended_at: DateTime<Local>,
) -> String {
if total_minutes == target_minutes {
return "Exactly done".to_string();
}
@ -66,8 +70,20 @@ pub fn get_charaterized_time_remaining(total_minutes: i64, target_minutes: i64)
} else {
let diff = target_minutes - total_minutes;
let (hours, minutes) = to_hrs_minutes(diff);
let end_at = (Local::now() + Duration::minutes(diff)).time();
let now = Local::now();
return if ended_at > now {
let end_at = (ended_at + Duration::minutes(diff)).time();
let end_str = end_at.format("%-I:%M %p");
return format!("You have {} remaining (end at {} starting now)", show_time(hours, minutes), end_str)
format!(
"You have {} remaining (end at {} starting from {})",
show_time(hours, minutes),
end_str,
ended_at.format("%-I:%M %p"),
)
} else {
let end_at = (now + Duration::minutes(diff)).time();
let end_str = end_at.format("%-I:%M %p");
format!("You have {} remaining (end at {} starting now)", show_time(hours, minutes), end_str)
}
}
}