Provided timezone mapping and included origins of this program
This commit is contained in:
parent
0fe7608e87
commit
f6ccac9d2a
54
README.md
54
README.md
@ -3,6 +3,7 @@
|
||||
tblancher's date-tally program. Take's input from file or stdin, in the following log format:
|
||||
```
|
||||
tag:date
|
||||
tag:date
|
||||
```
|
||||
Where `date` is the output of the Linux or macOS `date` program. E.g.:
|
||||
|
||||
@ -15,6 +16,57 @@ my_tag:Fri Nov 11 13:02:47 EST 2022
|
||||
```
|
||||
my_tag: 0 weeks, 2 days, 0 hours, 11 minutes, 15 seconds
|
||||
```
|
||||
## Origins
|
||||
This was originally designed so I could track how often I'd replace the AAA battery cells in my old Microsoft Bluetooth Notebook mouse, from 2009 or 2010. The first tag is silently ignored. Whenever I'd replace the batteries, I'd add another line to the log, with the tag being the make and model of the batteries I was replacing, and the current timestamp of when the batteries were replaced. E.g.:
|
||||
```
|
||||
echo "Duracell:$(date)" >> ~/mouse/batteries.txt
|
||||
```
|
||||
This way I could track the performance of various brands of batteries, gauging approximately how long they lasted in this mouse. For example, this is my current `~/mouse/batteris.txt` for my work MacBook Pro:
|
||||
|
||||
```
|
||||
Amazon Basics:Sat Jan 9 08:35:55 EST 2021
|
||||
Amazon Basics:Thu Feb 11 16:54:30 EST 2021
|
||||
Amazon Basics:Mon Mar 22 09:13:37 EDT 2021
|
||||
Energizer Max:Sat Apr 24 05:55:41 EDT 2021
|
||||
Amazon Basics:Wed Jun 2 14:03:27 EDT 2021
|
||||
Amazon Basics:Tue Jul 6 12:43:46 EDT 2021
|
||||
Energizer Max:Mon Aug 9 10:43:42 EDT 2021
|
||||
Duracell:Wed Sep 15 08:30:52 EDT 2021
|
||||
Duracell:Thu Oct 21 18:16:37 EDT 2021
|
||||
Duracell:Tue Nov 16 07:12:13 EST 2021
|
||||
Duracell:Thu Dec 16 10:10:45 EST 2021
|
||||
Duracell:Fri Jan 28 11:36:11 EST 2022
|
||||
Energizer Ultimate Lithium:Tue Mar 22 14:58:08 EDT 2022
|
||||
Energizer Ultimate Lithium:Wed May 11 08:46:17 EDT 2022
|
||||
Energizer Ultimate Lithium:Fri Jul 29 14:57:51 EDT 2022
|
||||
Energizer Ultimate Lithium:Tue Sep 20 10:14:21 EDT 2022
|
||||
Energizer Ultimate Lithium:Tue Nov 8 18:05:24 EST 2022
|
||||
```
|
||||
This produces the following report:
|
||||
```
|
||||
Amazon Basics: 4 weeks, 5 days, 8 hours, 18 minutes, 35 seconds
|
||||
Amazon Basics: 5 weeks, 3 days, 15 hours, 19 minutes, 7 seconds
|
||||
Energizer Max: 4 weeks, 4 days, 20 hours, 42 minutes, 4 seconds
|
||||
Amazon Basics: 5 weeks, 4 days, 8 hours, 7 minutes, 46 seconds
|
||||
Amazon Basics: 4 weeks, 5 days, 22 hours, 40 minutes, 19 seconds
|
||||
Energizer Max: 4 weeks, 5 days, 21 hours, 59 minutes, 56 seconds
|
||||
Duracell: 5 weeks, 1 days, 21 hours, 47 minutes, 10 seconds
|
||||
Duracell: 5 weeks, 1 days, 9 hours, 45 minutes, 45 seconds
|
||||
Duracell: 3 weeks, 4 days, 13 hours, 55 minutes, 36 seconds
|
||||
Duracell: 4 weeks, 2 days, 2 hours, 58 minutes, 32 seconds
|
||||
Duracell: 6 weeks, 1 days, 1 hours, 25 minutes, 26 seconds
|
||||
Energizer Ultimate Lithium: 7 weeks, 4 days, 2 hours, 21 minutes, 57 seconds
|
||||
Energizer Ultimate Lithium: 7 weeks, 0 days, 17 hours, 48 minutes, 9 seconds
|
||||
Energizer Ultimate Lithium: 11 weeks, 2 days, 6 hours, 11 minutes, 34 seconds
|
||||
Energizer Ultimate Lithium: 7 weeks, 3 days, 19 hours, 16 minutes, 30 seconds
|
||||
Energizer Ultimate Lithium: 7 weeks, 0 days, 8 hours, 51 minutes, 3 seconds
|
||||
```
|
||||
This was originally a shell script, that I converted to Python sometime in 2018. I finished the conversion to Rust in November 2022. Also in 2018, I added the tag, so I could keep track of which brand and type of batteries I was using. I do have logs from 2018 and 2019, when I was still in the office (so my employer supplied the batteries). These batteries (Energizer Industrial and Duracell ProCell) have had the best performance, 12 and 14 weeks, respectively. With consumer level batteries, the best I've been able to get out of this mouse has been seven weeks, which has always been a disappointment. The outlier for Energizer Ultimate Lithium was merely due to lack of use, I was on parental leave so did not touch this mouse for six weeks.
|
||||
|
||||
Again, this is for my old Microsoft Bluetooth Notebook mouse which is more than a decade old. I hardly ever turn this mouse off, so that may explain the relatively short life of these batteries. I'm actually considering purchasing a new mouse, which hopefully doesn't chew through batteries as fast. I kept this mouse for so long because I like its physical form factor, it's small and fits nicely in my hand. Also, I hate wired mice (the wire/tail just gets in my way), so the wireless aspect is a requirement for me. I did have an RF mouse a few years ago where I never changed its single AA cell, but that needed the USB RF dongle for it to work.
|
||||
|
||||
## Note
|
||||
Due to limitations of Rust's strftime implementation, the string representation of the timezone (in the example above, "EST") is ignored. This program currently assumes the timezone is US Eastern Time (`America/New_York`). If the log contains other timezones, we'd have to add functionality to convert the string timezone representation (with its ambiguities) to a fixed offset representation. Such changes are beyond the scope of this at the current time.
|
||||
Due to limitations of Rust's strftime implementation, the string representation of the timezone (in the example above, "EST") is ignored. This program currently has a limited set of timezone abbreviations it supports. If the log contains other timezones not explicitly defined in the `match timezone {}` block in the main program loop (`for line in reader.lines(){}`), the program will panic. We'd need to include an explicit mapping for the desired timezone.
|
||||
|
||||
Again, this timezone abbreviation to actual timezone mapping is not intended to be exhaustive, as there are inherent ambiguities in the timezone abbreviations. E.g., "CDT" could mean Central Daylight Time (US), or China Daylight Time. This match codes handles the ambiguity rather arbitrarily, consider changing the mapping to suit your needs.
|
||||
|
||||
|
40
src/main.rs
40
src/main.rs
@ -3,7 +3,7 @@ use std::fs;
|
||||
use std::io::{self, BufReader, BufRead};
|
||||
//use regex::Regex;
|
||||
use chrono::{Duration, DateTime, FixedOffset, NaiveDateTime, TimeZone, Utc};
|
||||
use chrono_tz::America::New_York;
|
||||
use chrono_tz::Tz;
|
||||
|
||||
fn main() {
|
||||
// Regexes
|
||||
@ -16,24 +16,42 @@ fn main() {
|
||||
Some(filename) => Box::new(BufReader::new(fs::File::open(filename).unwrap()))
|
||||
};
|
||||
|
||||
// set vacuous values for old and new timestamps, so we can detect the first and second lines
|
||||
// of input
|
||||
let mut old: DateTime<FixedOffset> = DateTime::from(DateTime::<Utc>::MIN_UTC);
|
||||
let mut new: DateTime<FixedOffset> = DateTime::from(DateTime::<Utc>::MIN_UTC);
|
||||
|
||||
for line in reader.lines() {
|
||||
let l: String = line.unwrap();
|
||||
let (item, timestamp) = l.split_once(':').unwrap();
|
||||
let (tag, timestamp) = l.split_once(':').unwrap();
|
||||
// Timezone mapping
|
||||
let ts: Vec<&str> = timestamp.split_whitespace().collect();
|
||||
let timezone = ts[4];
|
||||
let tz: Tz = match timezone {
|
||||
"EDT" => "America/New_York".parse().unwrap(),
|
||||
"EST" => "America/New_York".parse().unwrap(),
|
||||
"CDT" => "America/Chicago".parse().unwrap(),
|
||||
"CST" => "America/Chicago".parse().unwrap(),
|
||||
"MDT" => "America/Denver".parse().unwrap(),
|
||||
"MST" => "America/Denver".parse().unwrap(),
|
||||
"PDT" => "America/Los_Angeles".parse().unwrap(),
|
||||
"PST" => "America/Los_Angeles".parse().unwrap(),
|
||||
"UTC" => "UTC".parse().unwrap(),
|
||||
"GMT" => "GMT".parse().unwrap(),
|
||||
other => panic!("Timezone '{}' unknown!", other)
|
||||
};
|
||||
|
||||
let ndt = NaiveDateTime::parse_from_str(×tamp, "%a %b %e %H:%M:%S %Z %Y").unwrap();
|
||||
let dt = New_York.from_local_datetime(&ndt).unwrap();
|
||||
let ts: DateTime<FixedOffset> = DateTime::parse_from_rfc3339(&dt.to_rfc3339()).unwrap();
|
||||
if old == DateTime::<Utc>::MIN_UTC {
|
||||
old = ts;
|
||||
let dt = tz.from_local_datetime(&ndt).unwrap();
|
||||
let to: DateTime<FixedOffset> = DateTime::parse_from_rfc3339(&dt.to_rfc3339()).unwrap();
|
||||
if old == DateTime::<Utc>::MIN_UTC { // detect first line
|
||||
old = to;
|
||||
continue;
|
||||
} else if new == DateTime::<Utc>::MIN_UTC {
|
||||
new = ts;
|
||||
} else {
|
||||
} else if new == DateTime::<Utc>::MIN_UTC { // detect second line
|
||||
new = to;
|
||||
} else { // subsequent lines
|
||||
old = new;
|
||||
new = ts;
|
||||
new = to;
|
||||
};
|
||||
|
||||
let dur: Duration = new - old;
|
||||
@ -46,6 +64,6 @@ fn main() {
|
||||
- days * 24 * 60 * 60 - hours * 60 * 60 - minutes * 60;
|
||||
|
||||
println!("{}: {} weeks, {} days, {} hours, {} minutes, {} seconds",
|
||||
item, weeks, days, hours, minutes, seconds);
|
||||
tag, weeks, days, hours, minutes, seconds);
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user