From 3660f0608e25cf0c48561a5c89f4fa080a0eb564 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 16 Sep 2017 10:47:03 +0200 Subject: Add end-of-month type/calc Adds util module --- src/error.rs | 5 ++++ src/lib.rs | 1 + src/timetype.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++++--------- src/util.rs | 36 +++++++++++++++++++++++ 4 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 src/util.rs diff --git a/src/error.rs b/src/error.rs index 00fad98..4d4a0af 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,6 +38,11 @@ error_chain! { display("Argument Error: Cannot calculate end-of-year on a {:?}", tt) } + CannotCalculateEndOfMonthOn(tt: TimeType) { + description("Argument Error: Cannot calculate end-of-month") + display("Argument Error: Cannot calculate end-of-month on a {:?}", tt) + } + } } diff --git a/src/lib.rs b/src/lib.rs index 2f59f3f..624ce64 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,4 +6,5 @@ pub mod error; pub mod iter; pub mod result; pub mod timetype; +mod util; diff --git a/src/timetype.rs b/src/timetype.rs index 67c9229..961a9b3 100644 --- a/src/timetype.rs +++ b/src/timetype.rs @@ -15,6 +15,7 @@ use result::Result; use error::KairosErrorKind as KEK; use error::KairosError as KE; use error_chain::ChainedError; +use util::*; /// A Type of Time, currently based on chrono::NaiveDateTime #[derive(Debug, Clone)] @@ -32,6 +33,7 @@ pub enum TimeType { Subtraction(Box, Box), EndOfYear(Box), + EndOfMonth(Box), } impl Add for TimeType { @@ -165,6 +167,16 @@ impl TimeType { TimeType::EndOfYear(Box::new(self)) } + /// Calculate the end of the month based on the current TimeType + /// + /// # Warning + /// + /// If the current TimeType does _not_ evaluate to a `TimeType::Moment`, calculating the end of + /// the month will fail + pub fn end_of_month(self) -> TimeType { + TimeType::EndOfMonth(Box::new(self)) + } + /// Get the number of seconds, if the TimeType is not a duration type, zero is returned /// /// # Warning @@ -340,6 +352,7 @@ fn do_calculate(tt: TimeType) -> Result { TT::Addition(a, b) => add(a, b), TT::Subtraction(a, b) => sub(a, b), TT::EndOfYear(inner) => end_of_year(*inner), + TT::EndOfMonth(inner) => end_of_month(*inner), x => Ok(x) } } @@ -362,6 +375,32 @@ fn end_of_year(tt: TimeType) -> Result { els @ TT::Subtraction(_, _) => Err(KE::from_kind(KEK::CannotCalculateEndOfYearOn(els))), TT::Moment(m) => Ok(TT::moment(NaiveDate::from_ymd(m.year(), 12, 31).and_hms(0, 0, 0))), TT::EndOfYear(e) => do_calculate(*e), + TT::EndOfMonth(e) => do_calculate(*e), + } +} + +/// Evaluates the passed argument and if it is a `TT::Moment` it adjust its to the end of the month +/// else returns an error +/// +/// Calling a end-of-month on a end-of-month yields end-of-month applied only once. +fn end_of_month(tt: TimeType) -> Result { + use timetype::TimeType as TT; + + match try!(do_calculate(tt)) { + els @ TT::Seconds(_) | + els @ TT::Minutes(_) | + els @ TT::Hours(_) | + els @ TT::Days(_) | + els @ TT::Months(_) | + els @ TT::Years(_) | + els @ TT::Addition(_, _) | + els @ TT::Subtraction(_, _) => Err(KE::from_kind(KEK::CannotCalculateEndOfMonthOn(els))), + TT::Moment(m) => { + let last_day = get_num_of_days_in_month(m.year(), m.month()); + Ok(TT::moment(NaiveDate::from_ymd(m.year(), m.month(), last_day).and_hms(0, 0, 0))) + }, + TT::EndOfYear(e) => do_calculate(*e), + TT::EndOfMonth(e) => do_calculate(*e), } } @@ -407,6 +446,9 @@ fn add(a: Box, b: Box) -> Result { (TT::EndOfYear(e), other) => Err(KE::from_kind(KEK::CannotAdd(other, TT::EndOfYear(e)))), (other, TT::EndOfYear(e)) => Err(KE::from_kind(KEK::CannotAdd(other, TT::EndOfYear(e)))), + (TT::EndOfMonth(e), other) => Err(KE::from_kind(KEK::CannotAdd(other, TT::EndOfMonth(e)))), + (other, TT::EndOfMonth(e)) => Err(KE::from_kind(KEK::CannotAdd(other, TT::EndOfMonth(e)))), + others => unimplemented!(), } } @@ -423,6 +465,7 @@ fn add_to_seconds(amount: i64, tt: TimeType) -> Result { TT::Years(a) => Ok(TT::Seconds(a * 60 * 60 * 24 * 30 * 12 + amount)), TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Seconds(amount), TT::Moment(m)))), TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Seconds(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Seconds(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => add_to_seconds(amount, try!(add(b, c))), TT::Subtraction(b, c) => add_to_seconds(amount, try!(sub(b, c))), } @@ -440,6 +483,7 @@ fn add_to_minutes(amount: i64, tt: TimeType) -> Result { TT::Years(a) => Ok(TT::Minutes(a * 60 * 24 * 30 * 12 + amount)), TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Minutes(amount), TT::Moment(m)))), TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Minutes(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Minutes(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => add_to_minutes(amount, try!(add(b, c))), TT::Subtraction(b, c) => add_to_minutes(amount, try!(sub(b, c))), } @@ -457,6 +501,7 @@ fn add_to_hours(amount: i64, tt: TimeType) -> Result { TT::Years(a) => Ok(TT::Hours( a * 24 * 30 * 12 + amount)), TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Hours(amount), TT::Moment(m)))), TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Hours(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Hours(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => add_to_hours(amount, try!(add(b, c))), TT::Subtraction(b, c) => add_to_hours(amount, try!(sub(b, c))), } @@ -474,6 +519,7 @@ fn add_to_days(amount: i64, tt: TimeType) -> Result { TT::Years(a) => Ok(TT::Days( a * 30 * 12 + amount)), TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Days(amount), TT::Moment(m)))), TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Days(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Days(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => add_to_days(amount, try!(add(b, c))), TT::Subtraction(b, c) => add_to_days(amount, try!(sub(b, c))), } @@ -491,6 +537,7 @@ fn add_to_months(amount: i64, tt: TimeType) -> Result { TT::Years(a) => Ok(TT::Months( a * 12 + amount)), TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Months(amount), TT::Moment(m)))), TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Months(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Months(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => add_to_months(amount, try!(add(b, c))), TT::Subtraction(b, c) => add_to_months(amount, try!(sub(b, c))), } @@ -508,6 +555,7 @@ fn add_to_years(amount: i64, tt: TimeType) -> Result { TT::Years(a) => Ok(TT::Years( a + amount)), TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Years(amount), TT::Moment(m)))), TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Years(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Years(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => add_to_years(amount, try!(add(b, c))), TT::Subtraction(b, c) => add_to_years(amount, try!(sub(b, c))), } @@ -635,6 +683,7 @@ fn add_to_moment(mom: NaiveDateTime, tt: TimeType) -> Result { }, TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Moment(mom), TT::Moment(m)))), TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Moment(mom), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Moment(mom), TT::EndOfMonth(e)))), TT::Addition(a, b) => add_to_moment(mom, try!(add(a, b))), TT::Subtraction(a, b) => add_to_moment(mom, try!(sub(a, b))), } @@ -694,6 +743,10 @@ fn sub(a: Box, b: Box) -> Result { (TT::EndOfYear(e), other) => Err(KE::from_kind(KEK::CannotSub(other, TT::EndOfYear(e)))), (other, TT::EndOfYear(e)) => Err(KE::from_kind(KEK::CannotSub(other, TT::EndOfYear(e)))), + (TT::EndOfMonth(e), other) => Err(KE::from_kind(KEK::CannotSub(other, TT::EndOfMonth(e)))), + (other, TT::EndOfMonth(e)) => Err(KE::from_kind(KEK::CannotSub(other, TT::EndOfMonth(e)))), + + others => unimplemented!(), } } @@ -708,8 +761,9 @@ fn sub_from_seconds(amount: i64, tt: TimeType) -> Result { TT::Days(a) => Ok(TT::Seconds(amount - a * 60 * 60 * 24)), TT::Months(a) => Ok(TT::Seconds(amount - a * 60 * 60 * 24 * 30)), TT::Years(a) => Ok(TT::Seconds(amount - a * 60 * 60 * 24 * 30 * 12)), - TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Seconds(amount), TT::Moment(m)))), - TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Seconds(amount), TT::EndOfYear(e)))), + TT::Moment(m) => Err(KE::from_kind(KEK::CannotSub(TT::Seconds(amount), TT::Moment(m)))), + TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotSub(TT::Seconds(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotSub(TT::Seconds(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => sub_from_seconds(amount, try!(add(b, c))), TT::Subtraction(b, c) => sub_from_seconds(amount, try!(sub(b, c))), } @@ -725,8 +779,9 @@ fn sub_from_minutes(amount: i64, tt: TimeType) -> Result { TT::Days(a) => Ok(TT::Minutes(amount - a * 60 * 24)), TT::Months(a) => Ok(TT::Minutes(amount - a * 60 * 24 * 30)), TT::Years(a) => Ok(TT::Minutes(amount - a * 60 * 24 * 30 * 12)), - TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Minutes(amount), TT::Moment(m)))), - TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Minutes(amount), TT::EndOfYear(e)))), + TT::Moment(m) => Err(KE::from_kind(KEK::CannotSub(TT::Minutes(amount), TT::Moment(m)))), + TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotSub(TT::Minutes(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotSub(TT::Minutes(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => sub_from_minutes(amount, try!(add(b, c))), TT::Subtraction(b, c) => sub_from_minutes(amount, try!(sub(b, c))), } @@ -742,8 +797,9 @@ fn sub_from_hours(amount: i64, tt: TimeType) -> Result { TT::Days(a) => Ok(TT::Hours(amount - a * 24)), TT::Months(a) => Ok(TT::Hours(amount - a * 24 * 30)), TT::Years(a) => Ok(TT::Hours(amount - a * 24 * 30 * 12)), - TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Hours(amount), TT::Moment(m)))), - TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Hours(amount), TT::EndOfYear(e)))), + TT::Moment(m) => Err(KE::from_kind(KEK::CannotSub(TT::Hours(amount), TT::Moment(m)))), + TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotSub(TT::Hours(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotSub(TT::Hours(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => sub_from_hours(amount, try!(add(b, c))), TT::Subtraction(b, c) => sub_from_hours(amount, try!(sub(b, c))), } @@ -759,8 +815,9 @@ fn sub_from_days(amount: i64, tt: TimeType) -> Result { TT::Days(a) => Ok(TT::Days(amount - a)), TT::Months(a) => Ok(TT::Days(amount - a * 30)), TT::Years(a) => Ok(TT::Days(amount - a * 30 * 12)), - TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Days(amount), TT::Moment(m)))), - TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Days(amount), TT::EndOfYear(e)))), + TT::Moment(m) => Err(KE::from_kind(KEK::CannotSub(TT::Days(amount), TT::Moment(m)))), + TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotSub(TT::Days(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotSub(TT::Days(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => sub_from_days(amount, try!(add(b, c))), TT::Subtraction(b, c) => sub_from_days(amount, try!(sub(b, c))), } @@ -776,8 +833,9 @@ fn sub_from_months(amount: i64, tt: TimeType) -> Result { TT::Days(a) => Ok(TT::Days(amount * 30 - a)), TT::Months(a) => Ok(TT::Months(amount - a)), TT::Years(a) => Ok(TT::Months(amount - a * 12)), - TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Months(amount), TT::Moment(m)))), - TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Months(amount), TT::EndOfYear(e)))), + TT::Moment(m) => Err(KE::from_kind(KEK::CannotSub(TT::Months(amount), TT::Moment(m)))), + TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotSub(TT::Months(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotSub(TT::Months(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => sub_from_months(amount, try!(add(b, c))), TT::Subtraction(b, c) => sub_from_months(amount, try!(sub(b, c))), } @@ -793,8 +851,9 @@ fn sub_from_years(amount: i64, tt: TimeType) -> Result { TT::Days(a) => Ok(TT::Days(amount * 12 * 30 - a)), TT::Months(a) => Ok(TT::Months(amount * 12 - a)), TT::Years(a) => Ok(TT::Years(amount - a)), - TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Years(amount), TT::Moment(m)))), - TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Years(amount), TT::EndOfYear(e)))), + TT::Moment(m) => Err(KE::from_kind(KEK::CannotSub(TT::Years(amount), TT::Moment(m)))), + TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotSub(TT::Years(amount), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotSub(TT::Months(amount), TT::EndOfMonth(e)))), TT::Addition(b, c) => sub_from_years(amount, try!(add(b, c))), TT::Subtraction(b, c) => sub_from_years(amount, try!(sub(b, c))), } @@ -945,8 +1004,9 @@ fn sub_from_moment(mom: NaiveDateTime, tt: TimeType) -> Result { .and_hms(h as u32, mi as u32, s as u32); Ok(TimeType::moment(tt)) }, - TT::Moment(m) => Err(KE::from_kind(KEK::CannotAdd(TT::Moment(mom), TT::Moment(m)))), - TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Moment(mom), TT::EndOfYear(e)))), + TT::Moment(m) => Err(KE::from_kind(KEK::CannotSub(TT::Moment(mom), TT::Moment(m)))), + TT::EndOfYear(e) => Err(KE::from_kind(KEK::CannotSub(TT::Moment(mom), TT::EndOfYear(e)))), + TT::EndOfMonth(e) => Err(KE::from_kind(KEK::CannotSub(TT::Moment(mom), TT::EndOfMonth(e)))), TT::Addition(a, b) => sub_from_moment(mom, try!(add(a, b))), TT::Subtraction(a, b) => sub_from_moment(mom, try!(sub(a, b))), } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..3062c84 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,36 @@ + +#[inline] +pub fn get_num_of_days_in_month(y: i32, m: u32) -> u32 { + if m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12 { + 31 + } else if m == 2 { + if is_leap_year(y) { + 29 + } else { + 28 + } + } else { + 30 + } +} + +#[inline] +pub fn is_leap_year(y: i32) -> bool { + (y % 4 == 0) && (y % 100 != 0 || y % 400 == 0) +} + +#[test] +fn test_is_leap_year() { + let leaps = [ 1880, 1884, 1888, 1892, 1896, 1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, + 1936, 1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 1972, 1976, 1980, 1984, 1988, 1992, + 1996, 2000, 2004, 2008, 2012, 2020, 2024, 2028, 2032, 2036, 2040, 2044, 2048, 2052, 2056, + 2060, 2064, 2068, 2072, 2076, 2080, 2084, 2088, 2092, 2096, 2104, 2108, 2112, 2116, 2120, + 2124, 2128, 2132, 2136, 2140, 2144, 2148, 2152, 2156, 2160, 2164, 2168, 2172, 2176, 2180, + 2184, 2188, 2192, 2196, 2204, 2208 ]; + + for i in leaps.iter() { + assert!(is_leap_year(*i), "Is no leap year: {}", i); + assert!(!is_leap_year(*i + 1), "Seems to be leap year: {}, but should not be", i + 1); + } + +} -- cgit v1.2.3 From b1e120f29b6a8886c7f5397b3f3d838bd9b140b9 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 16 Sep 2017 10:50:09 +0200 Subject: Move utility functions to util package --- src/timetype.rs | 89 --------------------------------------------------------- src/util.rs | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/timetype.rs b/src/timetype.rs index 961a9b3..1c076ae 100644 --- a/src/timetype.rs +++ b/src/timetype.rs @@ -561,38 +561,6 @@ fn add_to_years(amount: i64, tt: TimeType) -> Result { } } -#[inline] -fn adjust_times_add(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: i64, mut s: i64) - -> (i64, i64, i64, i64, i64, i64) -{ - macro_rules! fix { - { - $base:ident, - $border:expr, - $next:ident - } => { - while $base >= $border { - $next += 1; - $base -= $border; - } - } - } - - fix! { s , 60, mi } - fix! { mi, 60, h } - fix! { h , 24, d } - - if mo == 1 || mo == 3 || mo == 5 || mo == 7 || mo == 8 || mo == 10 || mo == 12 { - fix! { d , 31, mo } - } else { - fix! { d , 30, mo } - } - - fix! { mo, 12, y } - - (y, mo, d, h, mi, s) -} - fn add_to_moment(mom: NaiveDateTime, tt: TimeType) -> Result { use timetype::TimeType as TT; @@ -859,63 +827,6 @@ fn sub_from_years(amount: i64, tt: TimeType) -> Result { } } -#[inline] -fn adjust_times_sub(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: i64, mut s: i64) - -> (i64, i64, i64, i64, i64, i64) -{ - - println!("s < 0 -> = {}", s); - if s < 0 { - println!("mi -= {}", (s.abs() / 60) + 1); - println!("s = {}", (60 - (0 - s).abs())); - - mi -= (s.abs() / 60) + 1; - s = 60 - (0 - s).abs(); - } - println!(""); - - println!("mi < 0 -> = {}", mi); - if mi < 0 { - println!("h -= {}", (mi.abs() / 60) + 1); - println!("mi = {}", (60 - (0 - mi).abs())); - - h -= (mi.abs() / 60) + 1; - mi = 60 - (0 - mi).abs(); - } - println!(""); - - println!("h < 0 -> = {}", h); - if h < 0 { - println!("d -= {}", (h.abs() / 24) + 1); - println!("h = {}", (24 - (0 - h).abs())); - - d -= (h.abs() / 24) + 1; - h = 24 - (0 - h).abs(); - } - println!(""); - - println!("d < 1 -> = {}", d); - if d < 1 { - println!("mo -= {}", (d.abs() / 32) + 1); - println!("d = {}", (31 - (0 - d).abs())); - - mo -= (d.abs() / 32) + 1; - d = 31 - (0 - d).abs(); - } - println!(""); - - println!("mo < 1 -> = {}", mo); - if mo < 1 { - println!("y -= {}", (mo.abs() / 13) + 1); - println!("mo = {}", (12 - (0 - mo).abs())); - - y -= (mo.abs() / 13) + 1; - mo = 12 - (0 - mo).abs(); - } - - (y, mo, d, h, mi, s) -} - fn sub_from_moment(mom: NaiveDateTime, tt: TimeType) -> Result { use timetype::TimeType as TT; diff --git a/src/util.rs b/src/util.rs index 3062c84..4f62849 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,92 @@ +#[inline] +pub fn adjust_times_add(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: i64, mut s: i64) + -> (i64, i64, i64, i64, i64, i64) +{ + macro_rules! fix { + { + $base:ident, + $border:expr, + $next:ident + } => { + while $base >= $border { + $next += 1; + $base -= $border; + } + } + } + + fix! { s , 60, mi } + fix! { mi, 60, h } + fix! { h , 24, d } + + if mo == 1 || mo == 3 || mo == 5 || mo == 7 || mo == 8 || mo == 10 || mo == 12 { + fix! { d , 31, mo } + } else { + fix! { d , 30, mo } + } + + fix! { mo, 12, y } + + (y, mo, d, h, mi, s) +} + +#[inline] +pub fn adjust_times_sub(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: i64, mut s: i64) + -> (i64, i64, i64, i64, i64, i64) +{ + + println!("s < 0 -> = {}", s); + if s < 0 { + println!("mi -= {}", (s.abs() / 60) + 1); + println!("s = {}", (60 - (0 - s).abs())); + + mi -= (s.abs() / 60) + 1; + s = 60 - (0 - s).abs(); + } + println!(""); + + println!("mi < 0 -> = {}", mi); + if mi < 0 { + println!("h -= {}", (mi.abs() / 60) + 1); + println!("mi = {}", (60 - (0 - mi).abs())); + + h -= (mi.abs() / 60) + 1; + mi = 60 - (0 - mi).abs(); + } + println!(""); + + println!("h < 0 -> = {}", h); + if h < 0 { + println!("d -= {}", (h.abs() / 24) + 1); + println!("h = {}", (24 - (0 - h).abs())); + + d -= (h.abs() / 24) + 1; + h = 24 - (0 - h).abs(); + } + println!(""); + + println!("d < 1 -> = {}", d); + if d < 1 { + println!("mo -= {}", (d.abs() / 32) + 1); + println!("d = {}", (31 - (0 - d).abs())); + + mo -= (d.abs() / 32) + 1; + d = 31 - (0 - d).abs(); + } + println!(""); + + println!("mo < 1 -> = {}", mo); + if mo < 1 { + println!("y -= {}", (mo.abs() / 13) + 1); + println!("mo = {}", (12 - (0 - mo).abs())); + + y -= (mo.abs() / 13) + 1; + mo = 12 - (0 - mo).abs(); + } + + (y, mo, d, h, mi, s) +} + #[inline] pub fn get_num_of_days_in_month(y: i32, m: u32) -> u32 { -- cgit v1.2.3 From 2a6d9d7da0930ed73e5ab7ac7e05e310bc1dd56a Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 16 Sep 2017 10:58:22 +0200 Subject: Remove printlns --- src/util.rs | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/util.rs b/src/util.rs index 4f62849..d946f08 100644 --- a/src/util.rs +++ b/src/util.rs @@ -34,52 +34,27 @@ pub fn adjust_times_add(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: pub fn adjust_times_sub(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: i64, mut s: i64) -> (i64, i64, i64, i64, i64, i64) { - - println!("s < 0 -> = {}", s); if s < 0 { - println!("mi -= {}", (s.abs() / 60) + 1); - println!("s = {}", (60 - (0 - s).abs())); - mi -= (s.abs() / 60) + 1; s = 60 - (0 - s).abs(); } - println!(""); - println!("mi < 0 -> = {}", mi); if mi < 0 { - println!("h -= {}", (mi.abs() / 60) + 1); - println!("mi = {}", (60 - (0 - mi).abs())); - h -= (mi.abs() / 60) + 1; mi = 60 - (0 - mi).abs(); } - println!(""); - println!("h < 0 -> = {}", h); if h < 0 { - println!("d -= {}", (h.abs() / 24) + 1); - println!("h = {}", (24 - (0 - h).abs())); - d -= (h.abs() / 24) + 1; h = 24 - (0 - h).abs(); } - println!(""); - println!("d < 1 -> = {}", d); if d < 1 { - println!("mo -= {}", (d.abs() / 32) + 1); - println!("d = {}", (31 - (0 - d).abs())); - mo -= (d.abs() / 32) + 1; d = 31 - (0 - d).abs(); } - println!(""); - println!("mo < 1 -> = {}", mo); if mo < 1 { - println!("y -= {}", (mo.abs() / 13) + 1); - println!("mo = {}", (12 - (0 - mo).abs())); - y -= (mo.abs() / 13) + 1; mo = 12 - (0 - mo).abs(); } @@ -87,7 +62,6 @@ pub fn adjust_times_sub(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: (y, mo, d, h, mi, s) } - #[inline] pub fn get_num_of_days_in_month(y: i32, m: u32) -> u32 { if m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12 { -- cgit v1.2.3 From c26d8962f8190af66ad51dd219244d37f1ca0d19 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 16 Sep 2017 10:58:37 +0200 Subject: Add tests for end-of-month --- src/timetype.rs | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) diff --git a/src/timetype.rs b/src/timetype.rs index 1c076ae..cbb3ee0 100644 --- a/src/timetype.rs +++ b/src/timetype.rs @@ -2337,3 +2337,223 @@ mod test_end_of_year { } } + +#[cfg(test)] +mod test_end_of_month { + use super::TimeType as TT; + use chrono::NaiveDate; + use chrono::Timelike; + use chrono::Datelike; + + macro_rules! generate_test_moment_operator_amount_and_end_of_month { + { + name = $name:ident; + base = $base:expr; + amount = $amount:expr; + expected = $exp:expr; + operator = $op:expr; + } => { + #[test] + fn $name() { + let base = TT::moment($base); + let result = $op(base, $amount).end_of_month().calculate(); + assert!(result.is_ok(), "Operation failed: {:?}", result); + let result = result.unwrap(); + let expected = $exp; + + assert_eq!(expected, *result.get_moment().unwrap()); + } + } + } + + macro_rules! generate_test_moment_plus_amount_and_end_of_month { + { + name = $name:ident; + base = $base:expr; + amount = $amount:expr; + expected = $exp:expr; + } => { + generate_test_moment_operator_amount_and_end_of_month! { + name = $name; + base = $base; + amount = $amount; + expected = $exp; + operator = |base, amount| base + amount; + } + } + } + + macro_rules! generate_test_moment_minus_amount_and_end_of_month { + { + name = $name:ident; + base = $base:expr; + amount = $amount:expr; + expected = $exp:expr; + } => { + generate_test_moment_operator_amount_and_end_of_month! { + name = $name; + base = $base; + amount = $amount; + expected = $exp; + operator = |base, amount| base - amount; + } + } + } + + // + // tests + // + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_zero_seconds; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::seconds(0); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_seconds; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::seconds(1); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_too_much_seconds; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::seconds(62); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_minutes; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::minutes(2); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_too_much_minutes; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::minutes(65); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_minutes_in_seconds; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::seconds(62); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_months; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::months(14); + expected = NaiveDate::from_ymd(2001, 3, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_years; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::years(62); + expected = NaiveDate::from_ymd(2062, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_more_than_one_year; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::years(1) + TT::months(1); + expected = NaiveDate::from_ymd(2001, 2, 28).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_more_than_one_month; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + + // As we calculate 1 month + 1 day first, we end up adding 31 days to the base + amount = TT::months(1) + TT::days(1); + + // and therefor this results in the date 2000-02-01 + // This is not that inuitive, of course. + expected = NaiveDate::from_ymd(2000, 2, 29).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_more_than_one_day; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::days(1) + TT::hours(1); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_more_than_one_hour; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::hours(1) + TT::minutes(1); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_more_than_one_minute; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::minutes(1) + TT::seconds(1); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_invalid_months; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::months(13); + expected = NaiveDate::from_ymd(2001, 2, 28).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_invalid_days; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::days(31); + expected = NaiveDate::from_ymd(2000, 2, 29).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_invalid_hours; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::hours(25); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_invalid_minutes; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::minutes(61); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_plus_amount_and_end_of_month! { + name = test_moment_plus_invalid_seconds; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::seconds(61); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_minus_amount_and_end_of_month! { + name = test_moment_minus_nothing; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::seconds(0); + expected = NaiveDate::from_ymd(2000, 1, 31).and_hms(0, 0, 0); + } + + generate_test_moment_minus_amount_and_end_of_month! { + name = test_moment_minus_seconds; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::seconds(1); + expected = NaiveDate::from_ymd(1999, 12, 31).and_hms(00, 00, 00); + } + + generate_test_moment_minus_amount_and_end_of_month! { + name = test_moment_minus_months; + base = NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0); + amount = TT::months(12); + expected = NaiveDate::from_ymd(1999, 1, 31).and_hms(0, 0, 0); + } + +} -- cgit v1.2.3 From e4048bbb5c3848056c830eda6af3f67e060abdde Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 16 Sep 2017 11:08:09 +0200 Subject: Use get_num_of_days_in_month() for adjusting --- src/timetype.rs | 2 +- src/util.rs | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/timetype.rs b/src/timetype.rs index cbb3ee0..063a27f 100644 --- a/src/timetype.rs +++ b/src/timetype.rs @@ -396,7 +396,7 @@ fn end_of_month(tt: TimeType) -> Result { els @ TT::Addition(_, _) | els @ TT::Subtraction(_, _) => Err(KE::from_kind(KEK::CannotCalculateEndOfMonthOn(els))), TT::Moment(m) => { - let last_day = get_num_of_days_in_month(m.year(), m.month()); + let last_day = get_num_of_days_in_month(m.year() as i64, m.month() as i64) as u32; Ok(TT::moment(NaiveDate::from_ymd(m.year(), m.month(), last_day).and_hms(0, 0, 0))) }, TT::EndOfYear(e) => do_calculate(*e), diff --git a/src/util.rs b/src/util.rs index d946f08..ec62a7f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -19,11 +19,8 @@ pub fn adjust_times_add(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: fix! { mi, 60, h } fix! { h , 24, d } - if mo == 1 || mo == 3 || mo == 5 || mo == 7 || mo == 8 || mo == 10 || mo == 12 { - fix! { d , 31, mo } - } else { - fix! { d , 30, mo } - } + let adjust = get_num_of_days_in_month(y, mo); + fix! { d , adjust, mo } fix! { mo, 12, y } @@ -63,11 +60,11 @@ pub fn adjust_times_sub(mut y: i64, mut mo: i64, mut d: i64, mut h: i64, mut mi: } #[inline] -pub fn get_num_of_days_in_month(y: i32, m: u32) -> u32 { +pub fn get_num_of_days_in_month(y: i64, m: i64) -> i64 { if m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12 { 31 } else if m == 2 { - if is_leap_year(y) { + if is_leap_year(y as i32) { 29 } else { 28 -- cgit v1.2.3 From 0672002dc23b34d15b93eac8169584fe9b5efde0 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 16 Sep 2017 11:24:41 +0200 Subject: Add test for get_num_of_days_in_month() --- src/util.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/util.rs b/src/util.rs index ec62a7f..cd6e038 100644 --- a/src/util.rs +++ b/src/util.rs @@ -94,3 +94,71 @@ fn test_is_leap_year() { } } + +#[test] +fn test_get_num_of_days_in_month() { + assert_eq!(31, get_num_of_days_in_month(2000, 01)); + assert_eq!(29, get_num_of_days_in_month(2000, 02)); + assert_eq!(31, get_num_of_days_in_month(2000, 03)); + assert_eq!(30, get_num_of_days_in_month(2000, 04)); + assert_eq!(31, get_num_of_days_in_month(2000, 05)); + assert_eq!(30, get_num_of_days_in_month(2000, 06)); + assert_eq!(31, get_num_of_days_in_month(2000, 07)); + assert_eq!(31, get_num_of_days_in_month(2000, 08)); + assert_eq!(30, get_num_of_days_in_month(2000, 09)); + assert_eq!(31, get_num_of_days_in_month(2000, 10)); + assert_eq!(30, get_num_of_days_in_month(2000, 11)); + assert_eq!(31, get_num_of_days_in_month(2000, 12)); + + assert_eq!(31, get_num_of_days_in_month(2001, 01)); + assert_eq!(28, get_num_of_days_in_month(2001, 02)); + assert_eq!(31, get_num_of_days_in_month(2001, 03)); + assert_eq!(30, get_num_of_days_in_month(2001, 04)); + assert_eq!(31, get_num_of_days_in_month(2001, 05)); + assert_eq!(30, get_num_of_days_in_month(2001, 06)); + assert_eq!(31, get_num_of_days_in_month(2001, 07)); + assert_eq!(31, get_num_of_days_in_month(2001, 08)); + assert_eq!(30, get_num_of_days_in_month(2001, 09)); + assert_eq!(31, get_num_of_days_in_month(2001, 10)); + assert_eq!(30, get_num_of_days_in_month(2001, 11)); + assert_eq!(31, get_num_of_days_in_month(2001, 12)); + + assert_eq!(31, get_num_of_days_in_month(2002, 01)); + assert_eq!(28, get_num_of_days_in_month(2002, 02)); + assert_eq!(31, get_num_of_days_in_month(2002, 03)); + assert_eq!(30, get_num_of_days_in_month(2002, 04)); + assert_eq!(31, get_num_of_days_in_month(2002, 05)); + assert_eq!(30, get_num_of_days_in_month(2002, 06)); + assert_eq!(31, get_num_of_days_in_month(2002, 07)); + assert_eq!(31, get_num_of_days_in_month(2002, 08)); + assert_eq!(30, get_num_of_days_in_month(2002, 09)); + assert_eq!(31, get_num_of_days_in_month(2002, 10)); + assert_eq!(30, get_num_of_days_in_month(2002, 11)); + assert_eq!(31, get_num_of_days_in_month(2002, 12)); + + assert_eq!(31, get_num_of_days_in_month(2003, 01)); + assert_eq!(28, get_num_of_days_in_month(2003, 02)); + assert_eq!(31, get_num_of_days_in_month(2003, 03)); + assert_eq!(30, get_num_of_days_in_month(2003, 04)); + assert_eq!(31, get_num_of_days_in_month(2003, 05)); + assert_eq!(30, get_num_of_days_in_month(2003, 06)); + assert_eq!(31, get_num_of_days_in_month(2003, 07)); + assert_eq!(31, get_num_of_days_in_month(2003, 08)); + assert_eq!(30, get_num_of_days_in_month(2003, 09)); + assert_eq!(31, get_num_of_days_in_month(2003, 10)); + assert_eq!(30, get_num_of_days_in_month(2003, 11)); + assert_eq!(31, get_num_of_days_in_month(2003, 12)); + + assert_eq!(31, get_num_of_days_in_month(2004, 01)); + assert_eq!(29, get_num_of_days_in_month(2004, 02)); + assert_eq!(31, get_num_of_days_in_month(2004, 03)); + assert_eq!(30, get_num_of_days_in_month(2004, 04)); + assert_eq!(31, get_num_of_days_in_month(2004, 05)); + assert_eq!(30, get_num_of_days_in_month(2004, 06)); + assert_eq!(31, get_num_of_days_in_month(2004, 07)); + assert_eq!(31, get_num_of_days_in_month(2004, 08)); + assert_eq!(30, get_num_of_days_in_month(2004, 09)); + assert_eq!(31, get_num_of_days_in_month(2004, 10)); + assert_eq!(30, get_num_of_days_in_month(2004, 11)); + assert_eq!(31, get_num_of_days_in_month(2004, 12)); +} -- cgit v1.2.3 From fcce67578f2815f6e67c1eebb36b812c9acc10f7 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 16 Sep 2017 11:28:59 +0200 Subject: Add test for adjusting over months --- src/timetype.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/timetype.rs b/src/timetype.rs index 063a27f..a6c51d2 100644 --- a/src/timetype.rs +++ b/src/timetype.rs @@ -2116,6 +2116,54 @@ mod test_time_adjustments { } } + #[test] + fn test_adjust_times_month_border() { + generate_test_add! { + y : 2000 + 0 => 2000; + mo : 1 + 0 => 2; + d : 22 + 14 => 5; + h : 0 + 0 => 0; + m : 0 + 0 => 0; + s : 0 + 0 => 0; + } + + generate_test_add! { + y : 2000 + 0 => 2000; + mo : 1 + 0 => 2; + d : 22 + 28 => 19; + h : 0 + 0 => 0; + m : 0 + 0 => 0; + s : 0 + 0 => 0; + } + + generate_test_add! { + y : 2000 + 0 => 2000; + mo : 2 + 0 => 3; + d : 22 + 14 => 7; + h : 0 + 0 => 0; + m : 0 + 0 => 0; + s : 0 + 0 => 0; + } + + generate_test_add! { + y : 2000 + 0 => 2000; + mo : 2 + 0 => 3; + d : 22 + 28 => 21; + h : 0 + 0 => 0; + m : 0 + 0 => 0; + s : 0 + 0 => 0; + } + + generate_test_add! { + y : 2000 + 0 => 2000; + mo : 3 + 0 => 4; + d : 22 + 14 => 5; + h : 0 + 0 => 0; + m : 0 + 0 => 0; + s : 0 + 0 => 0; + } + } + } #[cfg(test)] -- cgit v1.2.3