diff options
author | Fredrik Meringdal <fmeringdal@hotmail.com> | 2020-10-14 13:51:17 +0200 |
---|---|---|
committer | Fredrik Meringdal <fmeringdal@hotmail.com> | 2020-10-14 13:51:17 +0200 |
commit | 6a46eb45b6de82f13c98ce9eb27a5c21d0e2ae25 (patch) | |
tree | 3104e3d2c2fc613e2520f75a579cf08e69ca684b | |
download | rust_rrule-6a46eb45b6de82f13c98ce9eb27a5c21d0e2ae25.zip |
init with yearinfo
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Cargo.toml | 10 | ||||
-rw-r--r-- | src/lib.rs | 288 | ||||
-rw-r--r-- | src/masks.rs | 148 |
4 files changed, 448 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..8295829 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rust_ical" +version = "0.1.0" +authors = ["Fredrik Meringdal <fmeringdal@hotmail.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chrono = "0.4.19" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..809fae5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,288 @@ +extern crate chrono; + +mod masks; + +use chrono::prelude::*; +use chrono::{DateTime, Duration}; +use masks::Masks; + +struct RRule {} + +#[derive(Debug)] +struct YearInfo { + yearlen: usize, + nextyearlen: usize, + yearordinal: i64, + yearweekday: usize, + mmask: Vec<u32>, + mrange: Vec<u32>, + mdaymask: Vec<u32>, + nmdaymask: Vec<i32>, + wdaymask: Vec<u32>, + wnomask: Option<Vec<u32>>, +} + +#[derive(Debug)] +enum Frequenzy { + YEARLY, + MONTHLY, + WEEKLY, + DAILY, + HOURLY, + MINUTELY, + SECONDLY, +} + +#[derive(Debug)] +struct ParsedOptions { + freq: Frequenzy, + interval: usize, + count: Option<u32>, + until: Option<DateTime<Utc>>, + tzid: Option<String>, + dtstart: DateTime<Utc>, + wkst: usize, + bysetpos: Vec<u32>, + bymonth: Vec<u32>, + bymonthday: Vec<u32>, + bynmonthday: Vec<u32>, + byyearday: Vec<u32>, + byweekno: Vec<isize>, + byweekday: Vec<u32>, + byhour: Vec<u32>, + byminute: Vec<u32>, + bysecond: Vec<u32>, +} + +fn is_leap_year(year: i32) -> bool { + year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) +} + +fn get_year_len(year: i32) -> usize { + if is_leap_year(year) { + return 366; + } + 365 +} + +fn to_ordinal(date: &DateTime<Utc>) -> i64 { + date.timestamp() / 60 / 60 / 24 +} + +fn get_weekday_val(wk: &Weekday) -> usize { + match wk { + Weekday::Mon => 0, + Weekday::Tue => 1, + Weekday::Wed => 2, + Weekday::Thu => 3, + Weekday::Fri => 4, + Weekday::Sat => 5, + Weekday::Sun => 6, + } +} + +struct BaseMasks { + mmask: Vec<u32>, + mdaymask: Vec<u32>, + nmdaymask: Vec<i32>, + wdaymask: Vec<u32>, + mrange: Vec<u32>, +} + +fn base_year_masks(year: i32) -> BaseMasks { + let masks = Masks::new(); + let firstyday = Utc.ymd(year, 1, 1).and_hms_milli(0, 0, 0, 0); + let yearlen = get_year_len(year); + let wday = get_weekday_val(&firstyday.weekday()) as usize; + + if yearlen == 365 { + return BaseMasks { + mmask: masks.M365, + mdaymask: masks.MDAY365, + nmdaymask: masks.NMDAY365, + mrange: masks.M365RANGE, + wdaymask: Vec::from(&masks.WDAY[wday..]), + }; + } + + BaseMasks { + mmask: masks.M366, + mdaymask: masks.MDAY366, + nmdaymask: masks.NMDAY366, + mrange: masks.M366RANGE, + wdaymask: Vec::from(&masks.WDAY[wday..]), + } +} + +pub fn pymod(a: isize, b: isize) -> isize { + let r = a % b; + // If r and b differ in sign, add b to wrap the result to the correct sign. + if (r > 0 && b < 0) || (r < 0 && b > 0) { + return r + b; + } + r +} + +fn rebuild_year(year: i32, options: &ParsedOptions) -> YearInfo { + let firstyday = Utc.ymd(year, 1, 1).and_hms_milli(0, 0, 0, 0); + + let yearlen = get_year_len(year); + let nextyearlen = get_year_len(year + 1); + let yearordinal = to_ordinal(&firstyday); + let yearweekday = get_weekday_val(&firstyday.weekday()); + + let base_masks = base_year_masks(year); + + let mut result = YearInfo { + yearlen, + nextyearlen, + yearordinal, + yearweekday, + wnomask: None, + mmask: base_masks.mmask, + mdaymask: base_masks.mdaymask, + nmdaymask: base_masks.nmdaymask, + mrange: base_masks.mrange, + wdaymask: base_masks.wdaymask, + }; + + if options.byweekno.is_empty() { + return result; + } + + let mut wnomask = vec![0; yearlen + 7]; + let wyearlen; + let mut no1wkst = pymod((7 - yearweekday + options.wkst) as isize, 7); + let firstwkst = no1wkst; + if no1wkst >= 4 { + no1wkst = 0; + // Number of days in the year, plus the days we got + // from last year. + wyearlen = result.yearlen as isize + pymod(yearweekday as isize - options.wkst as isize, 7); + } else { + // Number of days in the year, minus the days we + // left in last year. + wyearlen = yearlen as isize - no1wkst; + } + + let div = (wyearlen as f32 / 7.).round() as isize; + let year_mod = pymod(div, 7); + //const numweeks = Math.floor(div + mod / 4) + let numweeks = div + (year_mod / 4); + + for j in 0..options.byweekno.len() { + let mut n = options.byweekno[j]; + if n < 0 { + n += (numweeks + 1) as isize; + } + if !(n > 0 && n <= numweeks) { + continue; + } + let mut i; + if n > 1 { + i = no1wkst + (n - 1) * 7; + if no1wkst != firstwkst { + i -= 7 - firstwkst; + } + } else { + i = no1wkst; + } + + for _ in 0..7 { + wnomask[i as usize] = 1; + i += 1; + if result.wdaymask[i as usize] as usize == options.wkst { + break; + } + } + } + + if options.byweekno.iter().any(|&wkno| wkno == 1) { + // Check week number 1 of next year as well + // orig-TODO : Check -numweeks for next year. + let mut i = no1wkst + numweeks * 7; + if no1wkst != firstwkst { + i -= 7 - firstwkst; + } + if i < yearlen as isize { + // If week starts in next year, we + // don't care about it. + for _ in 0..7 { + wnomask[i as usize] = 1; + i += 1; + if result.wdaymask[i as usize] as usize == options.wkst { + break; + } + } + } + } + + if no1wkst > 0 { + // Check last week number of last year as + // well. If no1wkst is 0, either the year + // started on week start, or week number 1 + // got days from last year, so there are no + // days from last year's last week number in + // this year. + let lnumweeks; + if options.byweekno.iter().any(|&weekno| weekno == -1) { + let lyearweekday = get_weekday_val(&Utc.ymd(year - 1, 1, 1).weekday()); + + let lno1wkst = pymod((7 - lyearweekday + options.wkst) as isize, 7); + + let lyearlen = get_year_len(year - 1); + let weekst; + if lno1wkst >= 4 { + //lno1wkst = 0; + weekst = lyearlen as isize + pymod((lyearweekday - options.wkst) as isize, 7); + } else { + weekst = yearlen as isize - no1wkst; + } + + lnumweeks = 52 + (pymod(weekst, 7) / 4) as isize; + } else { + lnumweeks = -1 as isize; + } + + if options.byweekno.iter().any(|&weekno| weekno == lnumweeks) { + for i in 0..no1wkst { + wnomask[i as usize] = 1; + } + } + } + + result.wnomask = Some(wnomask); + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let options = ParsedOptions { + freq: Frequenzy::YEARLY, + interval: 1, + count: Some(10), + until: None, + tzid: None, + dtstart: Utc.ymd(2020, 1, 1).and_hms(0, 0, 0), + wkst: 0, + bysetpos: vec![], + bymonth: vec![], + bymonthday: vec![], + bynmonthday: vec![], + byyearday: vec![], + byweekno: vec![1], + byweekday: vec![], + byhour: vec![], + byminute: vec![], + bysecond: vec![], + }; + let res = rebuild_year(2020, &options); + println!("Res: {:?}", res); + assert_eq!(2 + 2, 4); + } +} diff --git a/src/masks.rs b/src/masks.rs new file mode 100644 index 0000000..e2ca977 --- /dev/null +++ b/src/masks.rs @@ -0,0 +1,148 @@ +// ============================================================================= +// Date masks +// ============================================================================= + +// Every mask is 7 days longer to handle cross-year weekly periods. + +pub struct Masks { + pub WDAY: Vec<u32>, + pub M365: Vec<u32>, + pub M365RANGE: Vec<u32>, + pub M366: Vec<u32>, + pub M366RANGE: Vec<u32>, + pub MDAY365: Vec<u32>, + pub MDAY366: Vec<u32>, + pub NMDAY365: Vec<i32>, + pub NMDAY366: Vec<i32>, +} + +impl Masks { + pub fn new() -> Self { + let M28: Vec<u32> = (1..29).collect(); + let M29: Vec<u32> = (1..30).collect(); + let M30: Vec<u32> = (1..31).collect(); + let M31: Vec<u32> = (1..32).collect(); + + let NM28: Vec<i32> = (-28..0).collect(); + let NM29: Vec<i32> = (-29..0).collect(); + let NM30: Vec<i32> = (-30..0).collect(); + let NM31: Vec<i32> = (-31..0).collect(); + + Self { + WDAY: vec![(0..7).collect::<Vec<u32>>(); 55] + .into_iter() + .flatten() + .collect(), + M365: vec![ + vec![1; 31], + vec![2; 28], + vec![3; 31], + vec![4; 30], + vec![5; 31], + vec![6; 30], + vec![7; 31], + vec![8; 31], + vec![9; 30], + vec![10; 31], + vec![11; 30], + vec![12, 31], + vec![1; 7], + ] + .into_iter() + .flatten() + .collect(), + M365RANGE: vec![0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365], + MDAY366: vec![ + M31.clone(), + M29.clone(), + M31.clone(), + M30.clone(), + M31.clone(), + M30.clone(), + M31.clone(), + M31.clone(), + M30.clone(), + M31.clone(), + M30.clone(), + M31.clone(), + Vec::from(&M31.clone()[0..7]), + ] + .into_iter() + .flatten() + .collect(), + M366RANGE: vec![0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366], + MDAY365: vec![ + M31.clone(), + M28.clone(), + M31.clone(), + M30.clone(), + M31.clone(), + M30.clone(), + M31.clone(), + M31.clone(), + M30.clone(), + M31.clone(), + M30.clone(), + M31.clone(), + Vec::from(&M31.clone()[0..7]), + ] + .into_iter() + .flatten() + .collect(), + M366: vec![ + vec![1; 31], + vec![2; 29], + vec![3; 31], + vec![4; 30], + vec![5; 31], + vec![6; 30], + vec![7; 31], + vec![8; 31], + vec![9; 30], + vec![10; 31], + vec![11; 30], + vec![12, 31], + vec![1; 7], + ] + .into_iter() + .flatten() + .collect(), + NMDAY365: vec![ + NM31.clone(), + NM28.clone(), + NM31.clone(), + NM30.clone(), + NM31.clone(), + NM30.clone(), + NM31.clone(), + NM31.clone(), + NM30.clone(), + NM31.clone(), + NM30.clone(), + NM31.clone(), + Vec::from(&NM31.clone()[0..7]), + ] + .into_iter() + .flatten() + .collect(), + NMDAY366: vec![ + NM31.clone(), + NM29.clone(), + NM31.clone(), + NM30.clone(), + NM31.clone(), + NM30.clone(), + NM31.clone(), + NM31.clone(), + NM30.clone(), + NM31.clone(), + NM30.clone(), + NM31.clone(), + Vec::from(&NM31.clone()[0..7]), + ] + .into_iter() + .flatten() + .collect(), + } + } +} |