1use crate::{
4 calendars::Calendar,
5 constants,
6 datetime::CFDatetime,
7 datetimes::traits::IsLeap,
8 duration::CFDuration,
9 parser::{parse_cf_time, Unit},
10};
11use std::time::Duration;
12
13pub fn get_timestamp_from_ymd<T: IsLeap>(
29 year: i64,
30 month: u8,
31 day: u8,
32) -> Result<i64, crate::errors::Error> {
33 let mut timestamp: i64 = 0;
34
35 let mut current_year: i64 = year;
38 loop {
39 if current_year == constants::UNIX_DEFAULT_YEAR {
40 break;
41 }
42 let year_to_look_at = current_year - (current_year > constants::UNIX_DEFAULT_YEAR) as i64;
45 let seconds_in_year: i64 = if T::is_leap(year_to_look_at) {
46 constants::SECONDS_PER_YEAR_LEAP
47 } else {
48 constants::SECONDS_PER_YEAR_NON_LEAP
49 };
50
51 if current_year > constants::UNIX_DEFAULT_YEAR {
52 timestamp += seconds_in_year;
53 current_year -= 1;
54 } else {
55 timestamp -= seconds_in_year;
56 current_year += 1;
57 }
58 }
59
60 let mut current_month = 0;
62 loop {
63 if current_month + 1 == month {
64 break;
65 }
66 if T::is_leap(year) {
67 timestamp += constants::DAYS_PER_MONTH_LEAP[(current_month) as usize] as i64
68 * constants::SECS_PER_DAY as i64;
69 } else {
70 timestamp += constants::DAYS_PER_MONTH[(current_month) as usize] as i64
71 * constants::SECS_PER_DAY as i64;
72 }
73 current_month += 1;
74 }
75
76 timestamp += (day as i64 - 1) * constants::SECS_PER_DAY as i64;
78
79 Ok(timestamp)
80}
81
82pub fn get_hms_from_timestamp(timestamp: i64) -> (u8, u8, u8) {
92 let _mod_sec = constants::SECS_PER_DAY as i64;
93 let seconds = (timestamp % constants::SECS_PER_DAY as i64 + constants::SECS_PER_DAY as i64)
94 % constants::SECS_PER_DAY as i64;
95 let sec = (seconds % 60) as u8;
96 let min = ((seconds / 60) % 60) as u8;
97 let hour = ((seconds / 3600) % 24) as u8;
98 (hour, min, sec)
99}
100pub fn get_ymd_hms_from_timestamp<T: IsLeap>(timestamp: i64) -> (i64, u8, u8, u8, u8, u8) {
114 let mut remaining_timestamp = timestamp;
115 let mut current_year = constants::UNIX_DEFAULT_YEAR;
116
117 let direction = if timestamp >= 0 { 1 } else { -1 };
119
120 loop {
121 let year_to_look_at = if current_year > constants::UNIX_DEFAULT_YEAR {
122 current_year
123 } else {
124 current_year - 1
125 };
126 let seconds_in_year: i64 = if T::is_leap(year_to_look_at) {
127 constants::SECONDS_PER_YEAR_LEAP
128 } else {
129 constants::SECONDS_PER_YEAR_NON_LEAP
130 };
131
132 let new_remaining = remaining_timestamp - direction * seconds_in_year;
133
134 if direction == 1 && (new_remaining < 0) {
136 break;
137 }
138 else if direction == -1 && (new_remaining >= 0) {
141 remaining_timestamp = new_remaining;
142 current_year += direction;
143 break;
144 }
145 remaining_timestamp = new_remaining;
146 current_year += direction;
147 }
148
149 let mut month: i64 = 0;
152 loop {
153 let days_in_month: i64 = if T::is_leap(current_year) {
154 constants::DAYS_PER_MONTH_LEAP[month as usize] as i64
155 } else {
156 constants::DAYS_PER_MONTH[month as usize] as i64
157 };
158 let seconds_in_month = days_in_month * constants::SECS_PER_DAY as i64;
159
160 if remaining_timestamp < seconds_in_month {
161 break;
162 }
163 remaining_timestamp -= seconds_in_month;
164 month += 1;
165 }
166
167 let day = (remaining_timestamp / (constants::SECS_PER_DAY as i64)) as u8;
169
170 let (hour, min, sec) = get_hms_from_timestamp(remaining_timestamp);
171 (current_year, month as u8 + 1, day + 1, hour, min, sec)
172}
173
174pub fn is_leap_gregorian(year: i64) -> bool {
184 let f_year = ((year >> 63) & 1) + year;
187 (f_year % 400 == 0) || ((f_year % 4 == 0) && (f_year % 100 != 0))
188}
189
190pub fn is_leap_julian(year: i64) -> bool {
200 (((year >> 63) & 1) + year) % 4 == 0
203}
204
205fn extract_seconds_and_nanoseconds(seconds: f32) -> (u64, u32) {
206 let duration = Duration::from_secs_f32(seconds);
207 let secs = duration.as_secs();
208 let nanosecs = duration.subsec_nanos();
209
210 (secs, nanosecs)
211}
212
213pub fn get_timestamp_from_hms(
229 hour: u8,
230 min: u8,
231 sec: f32,
232) -> Result<(i64, u32), crate::errors::Error> {
233 if hour > 23 {
234 return Err(crate::errors::Error::InvalidTime(
235 format!("Hour {hour} is out of bounds").to_string(),
236 ));
237 }
238 if min > 59 {
239 return Err(crate::errors::Error::InvalidTime(
240 format!("Minute {min} is out of bounds").to_string(),
241 ));
242 }
243 if !(0.0..60.0).contains(&sec) {
244 return Err(crate::errors::Error::InvalidTime(
245 format!("Second {sec} is out of bounds").to_string(),
246 ));
247 }
248 let (round_seconds, nanoseconds) = extract_seconds_and_nanoseconds(sec);
249 let total_seconds = (hour as u32 * constants::SECS_PER_HOUR
250 + min as u32 * constants::SECS_PER_MINUTE
251 + round_seconds as u32)
252 % constants::SECS_PER_DAY;
253
254 Ok((total_seconds as i64, nanoseconds))
255}
256
257pub fn get_datetime_and_unit_from_units(
258 units: &str,
259 calendar: Calendar,
260) -> Result<(CFDatetime, Unit), crate::errors::Error> {
261 let parsed_cf_time = parse_cf_time(units)?;
262 let (year, month, day) = parsed_cf_time.datetime.ymd;
263 let (hour, minute, second) = match parsed_cf_time.datetime.hms {
264 Some(hms) => (hms.0, hms.1, hms.2),
265 None => (0, 0, 0.0),
266 };
267 let cf_datetime = CFDatetime::from_ymd_hms(year, month, day, hour, minute, second, calendar)?;
268 let unit = parsed_cf_time.unit;
269 Ok((cf_datetime, unit))
270}
271pub fn normalize_nanoseconds(nanoseconds: i64) -> (i64, u32) {
299 let mut remaining_seconds = nanoseconds / 1e9 as i64;
301
302 let remaining_nanoseconds: i64 = if remaining_seconds < 0 {
304 remaining_seconds -= 1;
306 (nanoseconds + (remaining_seconds.abs() * 1_000_000_000)) % 1_000_000_000
307 } else {
308 nanoseconds % 1e9 as i64
310 };
311 (remaining_seconds, remaining_nanoseconds as u32)
312}
313
314pub fn unit_to_encode(unit: &Unit, duration: CFDuration) -> f64 {
325 match unit {
326 Unit::Year => duration.num_years(), Unit::Month => duration.num_months(), Unit::Day => duration.num_days(), Unit::Hour => duration.num_hours(), Unit::Minute => duration.num_minutes(), Unit::Second => duration.num_seconds(), Unit::Millisecond => duration.num_milliseconds(), Unit::Microsecond => duration.num_microseconds(), Unit::Nanosecond => duration.num_nanoseconds(), }
336}