1use crate::datetimes::all_leap::AllLeapDatetime;
3use crate::datetimes::day_360::Day360Datetime;
4use crate::datetimes::julian::JulianDatetime;
5use crate::datetimes::no_leap::NoLeapDatetime;
6use crate::datetimes::proleptic_gregorian::ProlepticGregorianDatetime;
7use crate::datetimes::standard::StandardDatetime;
8use crate::datetimes::traits::CalendarDatetime;
9use crate::datetimes::traits::CalendarDatetimeCreator;
10use crate::duration::CFDuration;
11
12use crate::utils::normalize_nanoseconds;
13use crate::{calendars::Calendar, constants};
14
15pub struct CFDatetime {
43 inner: Box<dyn CalendarDatetime + Send + Sync>,
44}
45
46impl CFDatetime {
49 pub fn calendar(&self) -> Calendar {
51 self.inner.calendar()
52 }
53 pub fn timestamp(&self) -> i64 {
55 self.inner.timestamp()
56 }
57
58 pub fn ymd(&self) -> Result<(i64, u8, u8), crate::errors::Error> {
65 let (year, month, day, _, _, _) = self.ymd_hms()?;
66
67 Ok((year, month, day))
68 }
69 pub fn hms(&self) -> Result<(u8, u8, u8), crate::errors::Error> {
78 let (_, _, _, hour, min, sec) = self.ymd_hms()?;
79 Ok((hour, min, sec))
80 }
81 pub fn ymd_hms(&self) -> Result<(i64, u8, u8, u8, u8, u8), crate::errors::Error> {
89 self.inner.ymd_hms()
90 }
91 pub fn from_ymd_hms(
98 year: i64,
99 month: u8,
100 day: u8,
101 hour: u8,
102 minute: u8,
103 second: f32,
104 calendar: Calendar,
105 ) -> Result<Self, crate::errors::Error> {
106 match calendar {
107 Calendar::ProlepticGregorian => Ok(Self {
108 inner: Box::new(ProlepticGregorianDatetime::from_ymd_hms(
109 year, month, day, hour, minute, second,
110 )?),
111 }),
112 Calendar::Standard => Ok(Self {
113 inner: Box::new(StandardDatetime::from_ymd_hms(
114 year, month, day, hour, minute, second,
115 )?),
116 }),
117 Calendar::Day360 => Ok(Self {
118 inner: Box::new(Day360Datetime::from_ymd_hms(
119 year, month, day, hour, minute, second,
120 )?),
121 }),
122 Calendar::Julian => Ok(Self {
123 inner: Box::new(JulianDatetime::from_ymd_hms(
124 year, month, day, hour, minute, second,
125 )?),
126 }),
127 Calendar::NoLeap => Ok(Self {
128 inner: Box::new(NoLeapDatetime::from_ymd_hms(
129 year, month, day, hour, minute, second,
130 )?),
131 }),
132 Calendar::AllLeap => Ok(Self {
133 inner: Box::new(AllLeapDatetime::from_ymd_hms(
134 year, month, day, hour, minute, second,
135 )?),
136 }),
137 }
138 }
139
140 pub fn from_hms(
148 hour: u8,
149 minute: u8,
150 second: f32,
151 calendar: Calendar,
152 ) -> Result<Self, crate::errors::Error> {
153 Self::from_ymd_hms(
154 constants::UNIX_DEFAULT_YEAR,
155 constants::UNIX_DEFAULT_MONTH,
156 constants::UNIX_DEFAULT_DAY,
157 hour,
158 minute,
159 second,
160 calendar,
161 )
162 }
163 pub fn from_ymd(
171 year: i64,
172 month: u8,
173 day: u8,
174 calendar: Calendar,
175 ) -> Result<Self, crate::errors::Error> {
176 Self::from_ymd_hms(year, month, day, 0, 0, 0.0, calendar)
177 }
178 pub fn from_timestamp(
185 timestamp: i64,
186 nanoseconds: u32,
187 calendar: Calendar,
188 ) -> Result<Self, crate::errors::Error> {
189 match calendar {
190 Calendar::ProlepticGregorian => Ok(Self {
191 inner: Box::new(ProlepticGregorianDatetime::from_timestamp(
192 timestamp,
193 nanoseconds,
194 )),
195 }),
196 Calendar::Standard => Ok(Self {
197 inner: Box::new(StandardDatetime::from_timestamp(timestamp, nanoseconds)),
198 }),
199 Calendar::Day360 => Ok(Self {
200 inner: Box::new(Day360Datetime::from_timestamp(timestamp, nanoseconds)),
201 }),
202 Calendar::Julian => Ok(Self {
203 inner: Box::new(JulianDatetime::from_timestamp(timestamp, nanoseconds)),
204 }),
205 Calendar::NoLeap => Ok(Self {
206 inner: Box::new(NoLeapDatetime::from_timestamp(timestamp, nanoseconds)),
207 }),
208 Calendar::AllLeap => Ok(Self {
209 inner: Box::new(AllLeapDatetime::from_timestamp(timestamp, nanoseconds)),
210 }),
211 }
212 }
213
214 pub fn hours(&self) -> Result<u8, crate::errors::Error> {
216 let (hour, _, _) = self.hms()?;
217 Ok(hour)
218 }
219 pub fn minutes(&self) -> Result<u8, crate::errors::Error> {
221 let (_, min, _) = self.hms()?;
222 Ok(min)
223 }
224 pub fn seconds(&self) -> Result<u8, crate::errors::Error> {
226 let (_, _, sec) = self.hms()?;
227 Ok(sec)
228 }
229 pub fn nanoseconds(&self) -> u32 {
231 self.inner.nanoseconds()
232 }
233 pub fn change_calendar(&self, calendar: Calendar) -> Result<Self, crate::errors::Error> {
243 let (year, month, day, hour, minute, second) = self.ymd_hms()?;
244 let ns = self.nanoseconds();
245 let f_second = second as f32 + ns as f32 / 1e9;
246 Self::from_ymd_hms(year, month, day, hour, minute, f_second, calendar)
247 }
248 pub fn change_calendar_from_timestamp(
260 &self,
261 calendar: Calendar,
262 ) -> Result<Self, crate::errors::Error> {
263 let timestamp = self.timestamp();
264 let nanoseconds = self.nanoseconds();
265 Self::from_timestamp(timestamp, nanoseconds, calendar)
266 }
267}
268
269impl PartialEq for CFDatetime {
270 fn eq(&self, other: &Self) -> bool {
271 self.calendar() == other.calendar()
272 && self.timestamp() == other.timestamp()
273 && self.nanoseconds() == other.nanoseconds()
274 }
275}
276impl std::fmt::Display for CFDatetime {
278 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
279 let nanoseconds = self.nanoseconds() as f64 / 1_000_000_000.;
280 match self.ymd_hms() {
281 Ok((year, month, day, hour, minute, second)) => {
282 write!(
283 f,
284 "{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}",
285 year, month, day, hour, minute, second, nanoseconds,
286 )
287 }
288 Err(err) => {
289 write!(f, "{:?}", err)
290 }
291 }
292 }
293}
294
295macro_rules! impl_add_duration {
296 ($rhs:ty, $for:ty) => {
297 impl std::ops::Add<$rhs> for $for {
298 type Output = Result<CFDatetime, crate::errors::Error>;
299 fn add(self, rhs: $rhs) -> Self::Output {
300 if self.calendar() != rhs.calendar() {
301 return Err(crate::errors::Error::DifferentCalendars(
302 self.calendar().to_string(),
303 rhs.calendar().to_string(),
304 ));
305 }
306 let nanoseconds = self.nanoseconds() as i64 + rhs.nanoseconds as i64;
307 let (_remaining_seconds, remaining_nanoseconds) =
308 normalize_nanoseconds(nanoseconds);
309 let new_timestamp = self.timestamp() + rhs.seconds;
310 CFDatetime::from_timestamp(new_timestamp, remaining_nanoseconds, self.calendar())
311 }
312 }
313 };
314}
315impl_add_duration!(CFDuration, CFDatetime);
316impl_add_duration!(&CFDuration, CFDatetime);
317impl_add_duration!(CFDuration, &CFDatetime);
318impl_add_duration!(&CFDuration, &CFDatetime);
319
320macro_rules! impl_sub_duration {
321 ($rhs:ty, $for:ty) => {
322 impl std::ops::Sub<$rhs> for $for {
323 type Output = Result<CFDatetime, crate::errors::Error>;
324 fn sub(self, rhs: $rhs) -> Self::Output {
325 if self.calendar() != rhs.calendar() {
326 return Err(crate::errors::Error::DifferentCalendars(
327 self.calendar().to_string(),
328 rhs.calendar().to_string(),
329 ));
330 }
331 let nanoseconds = self.nanoseconds() as i64 - rhs.nanoseconds as i64;
332 let (remaining_seconds, remaining_nanoseconds) = normalize_nanoseconds(nanoseconds);
333 let new_timestamp = (self.timestamp() - rhs.seconds) + remaining_seconds;
334 CFDatetime::from_timestamp(new_timestamp, remaining_nanoseconds, self.calendar())
335 }
336 }
337 };
338}
339impl_sub_duration!(CFDuration, CFDatetime);
340impl_sub_duration!(&CFDuration, CFDatetime);
341impl_sub_duration!(CFDuration, &CFDatetime);
342impl_sub_duration!(&CFDuration, &CFDatetime);
343
344macro_rules! impl_sub_datetime {
345 ($rhs:ty, $for:ty) => {
346 impl std::ops::Sub<$rhs> for $for {
347 type Output = Result<CFDuration, crate::errors::Error>;
348 fn sub(self, rhs: $rhs) -> Self::Output {
349 if self.calendar() != rhs.calendar() {
350 return Err(crate::errors::Error::DifferentCalendars(
351 self.calendar().to_string(),
352 rhs.calendar().to_string(),
353 ));
354 }
355 let nanoseconds = self.nanoseconds() as i64 - rhs.nanoseconds() as i64;
356 let new_timestamp = self.timestamp() - rhs.timestamp();
357 Ok(CFDuration::new(new_timestamp, nanoseconds, self.calendar()))
358 }
359 }
360 };
361}
362
363impl_sub_datetime!(CFDatetime, CFDatetime);
364impl_sub_datetime!(&CFDatetime, CFDatetime);
365impl_sub_datetime!(CFDatetime, &CFDatetime);
366impl_sub_datetime!(&CFDatetime, &CFDatetime);
367
368#[cfg(test)]
369mod tests {
370 use crate::calendars;
371
372 use super::*;
373 #[test]
374 fn test_timestamp_zero_standard() {
375 let (year, month, day) = (1970, 1, 1);
376 let d = CFDatetime::from_ymd(year, month, day, Calendar::Standard).unwrap();
377 assert_eq!(0, d.timestamp());
378 }
379 #[test]
380 fn test_timestamp_20230101_standard() {
381 let (year, month, day) = (2023, 1, 1);
382 let d = CFDatetime::from_ymd(year, month, day, Calendar::Standard).unwrap();
383 assert_eq!(1672531200, d.timestamp());
384 }
385 #[test]
386 fn test_impossible_date_gregorian() {
387 let (year, month, day) = (1582, 10, 8);
388 let d = CFDatetime::from_ymd(year, month, day, Calendar::Standard);
389 assert!(d.is_err());
390 }
391 #[test]
392 fn test_timestamp_minus_one_all_calendars() {
393 let cals = vec![
394 calendars::Calendar::Standard,
395 calendars::Calendar::ProlepticGregorian,
396 calendars::Calendar::Julian,
397 calendars::Calendar::NoLeap,
398 calendars::Calendar::AllLeap,
399 ];
400 for cal in cals {
401 let d = CFDatetime::from_timestamp(-1, 0, cal);
402 assert_eq!((1969, 12, 31, 23, 59, 59), d.unwrap().ymd_hms().unwrap());
403 }
404 }
405 #[test]
406 fn test_timestamp_limit_gregorian_julian() {
407 let lower_limit_gregorian = CFDatetime::from_timestamp(-12219292800, 0, Calendar::Standard);
409 let upper_limit_julian = CFDatetime::from_timestamp(-12219292801, 0, Calendar::Standard);
410 assert_eq!(
411 lower_limit_gregorian.unwrap().ymd_hms().unwrap(),
412 (1582, 10, 15, 0, 0, 0)
413 );
414 assert_eq!(
415 upper_limit_julian.unwrap().ymd_hms().unwrap(),
416 (1582, 10, 4, 23, 59, 59)
417 );
418 }
419 #[test]
420 fn test_idempotence_all_calendars() {
421 let dates = vec![
422 (1970, 1, 1),
423 (1972, 1, 1), (1980, 1, 1),
425 (2020, 1, 1),
426 (100_000, 1, 1),
427 (1980, 6, 15),
428 (1969, 1, 1),
429 (-1_000_000, 1, 1),
430 (-100_000, 1, 1),
431 (1960, 1, 1), (1980, 6, 15),
433 (2001, 1, 3),
435 ];
436 let cals = vec![
437 calendars::Calendar::Day360,
438 calendars::Calendar::Standard,
439 calendars::Calendar::ProlepticGregorian,
440 calendars::Calendar::Julian,
441 calendars::Calendar::NoLeap,
442 calendars::Calendar::AllLeap,
443 ];
444 for cal in cals {
445 for date in dates.clone() {
446 let (year, month, day) = date;
447 let datetime: CFDatetime = CFDatetime::from_ymd(year, month, day, cal).unwrap();
448 let (expected_year, expected_month, expected_day) = datetime.ymd().unwrap();
449 assert_eq!(expected_year, year);
450 assert_eq!(expected_month, month);
451 assert_eq!(expected_day, day);
452 }
453 }
454 }
455 #[test]
456 fn test_add_duration() {
457 let cals = vec![
458 calendars::Calendar::Day360,
459 calendars::Calendar::Standard,
460 calendars::Calendar::ProlepticGregorian,
461 calendars::Calendar::Julian,
462 calendars::Calendar::NoLeap,
463 calendars::Calendar::AllLeap,
464 ];
465 for calendar in cals {
466 let duration_expected = vec![
467 (CFDuration::from_hours(1, calendar), (1970, 1, 1, 1, 0, 0)),
468 (CFDuration::from_minutes(1, calendar), (1970, 1, 1, 0, 1, 0)),
469 (CFDuration::from_seconds(1, calendar), (1970, 1, 1, 0, 0, 1)),
470 (CFDuration::from_days(1, calendar), (1970, 1, 2, 0, 0, 0)),
471 ];
472 for (duration, expected) in duration_expected {
473 let datetime = CFDatetime::from_ymd(1970, 1, 1, calendar).unwrap();
474 let new_datetime = datetime + duration;
475 assert_eq!(new_datetime.unwrap().ymd_hms().unwrap(), expected);
476 }
477 }
478 }
479 #[test]
480 fn test_timestamp() {
481 let timestamp_expected = vec![
482 (0, (1970, 1, 1, 0, 0, 0)),
483 (315532800, (1980, 1, 1, 0, 0, 0)),
484 (631152000, (1990, 1, 1, 0, 0, 0)),
485 (946684800, (2000, 1, 1, 0, 0, 0)),
486 (949363200, (2000, 2, 1, 0, 0, 0)),
487 (957139200, (2000, 5, 1, 0, 0, 0)),
488 (946771200, (2000, 1, 2, 0, 0, 0)),
489 (946857600, (2000, 1, 3, 0, 0, 0)),
491 ];
492 for (timestamp, expected) in timestamp_expected {
493 let datetime = CFDatetime::from_timestamp(timestamp, 0, calendars::Calendar::Standard);
494 assert_eq!(datetime.unwrap().ymd_hms().unwrap(), expected);
495 }
496 }
497
498 #[test]
499 fn test_360_day_from_timestamp() {
500 let timestamp_expected: Vec<(i64, (i64, u8, u8))> = vec![
501 (-61236000000, (1, 4, 1)),
502 (-61235913600, (1, 4, 2)),
503 (-61235827200, (1, 4, 3)),
504 (-61235740800, (1, 4, 4)),
505 (-61235654400, (1, 4, 5)),
506 (-61235568000, (1, 4, 6)),
507 ];
508 for (timestamp, expected) in timestamp_expected {
509 let datetime = CFDatetime::from_timestamp(timestamp, 0, calendars::Calendar::Day360);
510 assert_eq!(datetime.unwrap().ymd().unwrap(), expected);
511 }
512 }
513}