Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:persian-tools/rust-persian-tools int…
Browse files Browse the repository at this point in the history
…o dev
  • Loading branch information
ali77gh committed Jan 21, 2024
2 parents 6771bf3 + fbbdb22 commit 7e18612
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 8 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ include = ["src/**/*.rs", "Cargo.toml", "LICENSE", "README.md"]


[dependencies]
regex = "1.10.2"
urlencoding = { version = "2.1.3", optional = true }
serde = { version = "1.0.195", features = ["derive"], optional = true }
thiserror = { version = "1.0.56", optional = true }
Expand Down Expand Up @@ -49,6 +48,7 @@ full = [
"legal-id",
"words-to-number",
"sheba",
"remaining-time",
] # For now, by default we enable all features:


Expand All @@ -75,6 +75,7 @@ half-space = []
legal-id = ["dep:thiserror"]
words-to-number = ["dep:thiserror", "commas", "digits", "remove-ordinal-suffix"]
sheba = ["dep:thiserror"]
remaining-time = ["time-ago"]

[package.metadata.docs.rs]
all-features = true
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ all: build check test docs
fmt:
cargo fmt

build: full default add-ordinal-suffix commas digits find-capital-by-province persian-chars national-id remove-ordinal-suffix url-fix verity-card-number time-ago phone-number bill number-to-words get-bank-name-by-card-number extract-card-number get-place-by-iran-national-id half-space legal-id words-to-number sheba
build: full default add-ordinal-suffix commas digits find-capital-by-province persian-chars national-id remove-ordinal-suffix url-fix verity-card-number time-ago phone-number bill number-to-words get-bank-name-by-card-number extract-card-number get-place-by-iran-national-id half-space legal-id words-to-number sheba remaining-time

check: clippy lint

Expand Down Expand Up @@ -141,3 +141,9 @@ sheba:
cargo build --no-default-features --features=sheba
@ ls -sh target/debug/*.rlib


remaining-time:
@ echo ""
cargo build --no-default-features --features=remaining-time
@ ls -sh target/debug/*.rlib

4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
feature = "legal-id",
feature = "words-to-number",
feature = "sheba",
feature = "remaining-time",
)))]
compile_error!("No available Cargo feature is included");

Expand Down Expand Up @@ -85,3 +86,6 @@ pub mod words_to_number;

#[cfg(feature = "sheba")]
pub mod sheba;

#[cfg(feature = "remaining-time")]
pub mod remaining_time;
192 changes: 192 additions & 0 deletions src/remaining_time/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
use std::fmt;

use crate::time_ago::{
convert_to_timestamp, get_current_timestamp, TimeAgoError, DAY, HOUR, MINUTE, MONTH, YEAR,
};

#[derive(Debug, PartialEq)]
pub struct RemainingTime {
pub years: u32,
pub months: u8,
pub days: u8,
pub hours: u8,
pub minutes: u8,
pub seconds: u8,
pub is_finished: bool,
}

impl fmt::Display for RemainingTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Format the struct fields as needed

let mut periods: Vec<String> = Vec::new();

if self.years > 0 {
periods.push(format!("{} سال", self.years))
}
if self.months > 0 {
periods.push(format!("{} ماه", self.months))
}
if self.days > 0 {
periods.push(format!("{} روز", self.days))
}
if self.hours > 0 {
periods.push(format!("{} ساعت", self.hours))
}
if self.minutes > 0 {
periods.push(format!("{} دقیقه", self.minutes))
}
if self.seconds > 0 {
periods.push(format!("{} ثانیه", self.seconds))
}

write!(f, "{}", periods.join(" و "))
}
}

/// returns [RemainingTime] as result if the datetime has a valid format
///
/// in [RemainingTime] you can use its field like ```remaining_time.hours``` or ```remaining_time.days```
/// also Display trais is implmented for [RemainingTime] so if you could try try ```println("{}" , remaining_time)``` and a string like : ```۱ سال و ۱ ماه و ۲ روز و ۳ ساعت و ۵ دقیقه و ۸ ثانیه```
///
/// # Warning
/// This function is desgined to only works for these date time formats :
///
///
/// - `%Y-%m-%d %H:%M:%S`: Sortable format
/// - `%Y/%m/%d %H:%M:%S`: Sortable format
/// - `%Y-%m-%dT%H:%M:%S%:z`: ISO 8601 with timezone offset
/// - `%Y-%m-%dT%H:%M:%S%.3f%:z`: ISO 8601 with milliseconds and timezone offset
/// - `%a, %d %b %Y %H:%M:%S %z`: RFC 2822 Format
///
///
/// timezone is set with the current timezone of the OS.
///
/// ```
/// use rust_persian_tools::remaining_time::{remaining_time , RemainingTime};
/// use chrono::{Duration, Local};
///
/// let current_time = Local::now();
/// let due_date = current_time
/// + Duration::weeks(320)
/// + Duration::hours(7)
/// + Duration::minutes(13)
/// + Duration::seconds(37);
/// let formatted_time = due_date.format("%Y-%m-%d %H:%M:%S").to_string();
///
/// assert_eq!(
/// remaining_time(&formatted_time).unwrap(),
/// RemainingTime {
/// years: 6,
/// months: 1,
/// days: 20,
/// hours: 7,
/// minutes: 13,
/// seconds: 37,
/// is_finished: false,
/// }
/// );
///
/// assert_eq!(
/// format!("{}", remaining_time(&formatted_time).unwrap()),
/// String::from("6 سال و 1 ماه و 20 روز و 7 ساعت و 13 دقیقه و 37 ثانیه")
/// );
///
///
/// ```
pub fn remaining_time(datetime: impl AsRef<str>) -> Result<RemainingTime, TimeAgoError> {
let datetime = datetime.as_ref();

let due_date = convert_to_timestamp(datetime)?;
let now = get_current_timestamp();

let mut remaining_timestamp = due_date - now;

if remaining_timestamp <= 0 {
// if its due
return Ok(RemainingTime {
years: 0,
months: 0,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
is_finished: true,
});
}

let years: u32 = (remaining_timestamp / YEAR) as u32;
remaining_timestamp %= YEAR;

let months: u8 = ((remaining_timestamp / MONTH) % MONTH) as u8;
remaining_timestamp %= MONTH;

let days: u8 = ((remaining_timestamp / DAY) % DAY) as u8;
remaining_timestamp %= DAY;

let hours: u8 = ((remaining_timestamp / HOUR) % HOUR) as u8;
remaining_timestamp %= HOUR;

let minutes: u8 = ((remaining_timestamp / MINUTE) % MINUTE) as u8;
remaining_timestamp %= MINUTE;

let seconds: u8 = remaining_timestamp as u8;

Ok(RemainingTime {
years,
months,
days,
hours,
minutes,
seconds,
is_finished: false,
})
}

#[cfg(test)]
mod tests {
use super::*;
use chrono::{Duration, Local};

#[test]
fn remaining_time_test() {
let current_time = Local::now();
let due_date = current_time
+ Duration::weeks(320)
+ Duration::hours(7)
+ Duration::minutes(13)
+ Duration::seconds(37);
let formatted_time = due_date.format("%Y-%m-%d %H:%M:%S").to_string();

assert_eq!(
remaining_time(formatted_time).unwrap(),
RemainingTime {
years: 6,
months: 1,
days: 20,
hours: 7,
minutes: 13,
seconds: 37,
is_finished: false,
}
);
}

#[test]
fn remaining_time_as_string_test() {
let current_time = Local::now();
let due_date =
current_time + Duration::weeks(340) + Duration::minutes(12) + Duration::seconds(37);
let formatted_time = due_date.format("%Y-%m-%d %H:%M:%S").to_string();

assert_eq!(
format!("{}", remaining_time(formatted_time).unwrap()),
String::from("6 سال و 6 ماه و 10 روز و 12 دقیقه و 37 ثانیه")
);
}

#[test]
fn remaining_time_fail_test() {
assert!(remaining_time("123:12312").is_err());
}
}
12 changes: 6 additions & 6 deletions src/time_ago/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use chrono::{DateTime, Local, NaiveDateTime, TimeZone};
use thiserror::Error;

const MINUTE: i64 = 60;
const HOUR: i64 = MINUTE * 60;
const DAY: i64 = HOUR * 24;
const WEEK: i64 = DAY * 7;
const MONTH: i64 = DAY * 30;
const YEAR: i64 = DAY * 365;
pub(crate) const MINUTE: i64 = 60;
pub(crate) const HOUR: i64 = MINUTE * 60;
pub(crate) const DAY: i64 = HOUR * 24;
pub(crate) const WEEK: i64 = DAY * 7;
pub(crate) const MONTH: i64 = DAY * 30;
pub(crate) const YEAR: i64 = DAY * 365;

#[derive(Error, Debug)]
pub enum TimeAgoError {
Expand Down

0 comments on commit 7e18612

Please sign in to comment.