Skip to content

Commit

Permalink
Add a duration output and release as 1.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
jrduncans committed Aug 17, 2020
1 parent 43a199a commit 4f4b605
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 45 deletions.
39 changes: 23 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "timeturner"
version = "1.3.2"
version = "1.4.0"
authors = ["Stephen Duncan <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"
Expand All @@ -20,3 +20,4 @@ chrono-tz = "0.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono-english = "0.1.4"
humantime = "2.0.1"
2 changes: 2 additions & 0 deletions src/alfred.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ pub fn output_json(conversion_results: &[ConversionResult]) -> String {
ConversionFormat::Rfc3339Utc => String::from("rfc3339_utc"),
ConversionFormat::Rfc3339Local => String::from("rfc3339_local"),
ConversionFormat::EpochMillis => String::from("epoch_millis"),
ConversionFormat::DurationSince => String::from("duration_since"),
},
title: conversion_result.converted_text.clone(),
subtitle: match conversion_result.format {
ConversionFormat::Rfc3339Utc => String::from("RFC3339 - UTC"),
ConversionFormat::Rfc3339Local => String::from("RFC3339 - Local"),
ConversionFormat::EpochMillis => String::from("Epoch Millis"),
ConversionFormat::DurationSince => String::from("Duration"),
},
arg: conversion_result.converted_text.clone(),
})
Expand Down
99 changes: 78 additions & 21 deletions src/converting.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use super::parsing::DateTimeFormat;
use crate::parsing::ParsedInput;
use chrono::prelude::*;
use humantime::format_duration;
use std::convert::TryInto;
use std::time::Duration;

#[derive(PartialEq, Debug)]
pub enum ConversionFormat {
Rfc3339Utc,
Rfc3339Local,
EpochMillis,
DurationSince,
}

#[derive(PartialEq, Debug)]
Expand All @@ -15,7 +19,7 @@ pub struct ConversionResult {
pub format: ConversionFormat,
}

pub fn convert(parsed_input: &ParsedInput) -> Vec<ConversionResult> {
pub fn convert(parsed_input: &ParsedInput, now: DateTime<Utc>) -> Vec<ConversionResult> {
let mut results = Vec::new();

if parsed_input.input_zone != Some(FixedOffset::west(0)) {
Expand Down Expand Up @@ -44,9 +48,30 @@ pub fn convert(parsed_input: &ParsedInput) -> Vec<ConversionResult> {
});
}

results.push(ConversionResult {
converted_text: duration_since(parsed_input.value, now),
format: ConversionFormat::DurationSince,
});

results
}

pub fn duration_since(input: DateTime<Utc>, now: DateTime<Utc>) -> String {
let difference_millis = now.timestamp_millis() - input.timestamp_millis();

let in_future = difference_millis.is_negative();
let difference_millis = difference_millis.abs();

let duration = Duration::from_millis(difference_millis.try_into().unwrap());
let duration_format = format_duration(duration);

if in_future {
format!("in {}", duration_format)
} else {
format!("{} ago", duration_format)
}
}

#[cfg(test)]
mod tests {
#![allow(clippy::unreadable_literal)]
Expand All @@ -55,12 +80,16 @@ mod tests {

#[test]
fn missing_input() {
let now = Utc.timestamp_millis(1572303922748);
let date = Utc.timestamp_millis(1572213799747);
let result = convert(&ParsedInput {
input_format: DateTimeFormat::Missing,
input_zone: None,
value: date,
});
let result = convert(
&ParsedInput {
input_format: DateTimeFormat::Missing,
input_zone: None,
value: date,
},
now,
);

// Should include all output formats
assert_eq!(
Expand All @@ -79,19 +108,27 @@ mod tests {
ConversionResult {
converted_text: String::from("1572213799747"),
format: ConversionFormat::EpochMillis
},
ConversionResult {
converted_text: String::from("1day 1h 2m 3s 1ms ago"),
format: ConversionFormat::DurationSince
}
]
);
}

#[test]
fn epoch_millis_input() {
let now = Utc.timestamp_millis(1572123676746);
let date = Utc.timestamp_millis(1572213799747);
let result = convert(&ParsedInput {
input_format: DateTimeFormat::EpochMillis,
input_zone: None,
value: date,
});
let result = convert(
&ParsedInput {
input_format: DateTimeFormat::EpochMillis,
input_zone: None,
value: date,
},
now,
);

// Should skip epoch-millis output format
assert_eq!(
Expand All @@ -106,19 +143,27 @@ mod tests {
.with_timezone(&Local)
.to_rfc3339_opts(SecondsFormat::Millis, true),
format: ConversionFormat::Rfc3339Local
},
ConversionResult {
converted_text: String::from("in 1day 1h 2m 3s 1ms"),
format: ConversionFormat::DurationSince
}
]
);
}

#[test]
fn rfc3339_utc() {
let now = Utc.timestamp_millis(1572213929748);
let date = Utc.timestamp_millis(1572213799747);
let result = convert(&ParsedInput {
input_format: DateTimeFormat::Rfc3339,
input_zone: Some(FixedOffset::west(0)),
value: date,
});
let result = convert(
&ParsedInput {
input_format: DateTimeFormat::Rfc3339,
input_zone: Some(FixedOffset::west(0)),
value: date,
},
now,
);

// Should skip RFC3339 in UTC
assert_eq!(
Expand All @@ -133,19 +178,27 @@ mod tests {
ConversionResult {
converted_text: String::from("1572213799747"),
format: ConversionFormat::EpochMillis
},
ConversionResult {
converted_text: String::from("2m 10s 1ms ago"),
format: ConversionFormat::DurationSince
}
]
);
}

#[test]
fn rfc3339_offset() {
let now = Utc.timestamp_millis(1572213799749);
let date = Utc.timestamp_millis(1572213799747);
let result = convert(&ParsedInput {
input_format: DateTimeFormat::Rfc3339,
input_zone: Some(date.with_timezone(&Local).offset().fix()),
value: date,
});
let result = convert(
&ParsedInput {
input_format: DateTimeFormat::Rfc3339,
input_zone: Some(date.with_timezone(&Local).offset().fix()),
value: date,
},
now,
);

// Should skip RFC3339 in Local
assert_eq!(
Expand All @@ -158,6 +211,10 @@ mod tests {
ConversionResult {
converted_text: String::from("1572213799747"),
format: ConversionFormat::EpochMillis
},
ConversionResult {
converted_text: String::from("2ms ago"),
format: ConversionFormat::DurationSince
}
]
);
Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::converting::ConversionResult;
use chrono::prelude::*;

mod alfred;
mod converting;
Expand All @@ -19,7 +20,7 @@ pub enum OutputMode {
/// Will return an error string if `input` cannot be parsed to a date.
pub fn run(input: &Option<String>, output_mode: &OutputMode) -> Result<(), &'static str> {
let parsed_input = crate::parsing::parse_input(input)?;
let conversion_results = crate::converting::convert(&parsed_input);
let conversion_results = crate::converting::convert(&parsed_input, Utc::now());

match output_mode {
OutputMode::ValuePerLine => output_value_per_line(&conversion_results),
Expand Down
14 changes: 8 additions & 6 deletions src/parsing.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use chrono::prelude::*;
use chrono_english::{parse_date_string,Dialect};
use chrono_english::{parse_date_string, Dialect};

#[derive(PartialEq, Debug)]
pub enum DateTimeFormat {
Expand Down Expand Up @@ -95,11 +95,13 @@ fn parse_from_format_zoned(input: &str, format: &str) -> Option<ParsedInput> {
}

fn parse_from_english(input: &str) -> Option<ParsedInput> {
parse_date_string(input, Local::now(), Dialect::Us).ok().map(|d| ParsedInput {
input_format: DateTimeFormat::English,
input_zone: None,
value: d.with_timezone(&Utc),
})
parse_date_string(input, Local::now(), Dialect::Us)
.ok()
.map(|d| ParsedInput {
input_format: DateTimeFormat::English,
input_zone: None,
value: d.with_timezone(&Utc),
})
}

pub fn parse_input(input: &Option<String>) -> Result<ParsedInput, &'static str> {
Expand Down
Binary file modified timeturner.alfredworkflow
Binary file not shown.

0 comments on commit 4f4b605

Please sign in to comment.