diff options
author | Fredrik Meringdal <fmeringdal@hotmail.com> | 2020-10-25 23:00:50 +0100 |
---|---|---|
committer | Fredrik Meringdal <fmeringdal@hotmail.com> | 2020-10-25 23:00:50 +0100 |
commit | dc44e5f2312bbb8ac411b9dc0b3b52eb3b351b6c (patch) | |
tree | ee75b9aeac7c4b18d21b5dd0cb37e9c2098cfc54 /src/iter | |
parent | 55db5800701e9965b19ede01266442b11956b54b (diff) | |
download | rust_rrule-dc44e5f2312bbb8ac411b9dc0b3b52eb3b351b6c.zip |
restructure files
Diffstat (limited to 'src/iter')
-rw-r--r-- | src/iter/iterinfo.rs | 255 | ||||
-rw-r--r-- | src/iter/mod.rs | 339 | ||||
-rw-r--r-- | src/iter/monthinfo.rs | 70 | ||||
-rw-r--r-- | src/iter/poslist.rs | 63 | ||||
-rw-r--r-- | src/iter/yearinfo.rs | 217 |
5 files changed, 944 insertions, 0 deletions
diff --git a/src/iter/iterinfo.rs b/src/iter/iterinfo.rs new file mode 100644 index 0000000..09090f1 --- /dev/null +++ b/src/iter/iterinfo.rs @@ -0,0 +1,255 @@ +use crate::datetime::*; +use crate::easter::*; +use crate::iter::monthinfo::{MonthInfo, rebuild_month}; +use crate::iter::yearinfo::{YearInfo, rebuild_year}; +use crate::options::*; +use chrono::prelude::*; + +pub struct IterInfo<'a> { + pub yearinfo: Option<YearInfo>, + pub monthinfo: Option<MonthInfo>, + pub eastermask: Option<Vec<isize>>, + options: &'a ParsedOptions, +} + +impl<'a> IterInfo<'a> { + pub fn new(options: &'a ParsedOptions) -> Self { + Self { + options, + yearinfo: None, + monthinfo: None, + eastermask: None, + } + } + + pub fn rebuild(&mut self, year: isize, month: usize) { + if self.monthinfo.is_none() || year != self.monthinfo.as_ref().unwrap().lastyear { + self.yearinfo = Some(rebuild_year(year as i32, self.options)); + } + + if !self.options.bynweekday.is_empty() + && ((self.monthinfo.is_none() || month != self.monthinfo.as_ref().unwrap().lastmonth) + || (self.monthinfo.is_none() || year != self.monthinfo.as_ref().unwrap().lastyear)) + { + if let Some(yearinfo) = &self.yearinfo { + self.monthinfo = Some(rebuild_month( + year, + month, + yearinfo.yearlen, + &yearinfo.mrange, + &yearinfo.wdaymask, + self.options, + )); + } + } + + if let Some(byeaster) = self.options.byeaster { + self.eastermask = Some(easter(year, byeaster)); + } + } + + pub fn lastyear(&self) -> Option<isize> { + match &self.monthinfo { + Some(info) => Some(info.lastyear), + None => None, + } + } + pub fn lastmonth(&self) -> Option<usize> { + match &self.monthinfo { + Some(info) => Some(info.lastmonth), + None => None, + } + } + pub fn yearlen(&self) -> Option<usize> { + match &self.yearinfo { + Some(info) => Some(info.yearlen), + None => None, + } + } + pub fn yearordinal(&self) -> Option<isize> { + match &self.yearinfo { + Some(info) => Some(info.yearordinal), + None => None, + } + } + pub fn mrange(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => Some(&info.mrange), + None => None, + } + } + + pub fn eastermask(&self) -> Option<&Vec<isize>> { + match &self.eastermask { + Some(mask) => Some(&mask), + None => None, + } + } + pub fn wdaymask(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => Some(&info.wdaymask), + None => None, + } + } + + pub fn mmask(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => Some(&info.mmask), + None => None, + } + } + + pub fn wnomask(&self) -> Option<&Vec<usize>> { + match &self.yearinfo { + Some(info) => match &info.wnomask { + Some(mask) => Some(mask), + None => None, + }, + None => None, + } + } + pub fn nwdaymask(&self) -> Option<&Vec<isize>> { + match &self.monthinfo { + Some(info) => Some(&info.nwdaymask), + None => None, + } + } + pub fn nextyearlen(&self) -> Option<usize> { + match &self.yearinfo { + Some(info) => Some(info.nextyearlen), + None => None, + } + } + pub fn mdaymask(&self) -> Option<&Vec<isize>> { + match &self.yearinfo { + Some(info) => Some(&info.mdaymask), + None => None, + } + } + pub fn nmdaymask(&self) -> Option<&Vec<isize>> { + match &self.yearinfo { + Some(info) => Some(&info.nmdaymask), + None => None, + } + } + + pub fn ydayset(&self) -> (Vec<usize>, usize, usize) { + let yearlen = self.yearlen().unwrap(); + let mut v = Vec::with_capacity(yearlen); + for i in 0..yearlen { + v.push(i); + } + (v, 0, yearlen) + } + + pub fn mdayset(&self, month: usize) -> (Vec<usize>, usize, usize) { + let mrange = self.mrange().unwrap(); + let start = mrange[month - 1]; + let end = mrange[month]; + let mut set = vec![0; self.yearlen().unwrap()]; + for i in start..end { + set[i] = i; + } + (set, start, end) + } + + pub fn wdayset(&self, year: isize, month: usize, day: usize) -> (Vec<usize>, usize, usize) { + let set_len = self.yearlen().unwrap() + 7; + let mut set = vec![0; set_len]; + + let mut i = (to_ordinal( + &Utc.ymd(year as i32, month as u32, day as u32) + .and_hms(0, 0, 0), + ) - self.yearordinal().unwrap()) as usize; + + let start = i; + for _ in 0..7 { + if i >= set_len { + break; + } + set[i] = i; + i += 1; + if self.wdaymask().unwrap()[i] == self.options.wkst { + break; + } + } + (set, start, i) + } + + pub fn ddayset(&self, year: isize, month: usize, day: usize) -> (Vec<usize>, usize, usize) { + let mut set = vec![0; self.yearlen().unwrap()]; + + let i = (to_ordinal( + &Utc.ymd(year as i32, month as u32, day as u32) + .and_hms(0, 0, 0), + ) - self.yearordinal().unwrap()) as usize; + + set[i] = i; + (set, i, i + 1) + } + + pub fn htimeset(&self, hour: usize, _: usize, second: usize, millisecond: usize) -> Vec<Time> { + let mut set = self + .options + .byminute + .iter() + .map(|minute| self.mtimeset(hour, *minute, second, millisecond)) + .flatten() + .collect::<Vec<Time>>(); + set.sort_by_key(|a| a.time()); + set + } + + pub fn mtimeset(&self, hour: usize, minute: usize, _: usize, millisecond: usize) -> Vec<Time> { + let mut set = self + .options + .bysecond + .iter() + .map(|second| Time::new(hour, minute, *second, millisecond)) + .collect::<Vec<Time>>(); + set.sort_by_key(|a| a.time()); + set + } + + pub fn stimeset( + &self, + hour: usize, + minute: usize, + second: usize, + millisecond: usize, + ) -> Vec<Time> { + vec![Time::new(hour, minute, second, millisecond)] + } + + pub fn getdayset( + &self, + freq: &Frequenzy, + year: isize, + month: usize, + day: usize, + ) -> (Vec<usize>, usize, usize) { + match freq { + Frequenzy::Yearly => self.ydayset(), + Frequenzy::Monthly => self.mdayset(month), + Frequenzy::Weekly => self.wdayset(year, month, day), + Frequenzy::Daily => self.ddayset(year, month, day), + _ => self.ddayset(year, month, day), + } + } + + pub fn gettimeset( + &self, + freq: &Frequenzy, + hour: usize, + minute: usize, + second: usize, + millisecond: usize, + ) -> Vec<Time> { + match freq { + Frequenzy::Hourly => self.htimeset(hour, minute, second, millisecond), + Frequenzy::Minutely => self.mtimeset(hour, minute, second, millisecond), + Frequenzy::Secondly => self.stimeset(hour, minute, second, millisecond), + _ => panic!("Invalid freq"), + } + } +} diff --git a/src/iter/mod.rs b/src/iter/mod.rs new file mode 100644 index 0000000..d426a98 --- /dev/null +++ b/src/iter/mod.rs @@ -0,0 +1,339 @@ +mod yearinfo; +use yearinfo::get_weekday_val; + +mod monthinfo; + +mod iterinfo; +use iterinfo::IterInfo; + +mod poslist; +use poslist::build_poslist; + +use crate::options::*; +use crate::datetime::{Time, from_ordinal}; +use chrono::offset::TimeZone; +use chrono::prelude::*; +use chrono::{DateTime, Duration}; +use chrono_tz::*; + +pub trait IterResult { + fn accept(&mut self, date: DateTime<Tz>) -> bool; + fn get_value(&self) -> Vec<DateTime<Tz>>; +} + +pub fn iter<T: IterResult>( + iter_result: &mut T, + options: &mut ParsedOptions, +) -> Vec<DateTime<Tz>> { + + if (options.count.is_some() && options.count.unwrap() == 0) || options.interval == 0 { + return iter_result.get_value(); + } + + let mut counter_date = options.dtstart; + let mut ii = IterInfo::new(options); + ii.rebuild(counter_date.year() as isize, counter_date.month() as usize); + + let mut timeset = make_timeset(&ii, &counter_date, options); + let mut count = match options.count { + Some(count) => count, + _ => 0, + }; + + loop { + let (dayset, start, end) = ii.getdayset( + &options.freq, + counter_date.year() as isize, + counter_date.month() as usize, + counter_date.day() as usize, + ); + + let mut dayset = dayset + .into_iter() + .map(|s| Some(s as isize)) + .collect::<Vec<Option<isize>>>(); + + let filtered = remove_filtered_days(&mut dayset, start, end, &ii, options); + + if not_empty(&options.bysetpos) { + let poslist = build_poslist(&options.bysetpos, ×et, start, end, &ii, &dayset, &options.tzid); + + for j in 0..poslist.len() { + let res = poslist[j]; + if options.until.is_some() && res > options.until.unwrap() { + return iter_result.get_value(); + } + + if res >= options.dtstart { + //let rezoned_date = rezone_if_needed(&res, &options); + // let rezoned_date = UTC.timestamp(res.timestamp(), 0); + + if !iter_result.accept(res) { + return iter_result.get_value(); + } + + if count > 0 { + count -= 1; + if count == 0 { + return iter_result.get_value(); + } + } + } + } + } else { + for j in start..end { + let current_day = dayset[j]; + if current_day.is_none() { + continue; + } + + let current_day = current_day.unwrap(); + let date = from_ordinal(ii.yearordinal().unwrap() + current_day); + for k in 0..timeset.len() { + let res = options.tzid.ymd(date.year(), date.month(), date.day()).and_hms( + timeset[k].hour as u32, + timeset[k].minute as u32, + timeset[k].second as u32, + ); + if options.until.is_some() && res > options.until.unwrap() { + return iter_result.get_value(); + } + if res >= options.dtstart { + //let rezoned_date = rezone_if_needed(&res, &options); + // let rezoned_date = UTC.timestamp(res.timestamp(), 0); + // let rezoned_date = tzid.from_utc_datetime(&res.naive_utc()); + // let rezoned_date = res.with_timezone(&options.tzid); + + if !iter_result.accept(res) { + return iter_result.get_value(); + } + if count > 0 { + count -= 1; + if count == 0 { + return iter_result.get_value(); + } + } + } + } + } + } + + if options.interval == 0 { + return iter_result.get_value(); + } + + // Handle frequency and interval + counter_date = increment_counter_date(counter_date, options, filtered); + + if counter_date.year() > 2200 { + return iter_result.get_value(); + } + + if options.freq == Frequenzy::Hourly + || options.freq == Frequenzy::Minutely + || options.freq == Frequenzy::Secondly + { + timeset = ii.gettimeset( + &options.freq, + counter_date.hour() as usize, + counter_date.minute() as usize, + counter_date.second() as usize, + 0, + ); + } + + ii.rebuild(counter_date.year() as isize, counter_date.month() as usize); + } +} + + +pub fn increment_counter_date( + counter_date: DateTime<Utc>, + options: &ParsedOptions, + filtered: bool, +) -> DateTime<Utc> { + match options.freq { + Frequenzy::Yearly => counter_date + .with_year(counter_date.year() + options.interval as i32) + .unwrap(), + Frequenzy::Monthly => { + let new_month = counter_date.month() + options.interval as u32; + if new_month > 12 { + let mut year_div = new_month / 12; + let mut new_month = new_month % 12; + if new_month == 0 { + new_month = 12; + year_div -= 1; + } + let new_year = counter_date.year() + year_div as i32; + counter_date + .with_month(new_month) + .unwrap() + .with_year(new_year) + .unwrap() + } else { + counter_date.with_month(new_month).unwrap() + } + } + Frequenzy::Weekly => { + let mut day_delta = 0; + let weekday = get_weekday_val(&counter_date.weekday()); + if options.wkst > weekday { + day_delta += -((weekday + 1 + (6 - options.wkst)) as isize) + + (options.interval as isize) * 7; + } else { + day_delta += -((weekday - options.wkst) as isize) + (options.interval as isize) * 7; + } + counter_date + Duration::days(day_delta as i64) + } + Frequenzy::Daily => counter_date + Duration::days(options.interval as i64), + Frequenzy::Hourly => { + let mut new_hours = counter_date.hour() as usize; + if filtered { + new_hours += ((23 - new_hours) as f32 / options.interval as f32).floor() as usize + * options.interval; + } + + loop { + new_hours += options.interval; + if options.byhour.is_empty() + || options + .byhour + .iter() + .any(|bh| *bh == (new_hours % 24) as usize) + { + break; + } + } + counter_date.with_hour(0).unwrap() + Duration::hours(new_hours as i64) + } + Frequenzy::Minutely => counter_date + Duration::minutes(options.interval as i64), + Frequenzy::Secondly => counter_date + Duration::seconds(options.interval as i64), + } +} + +pub fn includes<T>(v: &Vec<T>, el: &T) -> bool +where + T: PartialEq, +{ + v.iter().any(|ve| ve == el) +} + +pub fn not_empty<T>(v: &Vec<T>) -> bool { + !v.is_empty() +} + +pub fn is_filtered(ii: &IterInfo, current_day: usize, options: &ParsedOptions) -> bool { + return (not_empty(&options.bymonth) + && !includes(&options.bymonth, &ii.mmask().unwrap()[current_day])) + || (not_empty(&options.byweekno) && (ii.wnomask().unwrap()[current_day]) == 0) + || (not_empty(&options.byweekday) + && !includes(&options.byweekday, &ii.wdaymask().unwrap()[current_day])) + || (ii.nwdaymask().is_some() + && not_empty(ii.nwdaymask().unwrap()) + && (ii.nwdaymask().unwrap()[current_day]) == 0) + || (options.byeaster.is_some() + && !(includes(ii.eastermask().unwrap(), &(current_day as isize)))) + || ((not_empty(&options.bymonthday) || not_empty(&options.bynmonthday)) + && !includes(&options.bymonthday, &ii.mdaymask().unwrap()[current_day]) + && !includes(&options.bynmonthday, &ii.nmdaymask().unwrap()[current_day])) + || (not_empty(&options.byyearday) + && ((current_day < ii.yearlen().unwrap() + && !includes(&options.byyearday, &(current_day as isize + 1)) + && !includes( + &options.byyearday.iter().map(|v| *v as isize).collect(), + &(-(ii.yearlen().unwrap() as isize) + current_day as isize), + )) + || (current_day >= ii.yearlen().unwrap() + && !includes( + &options.byyearday, + &((current_day + 1 - ii.yearlen().unwrap()) as isize), + ) + && !includes( + &options.byyearday.iter().map(|v| *v as isize).collect(), + &(-(ii.nextyearlen().unwrap() as isize) + current_day as isize + - ii.yearlen().unwrap() as isize), + )))); +} + +pub fn remove_filtered_days( + dayset: &mut Vec<Option<isize>>, + start: usize, + end: usize, + ii: &IterInfo, + options: &ParsedOptions, +) -> bool { + let mut filtered = false; + + for daycounter in start..end { + let current_day = dayset[daycounter]; + if current_day.is_none() { + continue; + } + + filtered = is_filtered(ii, current_day.unwrap() as usize, options); + if filtered { + dayset[daycounter] = None; + } + } + filtered +} + +pub fn build_timeset(options: &ParsedOptions) -> Vec<Time> { + let millisecond_mod = (options.dtstart.timestamp_millis() & 1000) as usize; + + if options.freq > Frequenzy::Daily { + return vec![]; + } + + let mut timeset = vec![]; + for hour in &options.byhour { + for minute in &options.byminute { + for second in &options.bysecond { + timeset.push(Time::new(*hour, *minute, *second, millisecond_mod)); + } + } + } + + timeset +} + +pub fn make_timeset( + ii: &IterInfo, + counter_date: &DateTime<Utc>, + options: &ParsedOptions, +) -> Vec<Time> { + if options.freq < Frequenzy::Hourly { + return build_timeset(options); + } + + if (options.freq >= Frequenzy::Hourly + && !options.byhour.is_empty() + && !options + .byhour + .iter() + .any(|&h| h == counter_date.hour() as usize)) + || (options.freq >= Frequenzy::Minutely + && !options.byminute.is_empty() + && !options + .byminute + .iter() + .any(|&m| m == counter_date.minute() as usize)) + || (options.freq >= Frequenzy::Secondly + && !options.bysecond.is_empty() + && !options + .bysecond + .iter() + .any(|&s| s == counter_date.second() as usize)) + { + return vec![]; + } + + ii.gettimeset( + &options.freq, + counter_date.hour() as usize, + counter_date.minute() as usize, + counter_date.second() as usize, + counter_date.timestamp_subsec_millis() as usize, + ) +} diff --git a/src/iter/monthinfo.rs b/src/iter/monthinfo.rs new file mode 100644 index 0000000..c89bfc3 --- /dev/null +++ b/src/iter/monthinfo.rs @@ -0,0 +1,70 @@ +use crate::options::*; +use crate::iter::yearinfo::pymod; + +#[derive(Debug)] +pub struct MonthInfo { + pub lastyear: isize, + pub lastmonth: usize, + pub nwdaymask: Vec<isize>, +} + +pub fn rebuild_month( + year: isize, + month: usize, + yearlen: usize, + mrange: &Vec<usize>, + wdaymask: &Vec<usize>, + options: &ParsedOptions, +) -> MonthInfo { + let mut result = MonthInfo { + lastyear: year, + lastmonth: month, + nwdaymask: vec![], + }; + + let mut ranges: Vec<(isize, isize)> = vec![]; + if options.freq == Frequenzy::Yearly { + if options.bymonth.is_empty() { + ranges = vec![(0, yearlen as isize)]; + } else { + for j in 0..options.bymonth.len() { + let m = options.bymonth[j]; + ranges.push((mrange[m - 1] as isize, mrange[m] as isize)) + } + } + } else if options.freq == Frequenzy::Monthly { + ranges.push((mrange[month - 1] as isize, mrange[month] as isize)); + } + + if ranges.is_empty() { + return result; + } + + // Weekly frequency won't get here, so we may not + // care about cross-year weekly periods. + result.nwdaymask = vec![0; yearlen]; + + for j in 0..ranges.len() { + let rang = ranges[j]; + let first = rang.0; + let last = rang.1 - 1; + + for k in 0..options.bynweekday.len() { + let mut i: isize; + let wday = options.bynweekday[k][0]; + let n = options.bynweekday[k][1]; + if n < 0 { + i = last + (n + 1) * 7; + i -= pymod(wdaymask[i as usize] as isize - wday, 7); + } else { + i = first + (n - 1) * 7; + i += pymod(7 - wdaymask[i as usize] as isize + wday, 7); + } + if first <= i && i <= last { + result.nwdaymask[i as usize] = 1; + } + } + } + + result +} diff --git a/src/iter/poslist.rs b/src/iter/poslist.rs new file mode 100644 index 0000000..fa9fbd8 --- /dev/null +++ b/src/iter/poslist.rs @@ -0,0 +1,63 @@ +use crate::datetime::*; +use crate::iter::iterinfo::IterInfo; +use crate::iter::yearinfo::pymod; +use crate::datetime::from_ordinal; +use chrono::prelude::*; +use chrono_tz::Tz; + +pub fn build_poslist( + bysetpost: &Vec<isize>, + timeset: &Vec<Time>, + start: usize, + end: usize, + ii: &IterInfo, + dayset: &Vec<Option<isize>>, + tz: &Tz +) -> Vec<DateTime<Tz>> { + let mut poslist = vec![]; + + for j in 0..bysetpost.len() { + let daypos; + let timepos; + let pos = bysetpost[j]; + if pos < 0 { + daypos = (pos as f32 / timeset.len() as f32).floor() as isize; + timepos = pymod(pos as isize, timeset.len() as isize); + } else { + daypos = ((pos - 1) as f32 / timeset.len() as f32) as isize; + timepos = pymod(pos as isize - 1, timeset.len() as isize); + } + + let mut tmp = vec![]; + for k in start..end { + let val = dayset[k]; + if val.is_some() { + tmp.push(val.unwrap()); + } + } + + let i; + if daypos < 0 { + let index = tmp.len() as isize + daypos; + i = &tmp[index as usize]; + } else { + i = &tmp[daypos as usize]; + } + + let date = from_ordinal(ii.yearordinal().unwrap() + i); + let res = tz.ymd(date.year(), date.month(), date.day()).and_hms( + timeset[timepos as usize].hour as u32, + timeset[timepos as usize].minute as u32, + timeset[timepos as usize].second as u32, + ); + // XXX: can this ever be in the array? + // - compare the actual date instead? + if !poslist.iter().any(|&p| p == res) { + poslist.push(res); + } + } + + poslist.sort_by_key(|a| a.timestamp()); + + poslist +} diff --git a/src/iter/yearinfo.rs b/src/iter/yearinfo.rs new file mode 100644 index 0000000..8a944f7 --- /dev/null +++ b/src/iter/yearinfo.rs @@ -0,0 +1,217 @@ +use crate::masks::MASKS; +use crate::options::*; +use chrono::prelude::*; +use crate::datetime::to_ordinal; + + +#[derive(Debug)] +pub struct YearInfo { + pub yearlen: usize, + pub nextyearlen: usize, + pub yearordinal: isize, + pub yearweekday: usize, + pub mmask: Vec<usize>, + pub mrange: Vec<usize>, + pub mdaymask: Vec<isize>, + pub nmdaymask: Vec<isize>, + pub wdaymask: Vec<usize>, + pub wnomask: Option<Vec<usize>>, +} + +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 +} + +pub 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, + } +} + +pub struct BaseMasks { + mmask: Vec<usize>, + mdaymask: Vec<isize>, + nmdaymask: Vec<isize>, + wdaymask: Vec<usize>, + mrange: Vec<usize>, +} + +fn base_year_masks(year: i32) -> BaseMasks { + let masks = MASKS.clone(); + 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 +} + +pub 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.).floor() as isize; + let year_mod = pymod(wyearlen, 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 +} |