2020-10-12 20:01:27 +00:00
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
2020-08-29 23:16:33 +00:00
|
|
|
|
2020-10-12 20:01:27 +00:00
|
|
|
use std::fmt::{Display, Formatter, Result as FmtResult};
|
2020-09-01 14:34:50 +00:00
|
|
|
|
2021-01-14 17:56:57 +00:00
|
|
|
use crate::consts::{LOCAL_TIMEZONE, PYTHON_LOCATION};
|
|
|
|
|
2020-09-01 16:07:51 +00:00
|
|
|
use chrono::TimeZone;
|
2020-10-12 20:01:27 +00:00
|
|
|
use chrono_tz::Tz;
|
2020-09-19 14:20:43 +00:00
|
|
|
use std::convert::TryFrom;
|
2021-01-14 17:56:57 +00:00
|
|
|
use std::str::from_utf8;
|
|
|
|
use tokio::process::Command;
|
2020-08-29 23:16:33 +00:00
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum InvalidTime {
|
|
|
|
ParseErrorDMY,
|
|
|
|
ParseErrorHMS,
|
|
|
|
ParseErrorDisplacement,
|
|
|
|
ParseErrorChrono,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Display for InvalidTime {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
|
|
|
write!(f, "InvalidTime: {:?}", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for InvalidTime {}
|
|
|
|
|
2020-08-29 23:16:33 +00:00
|
|
|
enum ParseType {
|
|
|
|
Explicit,
|
|
|
|
Displacement,
|
|
|
|
}
|
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
pub struct TimeParser {
|
2020-08-29 23:16:33 +00:00
|
|
|
timezone: Tz,
|
|
|
|
inverted: bool,
|
|
|
|
time_string: String,
|
|
|
|
parse_type: ParseType,
|
|
|
|
}
|
|
|
|
|
2020-09-19 14:20:43 +00:00
|
|
|
impl TryFrom<&TimeParser> for i64 {
|
|
|
|
type Error = InvalidTime;
|
|
|
|
|
|
|
|
fn try_from(value: &TimeParser) -> Result<Self, Self::Error> {
|
|
|
|
value.timestamp()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-29 23:16:33 +00:00
|
|
|
impl TimeParser {
|
2020-11-29 00:36:42 +00:00
|
|
|
pub fn new(input: &str, timezone: Tz) -> Self {
|
2020-09-25 11:48:44 +00:00
|
|
|
let inverted = input.starts_with('-');
|
2020-08-29 23:16:33 +00:00
|
|
|
|
2020-09-25 11:48:44 +00:00
|
|
|
let parse_type = if input.contains('/') || input.contains(':') {
|
2020-08-29 23:16:33 +00:00
|
|
|
ParseType::Explicit
|
2020-10-12 20:01:27 +00:00
|
|
|
} else {
|
2020-08-29 23:16:33 +00:00
|
|
|
ParseType::Displacement
|
|
|
|
};
|
|
|
|
|
|
|
|
Self {
|
|
|
|
timezone,
|
|
|
|
inverted,
|
2020-09-25 11:48:44 +00:00
|
|
|
time_string: input.trim_start_matches('-').to_string(),
|
2020-08-29 23:16:33 +00:00
|
|
|
parse_type,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
pub fn timestamp(&self) -> Result<i64, InvalidTime> {
|
2020-08-29 23:16:33 +00:00
|
|
|
match self.parse_type {
|
2020-10-12 20:01:27 +00:00
|
|
|
ParseType::Explicit => Ok(self.process_explicit()?),
|
2020-08-29 23:16:33 +00:00
|
|
|
|
|
|
|
ParseType::Displacement => {
|
|
|
|
let now = SystemTime::now();
|
|
|
|
let since_epoch = now
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
.expect("Time calculated as going backwards. Very bad");
|
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
Ok(since_epoch.as_secs() as i64 + self.process_displacement()?)
|
2020-10-12 20:01:27 +00:00
|
|
|
}
|
2020-09-01 14:34:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn displacement(&self) -> Result<i64, InvalidTime> {
|
|
|
|
match self.parse_type {
|
|
|
|
ParseType::Explicit => {
|
|
|
|
let now = SystemTime::now();
|
|
|
|
let since_epoch = now
|
|
|
|
.duration_since(UNIX_EPOCH)
|
|
|
|
.expect("Time calculated as going backwards. Very bad");
|
|
|
|
|
|
|
|
Ok(self.process_explicit()? - since_epoch.as_secs() as i64)
|
2020-10-12 20:01:27 +00:00
|
|
|
}
|
2020-09-01 14:34:50 +00:00
|
|
|
|
2020-10-12 20:01:27 +00:00
|
|
|
ParseType::Displacement => Ok(self.process_displacement()?),
|
2020-08-29 23:16:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
fn process_explicit(&self) -> Result<i64, InvalidTime> {
|
2020-09-25 11:48:44 +00:00
|
|
|
let segments = self.time_string.matches('-').count();
|
2020-09-01 14:34:50 +00:00
|
|
|
|
|
|
|
let parse_string = if segments == 1 {
|
2020-09-25 11:48:44 +00:00
|
|
|
let slashes = self.time_string.matches('/').count();
|
2020-09-01 14:34:50 +00:00
|
|
|
|
|
|
|
match slashes {
|
|
|
|
0 => Ok("%d-".to_string()),
|
|
|
|
1 => Ok("%d/%m-".to_string()),
|
|
|
|
2 => Ok("%d/%m/%Y-".to_string()),
|
2020-10-12 20:01:27 +00:00
|
|
|
_ => Err(InvalidTime::ParseErrorDMY),
|
2020-09-01 14:34:50 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Ok("".to_string())
|
2020-11-29 00:36:42 +00:00
|
|
|
}? + {
|
2020-09-25 11:48:44 +00:00
|
|
|
let colons = self.time_string.matches(':').count();
|
2020-09-01 14:34:50 +00:00
|
|
|
|
|
|
|
match colons {
|
|
|
|
1 => Ok("%H:%M"),
|
|
|
|
2 => Ok("%H:%M:%S"),
|
2020-10-12 20:01:27 +00:00
|
|
|
_ => Err(InvalidTime::ParseErrorHMS),
|
2020-09-01 14:34:50 +00:00
|
|
|
}
|
|
|
|
}?;
|
|
|
|
|
2020-10-12 20:01:27 +00:00
|
|
|
let dt = self
|
|
|
|
.timezone
|
|
|
|
.datetime_from_str(self.time_string.as_str(), &parse_string)
|
|
|
|
.map_err(|_| InvalidTime::ParseErrorChrono)?;
|
2020-09-01 14:34:50 +00:00
|
|
|
|
|
|
|
Ok(dt.timestamp() as i64)
|
2020-08-29 23:16:33 +00:00
|
|
|
}
|
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
fn process_displacement(&self) -> Result<i64, InvalidTime> {
|
2020-08-29 23:16:33 +00:00
|
|
|
let mut current_buffer = "0".to_string();
|
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
let mut seconds = 0 as i64;
|
|
|
|
let mut minutes = 0 as i64;
|
|
|
|
let mut hours = 0 as i64;
|
|
|
|
let mut days = 0 as i64;
|
2020-08-29 23:16:33 +00:00
|
|
|
|
|
|
|
for character in self.time_string.chars() {
|
|
|
|
match character {
|
|
|
|
's' => {
|
2020-09-01 14:34:50 +00:00
|
|
|
seconds = current_buffer.parse::<i64>().unwrap();
|
2020-08-29 23:16:33 +00:00
|
|
|
current_buffer = String::from("0");
|
2020-10-12 20:01:27 +00:00
|
|
|
}
|
2020-08-29 23:16:33 +00:00
|
|
|
|
|
|
|
'm' => {
|
2020-09-01 14:34:50 +00:00
|
|
|
minutes = current_buffer.parse::<i64>().unwrap();
|
2020-08-29 23:16:33 +00:00
|
|
|
current_buffer = String::from("0");
|
2020-10-12 20:01:27 +00:00
|
|
|
}
|
2020-08-29 23:16:33 +00:00
|
|
|
|
|
|
|
'h' => {
|
2020-09-01 14:34:50 +00:00
|
|
|
hours = current_buffer.parse::<i64>().unwrap();
|
2020-08-29 23:16:33 +00:00
|
|
|
current_buffer = String::from("0");
|
2020-10-12 20:01:27 +00:00
|
|
|
}
|
2020-08-29 23:16:33 +00:00
|
|
|
|
|
|
|
'd' => {
|
2020-09-01 14:34:50 +00:00
|
|
|
days = current_buffer.parse::<i64>().unwrap();
|
2020-08-29 23:16:33 +00:00
|
|
|
current_buffer = String::from("0");
|
2020-10-12 20:01:27 +00:00
|
|
|
}
|
2020-08-29 23:16:33 +00:00
|
|
|
|
|
|
|
c => {
|
|
|
|
if c.is_digit(10) {
|
2020-08-30 20:08:08 +00:00
|
|
|
current_buffer += &c.to_string();
|
2020-10-12 20:01:27 +00:00
|
|
|
} else {
|
|
|
|
return Err(InvalidTime::ParseErrorDisplacement);
|
2020-08-29 23:16:33 +00:00
|
|
|
}
|
2020-10-12 20:01:27 +00:00
|
|
|
}
|
2020-08-29 23:16:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-12 20:01:27 +00:00
|
|
|
let full = (seconds
|
|
|
|
+ (minutes * 60)
|
|
|
|
+ (hours * 3600)
|
|
|
|
+ (days * 86400)
|
|
|
|
+ current_buffer.parse::<i64>().unwrap())
|
|
|
|
* if self.inverted { -1 } else { 1 };
|
2020-08-29 23:16:33 +00:00
|
|
|
|
2020-09-01 14:34:50 +00:00
|
|
|
Ok(full)
|
2020-08-29 23:16:33 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-14 17:56:57 +00:00
|
|
|
|
|
|
|
pub(crate) async fn natural_parser(time: &str, timezone: &str) -> Option<i64> {
|
|
|
|
Command::new(&*PYTHON_LOCATION)
|
|
|
|
.arg("-c")
|
|
|
|
.arg(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/dp.py")))
|
|
|
|
.arg(time)
|
|
|
|
.arg(timezone)
|
|
|
|
.arg(&*LOCAL_TIMEZONE)
|
|
|
|
.output()
|
|
|
|
.await
|
|
|
|
.ok()
|
|
|
|
.map(|inner| {
|
|
|
|
if inner.status.success() {
|
|
|
|
Some(from_utf8(&*inner.stdout).unwrap().parse::<i64>().unwrap())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.flatten()
|
|
|
|
.map(|inner| if inner < 0 { None } else { Some(inner) })
|
|
|
|
.flatten()
|
|
|
|
}
|