diff options
Diffstat (limited to 'src/iter/mod.rs')
-rw-r--r-- | src/iter/mod.rs | 339 |
1 files changed, 339 insertions, 0 deletions
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, + ) +} |