data_faking/data/defaults/
date.rs

1use std::ops::Sub;
2
3use crate::utils::seeder;
4use chrono::{NaiveDate, Days, Utc, Datelike};
5
6pub fn random_date(start: String, end: String, exclude: Option<Vec<String>>) -> String {
7    if start == end {
8        println!("Timestamps cannot be the same.");
9        return String::from("");
10    }
11
12    let start_validate = validate_timestamp(start);
13    if start_validate.is_none() {
14        return String::from("");
15    };
16    let start_ts = start_validate.unwrap();
17    let start_ymd = NaiveDate::from_ymd_opt(start_ts.0, start_ts.1, start_ts.2).unwrap();
18
19    let end_validate = validate_timestamp(end);
20    if end_validate.is_none() {
21        return String::from("");
22    };
23    let end_ts = end_validate.unwrap();
24    let end_ymd = NaiveDate::from_ymd_opt(end_ts.0, end_ts.1, end_ts.2).unwrap();
25
26    let ast = end_ymd.sub(start_ymd);
27    let rand_days = seeder::gen_range(0..ast.num_days());
28
29    if exclude.is_some() {
30        let exclude_list: Vec<String> = exclude.unwrap();
31        let mut rand_ymd: Option<String> = None;
32        while rand_ymd.is_none() {
33            let ymd = start_ymd.checked_add_days(Days::new(seeder::gen_range(0..ast.num_days()) as u64));
34            if !exclude_list.contains(&ymd.unwrap().to_string()){
35                rand_ymd = Some(ymd.unwrap().to_string());
36            }
37        }
38        return rand_ymd.unwrap();
39    }
40
41    let rand_ymd = start_ymd.checked_add_days(Days::new(rand_days as u64));
42    if rand_ymd.is_some() {
43        return rand_ymd.unwrap().to_string();
44    }
45    else {
46        return String::from("");
47    }
48}
49
50pub fn random_date_dow(start: String, end: String, day_of_week: String) -> String {
51    if day_of_week != String::from("Mon") &&
52        day_of_week != String::from("Tue") &&
53        day_of_week != String::from("Wed") &&
54        day_of_week != String::from("Thu") &&
55        day_of_week != String::from("Fri") &&
56        day_of_week != String::from("Sat") &&
57        day_of_week != String::from("Sun") {
58        println!("Invalid day of week provided: {}", day_of_week);
59        return String::from("");
60    }
61
62    if start == end {
63        println!("Timestamps cannot be the same.");
64        return String::from("");
65    }
66
67    let start_validate = validate_timestamp(start);
68    if start_validate.is_none() {
69        return String::from("");
70    };
71    let start_ts = start_validate.unwrap();
72    let start_ymd = NaiveDate::from_ymd_opt(start_ts.0, start_ts.1, start_ts.2).unwrap();
73
74    let mut start_dow = start_ymd;
75
76    while start_dow.weekday().to_string() != day_of_week {
77        start_dow = start_dow.checked_add_days(Days::new(1)).unwrap();
78    }
79
80    let end_validate = validate_timestamp(end);
81    if end_validate.is_none() {
82        return String::from("");
83    };
84    let end_ts = end_validate.unwrap();
85    let end_ymd = NaiveDate::from_ymd_opt(end_ts.0, end_ts.1, end_ts.2).unwrap();
86
87    if start_dow > end_ymd {
88        println!("No date available on {} between {} and {}", day_of_week, start_ymd.to_string(), end_ymd.to_string());
89        return String::from("");
90    }
91
92    let available_weeks = (end_ymd - start_dow).num_days() / 7;
93
94    if available_weeks == 0 {
95        return start_dow.to_string();
96    }
97
98    let random_week = seeder::gen_range(0..available_weeks);
99
100    let rand_days = random_week * 7;
101
102    let rand_ymd = start_dow.checked_add_days(Days::new(rand_days as u64));
103
104    if rand_ymd.is_some() {
105        return rand_ymd.unwrap().to_string();
106    }
107    else {
108        return String::from("");
109    }
110}
111
112pub fn validate_timestamp(timestamp: String) -> Option<(i32, u32, u32)> {
113    let mut date_parts = timestamp.split("-");
114    let first = date_parts.next();
115    if first.is_none() {
116        println!("Incorrect Format for timestamp: {}", timestamp);
117        return None;
118    }
119    let year_str = first.unwrap();
120    let year_validate = year_str.parse::<i32>();
121    if year_validate.is_err() {
122        println!("Invalid value for year: {}", year_str);
123        return None;
124    }
125    let year = year_validate.unwrap();
126
127    let second = date_parts.next();
128    if second.is_none() {
129        println!("Incorrect Format for timestamp: {}", timestamp);
130        return None;
131    }
132    let month_str = second.unwrap();
133    let month_validate = month_str.parse::<u32>();
134    if month_validate.is_err() {
135        println!("Invalid value for month: {}", month_str);
136        return None;
137    }
138    let month = month_validate.unwrap();
139    if month > 12 {
140        println!("Invalid value for month: {}", month_str);
141    }
142
143    let third = date_parts.next();
144    if third.is_none() {
145        println!("Incorrect Format for timestamp: {}", timestamp);
146        return None;
147    }
148    let day_str = third.unwrap();
149    let day_validate = day_str.parse::<u32>();
150    if day_validate.is_err() {
151        println!("Invalid value for day: {}", day_str);
152        return None;
153    }
154    let day = day_validate.unwrap();
155
156    // number of days validation
157    let mut max_day = match month{
158        1 => 31,
159        2 => 28,
160        3 => 31,
161        4 => 30,
162        5 => 31,
163        6 => 30,
164        7 => 31,
165        8 => 31,
166        9 => 30,
167        10 => 31,
168        11 => 30,
169        12 => 31,
170        _ => 31 // Should never happpen
171    };
172    if month == 2 {
173        let leap_year = max_day % 4;
174        if leap_year == 0 {
175            max_day = 29;
176        }
177    }
178    if day > max_day {
179        println!("Invalid value for day {}", day_str);
180        return None;
181    }
182
183    let extra_value = date_parts.next();
184    if extra_value.is_some() {
185        println!("Trailing characters found for timestamp: {}", timestamp);
186    }
187
188    Some((year, month, day))
189}
190
191pub fn random_date_future(num_days: i32) -> String {
192    let rand_days = seeder::gen_range(0..num_days);
193
194    let naive_date_time: NaiveDate = Utc::now().checked_add_days(Days::new(rand_days.try_into().unwrap())).unwrap().naive_utc().into();
195    naive_date_time.to_string()
196}
197
198pub fn random_date_past(num_days: i32) -> String {
199    let rand_days = seeder::gen_range(0..num_days);
200
201    let naive_date_time: NaiveDate = Utc::now().checked_sub_days(Days::new(rand_days.try_into().unwrap())).unwrap().naive_utc().into();
202    naive_date_time.to_string()
203}
204
205//pub fn random_day_of_week