summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFredrik Meringdal <fmeringdal@hotmail.com>2020-10-14 13:51:17 +0200
committerFredrik Meringdal <fmeringdal@hotmail.com>2020-10-14 13:51:17 +0200
commit6a46eb45b6de82f13c98ce9eb27a5c21d0e2ae25 (patch)
tree3104e3d2c2fc613e2520f75a579cf08e69ca684b
downloadrust_rrule-6a46eb45b6de82f13c98ce9eb27a5c21d0e2ae25.zip
init with yearinfo
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml10
-rw-r--r--src/lib.rs288
-rw-r--r--src/masks.rs148
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(),
+ }
+ }
+}